在Perl中调试段错误的一些好方法或步骤是什么?

4

我的代码有两种情况不会导致分段错误:

  1. 当我至少在一个地方使用 Smart::Comments
  2. 通过调试器运行。

我已经追踪到了这个调用:

$action->{breakdown} 
    = join( ' '
          ,  each_pair { 
                my ( $name, $length ) = @_;
                return "x$length" if $name eq 'FILLER';
                push @$field_list_ref, $name;
                return "A$length";

            } @$field_def_ref
    );

其中each_pair在另一个模块中定义为:

sub each_pair (&@) { 
    my $block   = shift;
    return unless @_;
    my $caller  = caller();
    my $aref    = qualify( 'a', $caller );
    my $bref    = qualify( 'b', $caller );
    my @results;
    my $pairs   = 0;

    for ( my $index = 0; $index < $#_; $index += 2 ) { 
        $pairs++;
        my @pair                 = @_[$index..($index+1)];
        no strict 'refs';
        local ( $$aref, $$bref ) = @pair;
        push @results, $block->( @pair );
    }
    return wantarray || $pairs != 1 ? @results : shift @results;
}
  • 现在我知道可以用List::MoreUtils::natatime替换each_pair(尽管我听说它有一些bug),他们最近允许在我们的环境中使用这个模块,但我仍然对为什么这个调用会导致分段错误感兴趣——或者其他Perl程序员用于调试分段错误的方法。

我在这上面浪费了一些时间。


编辑

我有其他使用此函数的模块,有些指望能够使用$a$b,而且在同一模块的其他列表中也可以正常工作。我可以更改此函数的调用,我可以更改此文件的调用,但更改每个成功使用它的地方可能需要进行更多更改,这可能超出了我在这个时候被允许做出的更改。


是的。在生产代码中使用 Damian 模块?不是一个很好的想法。 - jrockway
5个回答

4

关于一般的调试步骤,您可以始终在gdb下运行Perl解释器。看到有用的内容的可能性并不一定很高,但我曾经尝试过几次,甚至可能有帮助。


1
你需要使用gdb和valgrind。Valgrind会让你的程序在接近实际错误时崩溃。 - jrockway

3

我将 echo chaos 对于 each_pair 函数的关注。如果你使用以下实现会发生什么?

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

sub each_pair(&@);

my $field_def_ref  = [ qw( FILLER 5 NOTFILLER 6 ) ];
my $field_list_ref;

print join(' ' => each_pair {
    my ($name, $length) = @_;
    return "x$length" if $name eq 'FILLER';
    push @$field_list_ref, $name;
    return "A$length";
} @$field_def_ref ), "\n";

print Dumper $field_list_ref;

sub each_pair( &@ ) {
    my $code = shift;
    my @results;

    for my $i ( 0 .. $#_/2 ) {
        push @results, $code->( shift, shift );
    }

    return @results;
}
__END__

3

在Perl中,分段错误异常地罕见。我不记得上一次遇到这种情况是什么时候了。

调试器足够侵入性,以至于代码在那里的行为有所不同并不特别令人惊讶,尽管肯定很令人沮丧。 Smart::Comments 使用源代码过滤器,我们都知道它们是 恶魔。查看 Smart::Comments 的源码,我发现它使用 List::Util,List::Util 通常使用XS实现。可能是 List::Util 解决了你的问题。尝试直接使用 List::Util 而不是 Smart::Comments。那不会解决任何问题,但它可能会消除源代码过滤器的不确定性。

不幸的是,你的问题似乎不在于代码本身,而在于不同事物之间意外的相互作用。你无法直接在Perl中触发分段错误。根源必须在 perl 本身或 XS 代码中。如果你能将其减少为一个小型但完整的示例,其他人可能就能够复制和隔离该问题。


我检查了我拍摄的 %INC 转储的差异,List::Util 出现在两个列表中。还是谢谢大家。 - Axeman

3

Segfault(段错误)可能源于C语言编写的外部模块通过XS进行绑定时出现的内存错误。

我建议在valgrind中运行您的脚本以发现错误:

valgrind perl ./yourfaultyscript.pl

看起来很有趣,但似乎它不能在AIX上运行。 - Axeman
是的,很遗憾,只在Linux上出现这个问题。 您在Linux系统上是否也遇到了同样的问题? - jeje

2

嗯,我无法理解为什么你的each_pair()会出现这种情况:

my $caller  = caller();
my $aref    = qualify( 'a', $caller );
my $bref    = qualify( 'b', $caller );

或者这样:
    no strict 'refs';
    local ( $$aref, $$bref ) = @pair;

如果你需要关闭 strict refs 并进行引用操作,那么在出现段错误的情况下,这些操作似乎立即具有可疑性。

如果你禁用了所有这些东西会发生什么?


那么调试器或更多与调试相关的代码会如何影响它? - Axeman
有时候调试代码会移动一些东西,以避免触发错误。这就是Heisenbug的本质。 http://catb.org/jargon/html/H/heisenbug.html - chaos
4
他试图让$a$b成为一对别名(就像sort做的那样),将它们放入调用者的命名空间中以在块中使用,并将它们本地化以避免与这些变量的任何其他使用发生冲突。禁用strict 'refs'仅允许使用符号引用。通常情况下,我不会怀疑它与段错误有关。但是qualify(我不熟悉)可能会有影响,因为我怀疑其中存在深层的XS魔术。 - Michael Carman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接