Perl';s caller()函数返回不正确的行号
我在Perl 5.10.1上运行了以下脚本:Perl';s caller()函数返回不正确的行号,perl,Perl,我在Perl 5.10.1上运行了以下脚本: #!/usr/bin/perl use strict; use warnings; foreach( my $x =0 ; $x < 1; $x++) { # Line 5 print_line(); # Line 6 } sub print_line { print "Function call from line: " . [caller(0)]->[2] . "\n";
#!/usr/bin/perl
use strict;
use warnings;
foreach( my $x =0 ; $x < 1; $x++) { # Line 5
print_line(); # Line 6
}
sub print_line {
print "Function call from line: " . [caller(0)]->[2] . "\n";
}
真正奇怪的是,如果我把一个随机语句扔到C循环中的一个空行中,<代码>呼叫者< /代码>返回正确的行号:
#!/usr/bin/perl
use strict;
use warnings;
foreach( my $x =0 ; $x < 1; $x++) {
my $x = 3;
print_line(); # Line 7
}
sub print_line {
print "Function call from line: " . [caller(0)]->[2] . "\n";
}
这是某种错误还是我可以做些什么让调用方准确地报告行号?我怀疑这可能是由于语句分隔符(分号)造成的。正如您可能已经发现的那样,对于正在运行的代码,
caller
报告的行号与foreach
循环相同
所以我认为发生的事情,是因为没有分号
如果要执行多行子呼叫,呼叫方
将报告第一行:
print "first call:", __LINE__, "\n";
print "Start of statement\n",
"a bit more on line ", __LINE__, "\n",
print_line(
1,
2,
3,
5,
);
你得到的是电话开始的号码,而不是结束的号码。因此,我认为这就是您得到的结果-语句在分号语句分隔符出现时开始-这是第一个示例中的foreach
行
因此,作为一种解决方法,我可能建议使用。虽然我也可能建议不要太担心它,因为它仍然会将您指向代码中正确的位置
如果您使用
croak
,可能出于同样的原因,您会得到类似的结果 我认为这可能是一个bug,因为如果您替换
foreach (my $x = 0 ; $x < 1 ; $x++) {
我不清楚到底发生了什么,但通过比较两个不同版本的操作树,我认为nextstate
op得到了不正确的优化。我的版本有
<;> nextstate(main 4 lineno.pl:11) v:*,&,x*,x&,x$,$ ->8
已从执行流程中删除
把它写成一个。$code>$perl-MO=concrete a.pl并不难
j离开[1参考]vKP/REFC->(结束)
1输入->2
下一个状态(main 6 a.pl:5)v:*,&,{,x*,x&,x$,$->3
5.设计vKS/2->6
3常数[IV 0]s->4
4 padsv[$x:3,5]sRM*/LVINTRO->5
6取消堆叠v*->7
i离开循环vK/2->j
7进入循环(下一步->上一步->我重做->8)v->e
-空vK/1->i
h和(其他->8)vK/1->i
g lt sK/2->h
e padsv[$x:3,5]s->f
f常数[IV 1]s->g
-lineseq vK->-
-范围vK->b 8 b
-前列表K->a
8推动标记s->9
-ex-rv2cv sK/2->-
9 gv[*打印行]s/EARLYCV->a
c preinc[t2]vK/1->d
b padsv[$x:3,5]sRM->c
d取消堆叠v->e
a、 pl语法正常
正在进行一些优化。范围被认为是不必要的,并被优化掉了。(注意“-
”意味着它从未达到。)
但同时,这删除了nextstate
op,它为警告和调用方设置行号
因此,这是一个错误,是由于不适当的优化造成的。正如已经指出的,这确实是Perl中的一个错误,至少可以追溯到5.10或11年前,但实际上我认为时间更长
据报道,这是一个Perl错误,尽管据称修复起来并不困难,但事实并非如此。修复起来可能也不那么容易,因为添加COP会降低性能,可能还需要一些管理工作来调整测试
即使这个错误被修复了,它也只能在Perl5.29和更高版本中修复,这对5.10没有帮助
因此,这里还有另一个策略,它不依赖于对Perl核心的更改,因此可以让用户更好地控制。然而,我要说的是,在前面它有点实验性,除非人们愿意在它上花费编码的精力,否则它不太可能追溯到5.10。现在我使用的最早的Perl版本是5.14,7年前,好像没有他的作品
使用可以编写不同的调用程序,我认为更好的调用程序()可以更详细地显示调用程序的位置。下面是修改后的程序:
#!/usr/bin/perl
use strict;
use warnings;
use B::DeparseTree::Fragment;
use Devel::Callsite;
sub dt_caller
{
my $level = $_ ? $_ : 0;
# Pick up the right caller's OP address.
my $addr = callsite($level+1);
# Hack alert 'main::main' should be replaced with the function name if not the top level. caller() is a little off-sync here.
my $op_info = deparse_offset('main::main', $addr);
# When Perl is in the middle of call, it has already advanced the PC,
# so we need to go back to the preceding op.
$op_info = get_prev_addr_info($op_info);
my $extract_texts = extract_node_info($op_info);
print join("\n", @$extract_texts), "\n";
}
foreach( my $x =0 ; $x < 1; $x++) {
print_line();
}
sub print_line {
dt_caller();
}
dt_caller()可以而且应该像Carp那样包装成一个包,这样你就看不到所有的丑陋。但是我会把它留给其他人。我要提到的是,为了让它工作,我必须做一些错误修复,所以这只在B::DeparseTree的3.4.0版开始工作。有趣-是的,似乎是这样。看起来就像它返回“循环”的行号一样。\uuuu line\uuuuuu
宏可能有助于故障排除。很好。很有趣。你用什么来提取代码的优化?虽然我看不到它是写出来的,但看起来这是一个已经修复的错误。Perl5版本20没有显示这种行为。@Borodin有趣,我怀疑它是错误当时我做的第一件事就是用5.20进行测试,我仍然看到了。我想知道有什么不同。我在一个Windows上运行了我的,而你可能在Linux上?尽管我无法想象这会有什么不同。
foreach my $x (0 .. 0) {
<;> nextstate(main 4 lineno.pl:11) v:*,&,x*,x&,x$,$ ->8
<0> ex-nextstate v ->8
$ perl -MO=Concise a.pl
j <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 6 a.pl:5) v:*,&,{,x*,x&,x$,$ ->3
5 <2> sassign vKS/2 ->6
3 <$> const[IV 0] s ->4
4 <0> padsv[$x:3,5] sRM*/LVINTRO ->5
6 <0> unstack v* ->7
i <2> leaveloop vK/2 ->j
7 <{> enterloop(next->b last->i redo->8) v ->e
- <1> null vK/1 ->i
h <|> and(other->8) vK/1 ->i
g <2> lt sK/2 ->h
e <0> padsv[$x:3,5] s ->f
f <$> const[IV 1] s ->g
- <@> lineseq vK ->-
- <@> scope vK ->b <---
- <0> ex-nextstate v ->8 <---
a <1> entersub[t5] vKS/TARG,2 ->b
- <1> ex-list K ->a
8 <0> pushmark s ->9
- <1> ex-rv2cv sK/2 ->-
9 <#> gv[*print_line] s/EARLYCV ->a
c <1> preinc[t2] vK/1 ->d
b <0> padsv[$x:3,5] sRM ->c
d <0> unstack v ->e
a.pl syntax OK
#!/usr/bin/perl
use strict;
use warnings;
use B::DeparseTree::Fragment;
use Devel::Callsite;
sub dt_caller
{
my $level = $_ ? $_ : 0;
# Pick up the right caller's OP address.
my $addr = callsite($level+1);
# Hack alert 'main::main' should be replaced with the function name if not the top level. caller() is a little off-sync here.
my $op_info = deparse_offset('main::main', $addr);
# When Perl is in the middle of call, it has already advanced the PC,
# so we need to go back to the preceding op.
$op_info = get_prev_addr_info($op_info);
my $extract_texts = extract_node_info($op_info);
print join("\n", @$extract_texts), "\n";
}
foreach( my $x =0 ; $x < 1; $x++) {
print_line();
}
sub print_line {
dt_caller();
}
$ perl bug-caller.pl
print_line()
------------