1

我在尝试调试的 Perl 中的一个相当复杂的程序(在 Linux 下)遇到问题。我可以用这里的简单代码段来模拟问题(test.pl):

use warnings;
use strict;
use feature qw/say/;

my @testa = ();
my $numelems = 10000;

# populate array/list of arrays
for (my $ix = 0; $ix < $numelems; $ix++) {
  my @miniarr = ($ix, 1);
  push(@testa, \@miniarr);
}

say "Array is now " . scalar(@testa) . " elements long";

my $BADnumelems = $numelems + 2;
my $sum = 0;

# loop through array/list of arrays
for (my $ix = 0; $ix < $BADnumelems; $ix++) {
  my @minientry = @{$testa[$ix]};
  $sum += $minientry[0];
}

say "Sum of elements is $sum";

运行此程序退出:

$ perl test.pl 
Array is now 10000 elements long
Can't use an undefined value as an ARRAY reference at test.pl line 22.

所以,现在我想调试它——但是错误导致程序死掉,并退出调试器:

$ perl -d test.pl 

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(test.pl:6):  my @testa = ();
  DB<1> c
Array is now 10000 elements long
Can't use an undefined value as an ARRAY reference at test.pl line 22.
 at test.pl line 22
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.  
  DB<1> p $sum

  DB<2> exit
$ 

 

然后,我在调试器(PerlMonks)中发现警告中断;所以我尝试添加这个:

...
use feature qw/say/;

$SIG{__DIE__} = sub { my($signal) = @_; say "DIEhandler: $signal"; $DB::single = 1; };
$SIG{__WARN__} = sub { my($signal) = @_; say "WARNhandler: $signal"; $DB::single = 1; };

my @testa = ();
...

...但这也会杀死调试器:

$ perl -d test.pl 

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(test.pl:6):  $SIG{__DIE__} = sub { my($signal) = @_; say "DIEhandler: $signal"; $DB::single = 1; };
  DB<1> c
Array is now 10000 elements long
DIEhandler: Can't use an undefined value as an ARRAY reference at test.pl line 25.

Can't use an undefined value as an ARRAY reference at test.pl line 25.
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.  

 

现在,问题是,我知道如果我用 Ctrl-C 中断程序 - 这通常会导致调试器进入单步模式;例如,您可以设置my $numelems = 100000000;,并在它循环时按 Ctrl-C - 您可以调试:

$ perl -d test.pl 

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(test.pl:6):  $SIG{__DIE__} = sub { my($signal) = @_; say "DIEhandler: $signal"; $DB::single = 1; };
  DB<1> c
^Cmain::(test.pl:14):     my @miniarr = ($ix, 1);
  DB<1> p $ix
148607
  DB<2> q

现在,我可以在源 perl 程序中插入断点

...
for (my $ix = 0; $ix < $BADnumelems; $ix++) {
  $DB::single = 1;               ### <=== BREAK HERE
  my @minientry = @{$testa[$ix]};
...

但它在以下情况下进入循环$ix = 0

$ perl -d test.pl 

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(test.pl:6):  $SIG{__DIE__} = sub { my($signal) = @_; say "DIEhandler: $signal"; $DB::single = 1; };
  DB<1> c
Array is now 10000 elements long
main::(test.pl:26):   my @minientry = @{$testa[$ix]};
  DB<1> p $ix
0
  DB<2> q

...而且我不想通过 10000 个元素来解决有问题的部分:)

 

所以,我想到了以下几点——如果有一种方法可以从 Perl 脚本本身引发/生成/创建 SIGINT (Ctrl-C) ,那么我就可以从模具处理程序中引发它,并希望能引起一步在进程死亡之前的调试器。所以,我的问题是:

  • 是否可以从 Perl 脚本中引发 SIGINT,这会破坏自身 - 如果是这样,如何?
  • 如果前一个是可能的,它是否能够在进程终止之前使用模具处理程序中的 Ctrl-C 进入调试器步骤模式?
  • 如果前一个是不可能的 - 我有什么可能导致进入调试器步骤模式,就在 program dies 的行?
4

2 回答 2

0

$DB::single可以设置为表达式,这样它只会在您指定的条件下中断。

...
for (my $ix = 0; $ix < $BADnumelems; $ix++) {

  $DB::single ||= ref($testa[$ix]) ne 'ARRAY';

  my @minientry = @{$testa[$ix]};
  $sum += $minientry[0];
}
...

...
for (my $ix = 0; $ix < $BADnumelems; $ix++) {
  eval {
      my @minientry = @{$testa[$ix]};
      $sum += $minientry[0];
  };
  $DB::single ||= $@; # break if eval block fails
  # give debugger somewhere to break before proceeding to next iteration
  1;
}
...

(我更喜欢$DB::single ||= ...这样做,$DB::single = ...因为如果您真的在该块(或该块之后的任何代码)中逐步执行每个迭代,Perl 将希望$DB::single为您设置为 true,并且您不希望您的代码无意中取消设置该值并阻止您不会单步执行代码的其他行。)

于 2013-02-07T17:04:41.537 回答
0

得到它(虽然不确定我是否正确理解所有内容:)),这要归功于这些:

只需使用eval换行修改代码,warn如下所示:

...
# loop through array/list of arrays
for (my $ix = 0; $ix < $BADnumelems; $ix++) {
  eval {
    my @minientry;
    @minientry = @{$testa[$ix]};
    $sum += $minientry[0];
  }; # just this causes step into debugger at $ix = 10001
  warn $@ if $@; # this causes step into debugger at $ix=10000 (OK)
}
...

...然后调试器的工作方式如下:

$ perl -d test.pl 

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(test.pl:6):  $SIG{__DIE__} = sub { my($signal) = @_; say "DIEhandler: $signal"; @_ = (); $DB::single = 1; };
  DB<1> c
Array is now 10000 elements long
DIEhandler: Can't use an undefined value as an ARRAY reference at test.pl line 27.

main::(test.pl:30):   warn $@ if $@; # this causes step into debugger at $ix=10000 (OK)
  DB<1> p $ix
10000
  DB<2> p @{$testa[$ix]}

  DB<3> p @{$testa[$ix-1]}
99991
  DB<4> p join("--", @{$testa[$ix]})

  DB<5> p join("--", @{$testa[$ix-1]})
9999--1
  DB<6> q

...也就是说,直接进入有问题的路线,这就是我想要的。

好吧,希望这对某人有所帮助,
干杯!

编辑:也可能相关:为什么我需要在使用 eval 之前本地化 $@?

于 2013-02-07T16:43:02.960 回答