How can I get a call stack listing in Perl?

后端 未结 8 699
慢半拍i
慢半拍i 2020-12-02 08:59

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 chan

相关标签:
8条回答
  • 2020-12-02 09:50

    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;
    }
    
    0 讨论(0)
  • 2020-12-02 09:54

    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.

    0 讨论(0)
提交回复
热议问题