在Perl中获取堆栈跟踪?

26

我如何在Perl中获取堆栈跟踪?


perl -d script.pl DB<1> t 50 Trace = on (to level 51) DB<2> T - nurp
4个回答

38

为了调试需求,我喜欢使用Carp::Always

perl -MCarp::Always my_script.pl

1
我似乎需要执行-MCarp = verbose。 - brianegge

35

Carp::confess(来自 use Carp;)可以作为错误的一部分提供完整的堆栈跟踪。如果你只需要作为失败的一部分,confess 就足够了。

根据评论,以下是各种 Carp 函数的输出:

use strict;
use warnings;
use Carp qw/longmess cluck confess/;

sub foo {
  &bar;
}

sub bar {
   &baz;
}

sub baz {
   shift->();
}

my %tests = (
    'longmess' => sub { print longmess 'longmess' },
    'cluck'    => sub { cluck 'using cluck' },
    'confess'  => sub { confess 'using confess' },
);

while (my ($name, $sub) = each %tests) {
    print "$name - before eval:\n";
    eval {
        foo($sub);
    };
    print "$name - before if:\n";
    if ($@) {
        print "caught: $@";
    }
    print "$name - done\n\n";
}

运行此脚本,您会得到:

longmess - 在 eval 之前:
longmess 在 - 第 14 行
        main::baz 被调用于 - 第 10 行
        main::bar 被调用于 - 第 6 行
        main::foo('CODE(0x183a4d0)') 被调用于 - 第 26 行
        eval {...} 被调用于 - 第 25 行
longmess - 在 if 之前:
longmess - 完成
confess - 在 eval 之前: confess - 在 if 之前: caught: using confess at - 第 20 行 main::__ANON__() 被调用于 - 第 14 行 main::baz 被调用于 - 第 10 行 main::bar 被调用于 - 第 6 行 main::foo('CODE(0x183a3e0)') 被调用于 - 第 26 行 eval {...} 被调用于 - 第 25 行 confess - 完成
cluck - 在 eval 之前: using cluck at - 第 19 行 main::__ANON__() 被调用于 - 第 14 行 main::baz 被调用于 - 第 10 行 main::bar 被调用于 - 第 6 行 main::foo('CODE(0x183a434)') 被调用于 - 第 26 行 eval {...} 被调用于 - 第 25 行 cluck - 在 if 之前: cluck - 完成

运行此脚本但将 STDOUT 重定向(因此显示在 STDERR 上的内容),您会得到:

using cluck at - 第 19 行
        main::__ANON__() 被调用于 - 第 14 行
        main::baz 被调用于 - 第 10 行
        main::bar 被调用于 - 第 6 行
        main::foo('CODE(0x183a434)') 被调用于 - 第 26 行
        eval {...} 被调用于 - 第 25 行

3
这会将堆栈跟踪和错误发送到 STDERR;如果您需要捕获它,请直接使用底层的 Carp::longmess()。 而 Carp::cluck 类似 confess,但之后程序不会停止运行。 - ysth
2
我认为这是相反的 - cluck 是带有堆栈跟踪的警告,而 confess 是死亡。 - mob

18

有许多有用的、基于核心和CPAN的工具可以生成堆栈跟踪(正如其他答案所示)。然而,如果您想要自己操作,请查看caller内置函数。您可以使用它来沿着堆栈向下走,并查看正在发生的情况。


6
嗯,或者使用Devel::StackTrace - jrockway
11
那是一个不错的选择,但它需要一个外部模块,可能会让我感到不太吸引。为什么不直接把这个作为答案发出来,而不是点踩呢? - Robert P
您可以在此答案中找到使用caller的示例。 - x-yuri

14

使用caller的简单方法。 此代码不使用任何额外模块,只需在需要的地方包含即可。

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

虽然这段代码很好用,但我认为在输出中交换$call_details[1].":".$call_details[2](呼叫者)和$call_details[3](被呼叫者)更合理。 - zb226

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接