How can I get a call stack listing in Perl?

PerlStack TraceCallstack

Perl Problem Overview


Is there a way I can access (for printout) a list of sub + module to arbitrary depth of sub-calls preceding a current position in a Perl script?

I need to make changes to some Perl modules (.pm's). The workflow is initiated from a web-page thru a cgi-script, passing input through several modules/objects ending in the module where I need to use the data. Somewhere along the line the data got changed and I need to find out where.

Perl Solutions


Solution 1 - Perl

You can use http://search.cpan.org/dist/Devel-StackTrace/">Devel::StackTrace</a>;.

use Devel::StackTrace;
my $trace = Devel::StackTrace->new;
print $trace->as_string; # like carp

It behaves like Carp's trace, but you can get more control over the frames.

The one problem is that references are stringified and if a referenced value changes, you won't see it. However, you could whip up some stuff with http://search.cpan.org/dist/PadWalker/">PadWalker</a> to print out the full data (it would be huge, though).

Solution 2 - Perl

This code works without any additional modules. Just include it where needed.

my $i = 1;
print STDERR "Stack Trace:\n";
while ( (my @call_details = (caller($i++))) ){
	print STDERR $call_details[1].":".$call_details[2]." in function ".$call_details[3]."\n";
}

Solution 3 - Perl

Carp::longmess will do what you want, and it's standard.

use Carp qw<longmess>;
use Data::Dumper;
sub A { &B; }
sub B { &C; }
sub C { &D; }
sub D { &E; }

sub E { 
    # Uncomment below if you want to see the place in E
    # local $Carp::CarpLevel = -1; 
    my $mess = longmess();
    print Dumper( $mess );
}

A();
__END__
$VAR1 = ' at - line 14
	main::D called at - line 12
	main::C called at - line 10
	main::B called at - line 8
	main::A() called at - line 23
';

I came up with this sub (Now with optional blessin' action!)

my $stack_frame_re = qr{
    ^                # Beginning of line
    \s*              # Any number of spaces
    ( [\w:]+ )       # Package + sub
    (?: [(] ( .*? ) [)] )? # Anything between two parens
    \s+              # At least one space
    called [ ] at    # "called" followed by a single space
    \s+ ( \S+ ) \s+  # Spaces surrounding at least one non-space character
    line [ ] (\d+)   # line designation
}x;

sub get_stack {
    my @lines = split /\s*\n\s*/, longmess;
    shift @lines;
    my @frames
        = map { 
              my ( $sub_name, $arg_str, $file, $line ) = /$stack_frame_re/;
              my $ref =  { sub_name => $sub_name
                         , args     => [ map { s/^'//; s/'$//; $_ } 
                                         split /\s*,\s*/, $arg_str 
                                       ]
                         , file     => $file
                         , line     => $line 
                         };
              bless $ref, $_[0] if @_;
              $ref
          } 
          @lines
       ;
    return wantarray ? @frames : \@frames;
}

Solution 4 - Perl

caller can do that, though you may want even more information than that.

Solution 5 - Perl

There's also Carp::confess and Carp::cluck.

Solution 6 - Perl

One that is more pretty: [Devel::PrettyTrace][1]

use Devel::PrettyTrace;
bt;

[1]: https://metacpan.org/pod/Devel::PrettyTrace "Devel::PrettyTrace"

Solution 7 - Perl

In case you can't use (or would like to avoid) non-core modules, here's a simple subroutine I came up with:

#!/usr/bin/perl
use strict;
use warnings;

sub printstack {
    my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash);
    my $i = 1;
    my @r;
    while (@r = caller($i)) {
        ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) = @r;
        print "$filename:$line $subroutine\n";
        $i++;
    }
}

sub i {
    printstack();
}

sub h {
    i;
}

sub g {
    h;
}

g;

It produces output like as follows:

/root/_/1.pl:21 main::i
/root/_/1.pl:25 main::h
/root/_/1.pl:28 main::g

Or a oneliner:

for (my $i = 0; my @r = caller($i); $i++) { print "$r[1]:$r[2] $r[3]\n"; }

You can find documentation on caller here.

Solution 8 - Perl

Moving my comment to an answer:

  1. Install Devel::Confess the right way

     cpanm Devel::Confess
    
  2. Run with

     perl -d:Confess myscript.pl
    

On errors, this will show the whole call stack list.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionslashmaisView Question on Stackoverflow
Solution 1 - PerlOvidView Answer on Stackoverflow
Solution 2 - PerlThariamaView Answer on Stackoverflow
Solution 3 - PerlAxemanView Answer on Stackoverflow
Solution 4 - PerlLeon TimmermansView Answer on Stackoverflow
Solution 5 - PerljkramerView Answer on Stackoverflow
Solution 6 - Perluser2291758View Answer on Stackoverflow
Solution 7 - Perlx-yuriView Answer on Stackoverflow
Solution 8 - PerlPablo BianchiView Answer on Stackoverflow