[英]Create/raise a SIGINT (Ctrl-C) from Perl script - and cause debugger entry in step mode from die handler?
我正在尝试调试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),那么我将能够从die处理程序中引发它,并希望引起这一点。进程终止之前的调试器。 因此,我的问题是:
die
S' 顺其自然(尽管不确定我是否理解正确:)
),这要归功于这些:
只需使用eval
包装修改代码,并warn
如下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之前我需要本地化$ @?
可以将$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,并且您不希望您的代码无意中取消该值并阻止您单步执行其他代码行。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.