72

有没有一种方法可以访问(用于打印输出)子 + 模块列表到 Perl 脚本中当前位置之前的任意深度的子调用?

我需要对一些 Perl 模块 (.pm's) 进行更改。工作流程通过 cgi 脚本从网页启动,通过几个模块/对象传递输入,这些模块/对象以我需要使用数据的模块结尾。沿着这条线的某个地方,数据发生了变化,我需要找出在哪里。

4

8 回答 8

68

您可以使用Devel::StackTrace

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

它的行为类似于 Carp 的跟踪,但您可以更好地控制帧。

一个问题是引用是字符串化的,如果引用的值发生变化,您将看不到它。但是,您可以使用PadWalker 制作一些东西来打印出完整的数据(虽然它会很大)。

于 2008-10-23T08:59:56.390 回答
24

此代码无需任何附加模块即可工作。只需在需要的地方包含它。

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";
}
于 2016-08-18T06:59:47.253 回答
20

Carp::longmess会做你想做的,这是标准的。

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
';

我想出了这个子(现在有可选的祝福动作!)

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;
}
于 2008-10-23T22:50:45.557 回答
18

caller可以这样做,尽管您可能需要更多信息。

于 2008-10-23T09:01:26.463 回答
16

还有Carp::confessCarp::cluck

于 2008-10-23T09:53:32.740 回答
3

一个更漂亮的:Devel::PrettyTrace

use Devel::PrettyTrace;
bt;
于 2014-07-18T10:28:55.120 回答
3

如果您不能使用(或想避免)非核心模块,这是我想出的一个简单的子程序:

#!/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;

它产生如下输出:

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

或单线:

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

您可以在此处找到有关呼叫者的文档。

于 2019-06-14T10:48:06.647 回答
2

我的评论移至答案:

  1. 安装Devel::Confess正确的方式

    cpanm Devel::Confess
    
  2. 运行

    perl -d:Confess myscript.pl
    

出错时,这将显示整个调用堆栈列表。

于 2020-01-17T17:02:52.470 回答