如何在 Perl 中获取调用堆栈列表?
Posted
技术标签:
【中文标题】如何在 Perl 中获取调用堆栈列表?【英文标题】:How can I get a call stack listing in Perl? 【发布时间】:2010-09-18 17:52:34 【问题描述】:有没有一种方法可以访问(用于打印输出)子 + 模块列表到 Perl 脚本中当前位置之前的任意深度的子调用?
我需要对一些 Perl 模块 (.pm's) 进行更改。工作流程是从网页通过 cgi 脚本启动的,通过几个模块/对象传递输入,这些模块/对象以我需要使用数据的模块结尾。数据在某处发生了变化,我需要找出在哪里。
【问题讨论】:
虽然这不能回答您的问题,但它可能会帮助您解决问题 :-) 这是一篇有趣的文章,描述了一种如何找出谁从 Mark Dominus 更改您的变量的方法 【参考方案1】:您可以使用Devel::StackTrace。
use Devel::StackTrace;
my $trace = Devel::StackTrace->new;
print $trace->as_string; # like carp
它的行为类似于 Carp 的跟踪,但您可以更好地控制帧。
一个问题是引用是字符串化的,如果引用的值发生变化,您将看不到它。但是,您可以使用 PadWalker 生成一些东西来打印出完整的数据(不过会很大)。
【讨论】:
一个非常有用的替代方案:perl -d:Confess script.pl
from Devel::Confess。【参考方案2】:
caller 可以做到这一点,尽管您可能需要更多信息。
【讨论】:
【参考方案3】:还有Carp::confess
和Carp::cluck
。
【讨论】:
【参考方案4】: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;
【讨论】:
longmess 不再是 Carp 的文档化或自动导出功能。但是:my $mess = carp();
将提供相似但不相同的行为。【参考方案5】:
一个更漂亮的:Devel::PrettyTrace
use Devel::PrettyTrace;
bt;
【讨论】:
【参考方案6】:此代码无需任何额外模块即可工作。 只需在需要的地方添加即可。
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";
【讨论】:
整洁的技术(虽然我必须说我已经有一段时间没有涉足 Perl 了:) 我不得不说非常好!谢谢:-) 从 0 开始,而不是 1。【参考方案7】:如果你不能使用(或想避免)非核心模块,这是我想出的一个简单的子程序:
#!/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";
您可以在调用者here 上找到文档。
【讨论】:
【参考方案8】:将my comment 移至答案:
安装Devel::Confessright way
cpanm Devel::Confess
运行
perl -d:Confess myscript.pl
出现错误时,这将显示整个调用堆栈列表。
【讨论】:
以上是关于如何在 Perl 中获取调用堆栈列表?的主要内容,如果未能解决你的问题,请参考以下文章