【问题标题】:How can I print all executed subroutines?如何打印所有执行的子例程?
【发布时间】:2018-12-16 01:54:41
【问题描述】:

例如我有以下 Perl 脚本

{ 
    package A;

    {
        package B;

        sub _y {
            print "Just Another Perl Hacker\n";

        }

    }

    sub _x {
        print "Hello world!\n";
        B::_y();
    }
}


use strict;
use warnings;

_x();

如何将每个执行的带有包限定符的子打印到 STDERR 或任何日志文件?

例如从上面的脚本中,我希望看到以下输出:

1 A::_x()
2 B::_y()

我认为可以使用像 Devel::NYTProf 这样的调试器,但我还没有找到用于该简单任务的特定调试器模块或其参数。

有什么想法吗?

【问题讨论】:

  • 此代码不起作用。最后一行应该是A::_x() 而不仅仅是_x()
  • 嗯,代码在我身边工作,没有错误。

标签: perl trace call-graph


【解决方案1】:

考虑调试模块会让您走上正轨。启用调试模式后,Perl 在程序中的每个执行步骤调用函数DB::DB()。从这里您可以从 caller 内置函数中提取子例程名称(其中将包括包名称),然后按照您认为合适的方式输出它。

从您的 @INC 路径中某个名为 Devel/AllSubs.pm 的文件开始:

package Devel::AllSubs;
my $count = 0;
my $last_sub = '::';
sub DB::DB {
    my ($pkg, $file, $line,$sub) = caller(1);
    if ($sub ne $last_sub) {
        print STDERR ++$count," $sub\n";
        $last_sub = $sub;
    }
}
1;

然后运行你的程序

$ perl -d:AllSubs script.pl

示例输出:

1 A::_x
Hello world!
2 B::_y
Just Another Perl Hacker

【讨论】:

  • 感谢您的回答,但您的代码在“)[”附近有语法错误
  • @PaulSerikov,已修复
  • 请注意,这会大大降低程序的速度。
  • 这将每次打印多次,甚至每次调用多次。将|| !$seen{$_}++ 添加到if 即可。
【解决方案2】:

可以使用标准的 perl 调试器来完成:

$ PERLDB_OPTS="NonStop frame=1" perl -d prog.pl
  entering CODE(0x260cd78)
   entering strict::import
  entering CODE(0x260cd18)
   entering warnings::import
Package try.pl.
  entering DB::Obj::_init
  entering A::_x
Hello world!
   entering B::_y
Just Another Perl Hacker

(请注意,我必须将 _x(); 更改为 A::_x(); 才能运行您的代码。)

如果要将输出放在文件中,请添加LineInfo=filenamehere。有关详细信息,请参阅perldoc perldebug。 (特别是,如果您将选项更改为 frame=2,您还会收到从子程序返回的消息。)

CODE 引用用于 use 语句周围的隐式 BEGIN 块:

use strict;

真正的意思

BEGIN {
    require "strict.pm";
    strict->import();
}

【讨论】:

    【解决方案3】:

    另一种解决方案,使用已经提到的Devel::NYTProf(更准确地说是分析器而不是调试器),它不需要您编写任何额外的代码并为您提供更多信息。

    % perl -d:NYTProf yourscript.pl
    

    这将默认创建一个nytprof.out 文件。然后你可以这样做:

    % nytprofcalls nytprof.out
    

    这将为您提供详细的通话信息。示例脚本:

    use strict;
    
    sub f1 {
      print "hi;\n";
      f2();
    }
    
    sub f2 {
      print "hi2\n";
    }
    
    f1();
    

    和样本输出:

    main::f1;main::f2 30
    main::f1;main::CORE:print 124
    main::f1;main::f2;main::CORE:print 40
    main::BEGIN@1 4262
    main::f1 113
    main::BEGIN@1;strict::import 39
    main::BEGIN@1;strict::BEGIN@7 396
    main::BEGIN@1;strict::BEGIN@7;strict::CORE:match 58
    

    您可以看到 NYTProf 还列出了对核心函数 (CORE::print) 的调用等。

    作为奖励,您可以使用以下命令查看整个配置文件输出:

    % nytprofhtml
    

    然后打开报告页面,例如使用:

    % firefox nytprof/index.html
    

    您可以查看已执行的子例程(按运行时间排序)、运行次数等等。

    【讨论】:

    • 是的,NYTProf 是一个很棒的工具,但是用于分析,而不是用于代码跟踪。 NYTProf 按浪费的时间顺序显示已执行函数的列表,而不是按原始顺序排序的机会。顺便说一句,您可以通过设置start=init 而不是默认的start=begin 来禁用记录核心函数调用。我创建了一个从 cpan 测试跟踪器模块的 Playground 存储库,欢迎您检查并贡献github.com/pavelsr/perl-debug-example
    猜你喜欢
    • 2018-09-01
    • 2015-01-13
    • 1970-01-01
    • 2016-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多