“PmmREFCNT_dec:REFCNT递减到0以下”是什么意思?

时间:2014-05-09 16:28:05

标签: perl segmentation-fault

我必须让perl非常沮丧,因为它给了我一条错误消息,而perldiag中没有记录:

  

PmmREFCNT_dec:对于53a6930,REFCNT递减至0以下!。

根据其情绪,有时会出现以下情况:

  

*** glibc检测到*** / usr / bin / perl:双重免费或损坏(!prev):0x0000000004e58a60 *

......或更明显地说:

  

分段错误

这显然是致命的,但我也测试过它是可以捕获的。使用Try::Tiny时,我将始终在同一位置捕获错误,但在不使用它时,会在崩溃发生之前执行更多指令。此外,即使我的模块完全是确定性的,并且我非常确定所有依赖关系也是如此,但错误并不是一直发生的。

不幸的是,给我这个问题的模块是 huge,有很多依赖项,我无法在一个较小的例子上复制问题。因此,我不能请求帮助调试它,但如果熟悉Perl内部的人知道在什么情况下发生此错误,这可能有助于我(或任何其他人将看到此消息)找到问题的根源和/或解决方法。

如果它有用,一般的想法是这样的。我有两个课程,我们称他们为ThingSetOfThingsSetOfThings的属性是一组Thing个实例。这两个类都有一个explode方法,可以这样做:

# SetOfThings
sub explode {
    my $self = shift;
    my $new  = dclone $self;
    delete $new->{'some_attribute'};
    $new->set_of_things( map { $_->explode } $self->constraints );
    return $new;
}

# Thing
sub explode {
    my $self = shift;
    return  map { new Thing( do_something_fancy ) } keys %$self;
}

在调用SetOfThings::explode或将SetOfThings::set_of_things作为吸气剂调用时,通常会出现错误。

编辑:Backtrace

我不相信我有足够的能力解释它,但我从gdb获得了回溯:

#0  0x00007ffff70a6094 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff70a76a8 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffff70aab1c in free () from /lib/x86_64-linux-gnu/libc.so.6
#3  0x00007ffff7b0869b in Perl_hv_undef_flags () from /usr/lib/libperl.so.5.14
#4  0x00007ffff7b1ae66 in Perl_sv_clear () from /usr/lib/libperl.so.5.14
#5  0x00007ffff7b1b292 in Perl_sv_free2 () from /usr/lib/libperl.so.5.14
#6  0x00007ffff7b04bc3 in Perl_hv_free_ent () from /usr/lib/libperl.so.5.14
#7  0x00007ffff7b04e6e in ?? () from /usr/lib/libperl.so.5.14
#8  0x00007ffff7b08683 in Perl_hv_undef_flags () from /usr/lib/libperl.so.5.14
#9  0x00007ffff7b1ae66 in Perl_sv_clear () from /usr/lib/libperl.so.5.14
#10 0x00007ffff7b1b292 in Perl_sv_free2 () from /usr/lib/libperl.so.5.14
#11 0x00007ffff7b42cef in Perl_leave_scope () from /usr/lib/libperl.so.5.14
#12 0x00007ffff7b11112 in Perl_pp_leave () from /usr/lib/libperl.so.5.14
#13 0x00007ffff7b0bce6 in Perl_runops_standard () from /usr/lib/libperl.so.5.14
#14 0x00007ffff7aad815 in perl_run () from /usr/lib/libperl.so.5.14
#15 0x0000000000400f89 in main ()

编辑2:Valgrind回溯

这是我在运行valgrind时获得的。虽然我仍然不确定发生了什么,但至少现在我知道应该责怪谁。 : - )

==27226== Invalid free() / delete / delete[] / realloc()
==27226==    at 0x4C27D4E: free (vg_replace_malloc.c:427)
==27226==    by 0xA138F42: PmmREFCNT_dec (in /usr/lib/perl5/auto/XML/LibXML/LibXML.so)
==27226==    by 0xA11D3FA: XS_XML__LibXML__Node_DESTROY (in /usr/lib/perl5/auto/XML/LibXML/LibXML.so)
==27226==    by 0x4EE770B: Perl_pp_entersub (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4E7AB90: Perl_call_sv (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EEDBD8: Perl_sv_clear (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EEE291: Perl_sv_free2 (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4ED7BC2: Perl_hv_free_ent (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4ED7E6D: ??? (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EDB682: Perl_hv_undef_flags (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EEDE65: Perl_sv_clear (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EEE291: Perl_sv_free2 (in /usr/lib/libperl.so.5.14.2)
==27226==  Address 0x17d0b710 is 0 bytes inside a block of size 32 free'd
==27226==    at 0x4C27D4E: free (vg_replace_malloc.c:427)
==27226==    by 0xA138F42: PmmREFCNT_dec (in /usr/lib/perl5/auto/XML/LibXML/LibXML.so)
==27226==    by 0xA11D3FA: XS_XML__LibXML__Node_DESTROY (in /usr/lib/perl5/auto/XML/LibXML/LibXML.so)
==27226==    by 0x4EE770B: Perl_pp_entersub (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4E7AB90: Perl_call_sv (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EEDBD8: Perl_sv_clear (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EEE291: Perl_sv_free2 (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4ED7BC2: Perl_hv_free_ent (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EDA919: Perl_hv_common (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4F0EEC7: Perl_pp_delete (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4EDECE5: Perl_runops_standard (in /usr/lib/libperl.so.5.14.2)
==27226==    by 0x4E80814: perl_run (in /usr/lib/libperl.so.5.14.2)

1 个答案:

答案 0 :(得分:3)

引用ikegami的评论,因为我无法更好地制定评论:

  

这是Perl或XS模块中的错误。当引用计数达到零时,应该释放一个变量,但是当变量已经为零时,某些东西试图减少变量的引用计数。

正如valgrind的输出所示,在此特定情况下,问题出现在XML::LibXML

我认为,只要问题得到理解和解决,Sinan Ünür建议更新XML::LibXML即可。不幸的是,从2.0001(Debian稳定版本)更新到2.0116(CPAN版本)并没有解决它。

最终解决问题的方法是修改SetOfThings::explode以便创建新实例并复制所需的属性而不是克隆当前实例并删除属性不需要:

sub explode {
    my $self = shift;
    my $new  = __PACKAGE__->new;
    $new->some_attribute('whatever');
    $new->set_of_things( map { $_->explode } $self->constraints );
    return $new;
}

克隆然后删除的SetOfThings对象的一个​​属性是DOM,显然XML::LibXML 欣赏。感谢这些知识和发布的评论,我终于能够在一个非常小的脚本中重现我的问题并发布bug report

#!/usr/bin/perl

use strict;
use warnings;

use Clone 'clone';
use XML::LibXML;

my $dom1 = new XML::LibXML::Document;
my $dom2 = clone $dom1;

正如ikegami所指出的,克隆Perl变量不会复制库使用的underlying C structure。但XML::LibXML确实提供了cloneNode方法,因此将最后一行更改为

my $dom2 = $dom1->cloneNode(1)

给出了期望的结果。