是否可以在调试器下仅运行特定的子程序?

时间:2019-06-18 11:52:13

标签: perl debugging

我只想打印特定子程序的stacktrace。 可能吗 ?脚本在不带-d选项的perl下运行。

简单的例子:

#!/usr/bin/env perl

foo();

sub foo {
    print "Hello world\n";
    # enable debugger from this moment with PERLDB_OPTS='NonStop frame=1'
    bar();
    # disable debugger from this moment
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}

预期输出:

$ perl test.pl 
Hello world
    entering main::bar
Just another Perl hacker

我尝试和$ENV{PERL5DB}一起玩,但是没有效果:

$ENV{PERL5DB}='sub DB::DB {} sub sub {print ++$i, " $sub\n"; &$sub}';
bar();
$ENV{PERL5DB} = undef;

3 个答案:

答案 0 :(得分:3)

您可以直接在代码中直接设置$DB::frame

#!/usr/bin/env perl

foo();

sub foo {
    print "Hello world\n";
    $DB::frame = 1;
    bar();
    $DB::frame = 0;
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}

可以如下运行:

$ PERLDB_OPTS=NonStop perl -d try.pl
Hello world
   entering main::bar
Just another Perl hacker

就是这样。


如果您想手动执行并定义自己的自定义调试器,则可以按照以下步骤进行操作:

#!/usr/bin/env perl

foo();

sub foo {
    print "Hello world\n";
    $DB::xtrace = 1;
    bar();
    $DB::xtrace = 0;
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}
$ PERL5DB='BEGIN { package DB; sub DB {} sub sub { print STDERR "    entering $sub\n" if $xtrace; &$sub } }' perl -d try.pl
Hello world
    entering main::bar
Just another Perl hacker

答案 1 :(得分:1)

这仅仅是关于如何产生堆栈跟踪的问题吗?您不需要调试器。

use Carp;
sub bar {
    Carp::cluck("entering main::bar");
    ...
}

答案 2 :(得分:1)

将脚本转换为模数将使您受益。

您遇到的问题是foo()会在Perl进入运行时立即被直接调用。但是从调试的角度来看,这不是您想要的行为。

modulino的目的是允许将大部分代码视为嵌入式模块,并且仅在直接运行脚本时才运行启动或启动代码,而在脚本时则不运行而是作为模块加载。这样,您就可以针对模块代码编写测试套件,而仅将最小的启动代码保留在单元测试的范围之外。

以下是该主题的一些文章之一:https://perlmaven.com/modulino-both-script-and-module

出于您的目的,使用modulino,您可以更加选择使用Perl调试器运行什么。

如果您按如下所示组织代码,那么您会更加成功:

package MyModulino;
use parent 'Exporter';
our @EXPORT = qw(foo bar);

sub foo {
    print "Hello world\n";
    # enable debugger from this moment with PERLDB_OPTS='NonStop frame=1'
    bar();
    # disable debugger from this moment
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}

package main;

if(!caller) {
    MyModulino->import();
    foo();
}

现在您可以执行以下操作:

perl -I/some/path/to/script -e 'do "scriptname"; MyModulino::bar()'

换句话说,您现在可以将scriptname视为具有MyModulino包的模块,无论它是什么,您都可以直接调用bar()而不是依靠{{ 1}}来调用它。仅当foo()返回false时,才发生对foo()的调用,如果直接运行该程序,则将执行此操作,但是如果将其作为模块调用,则不会进行该操作。

通过将caller()视为模块,scriptname调用不会隐式运行,并且调试器代码可以从foo()包中调用bar()

这一行可以让您直接进入调试器,而无需调用MyModulino

foo()

您可以将此技术与perl -d -I/path/to/script -e 'do "scriptname"; MyModulino::bar()' 结合使用,以模拟在测试Test::MockModule时也不关心的调用堆栈部分。但是,当您开始走这条路时,您确实应该花时间在自动化测试脚本中实施这些策略。在这种情况下,“模块化”建议仍然适用。