【问题标题】:How can I get a call stack listing in Perl?如何在 Perl 中获取调用堆栈列表?
【发布时间】:2010-09-18 17:52:34
【问题描述】:

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

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

【问题讨论】:

  • 虽然这不能回答您的问题,但它可能会帮助您解决问题 :-) 这是一篇有趣的文章,描述了一种如何找出谁从 Mark Dominus 更改您的变量的方法

标签: perl stack-trace callstack


【解决方案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::confessCarp::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 移至答案:

          1. 安装Devel::Confessright way

            cpanm Devel::Confess
            
          2. 运行

            perl -d:Confess myscript.pl
            

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

          【讨论】:

            猜你喜欢
            • 2011-01-02
            • 2011-05-03
            • 2011-01-22
            • 2017-04-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-04-26
            • 2011-03-01
            相关资源
            最近更新 更多