nobull has asked for the wisdom of the Perl Monks concerning the following question:

I'm suffering from the dreaded attempts to free unreferenced scalars.

My understanding is that the cause of the problem is not local to the manifestation. Something somewhere (usually an XS module) has erroneously allowed an SV's refcount to drop to zero even though it still has a pointer to it. Something else then re-allocates that SV.

Now we have two unrelated scalars sharing an SV an totoally random things could happen.

The first thing then drops the refcount again. When the second thing tries to drop the refcount we get the warning.

What I need is something like a mark-and-sweep GC which doesn't actually GC but just checks that all the refcounts are right. By calling this at suitable points in the code I could perhaps narrow down the culpit.

I've had a look and Devel::Leak and various other Devel::* modules on CPAN but I've failed to find anything that attempts to walk the entire perl symbol table / stack / pads. Has anything like this ever been attempted?

UPDATE 2004-10-06: Although I still think there can be a problem with bad refcounts and action-at-a-distance I'm beginnning to suspect what's actually happening in my application is rather more straight forward. I now suspect that it has to do with aliases to elements of transitory tied hashes created in an object's '%{}' overload method.

Replies are listed 'Best First'.
Re: Mark-and-sweep-like mechanism to verify refcount integrity
by Corion (Patriarch) on Oct 04, 2004 at 11:35 UTC

    I'm not exactly sure that the "correctness" of a refcount can be determined, as one would have to scan all memory available to Perl for references to the SV in question, and even then the memory pointing to it might not be in use anymore.

    What I think should be feasible would be a (tree-structured?) dump of all memory references/SVs, together with their reference count. Then you can liberally sprinkle those calls to the dumper around and see where there is change in the tree when there should be none. On the other hand, the signal to noise ratio here is pretty bad I guess, as pretty much every op allocates or deallocates memory...

    I guess that the only other recourse would be valgrind or another memory bounds checker, but I'm not sure if they (can) catch and diagnose double frees via refcount mess-ups. Maybe you can trick Perl into not reusing existing SVs, so you then get a real leak or a real access violation?

    I have never done any of this, and the only thing possible without any too deep XS magic seems to me the tree dump, courtesy of Scalar::Util and PadWalker, and the output of that won't necessarily be helpfull...

      While valgrind is an excellent tools to check whether perl itself has any memory related problems, it won't help at all for debugging Perl programs. valgrind sits between perl and the OS (roughly speaking) and checks memory access with respect to malloc()ed and free()d memory. valgrind doesn't know the difference between an SV and a hole in the ground.
Re: Mark-and-sweep-like mechanism to verify refcount integrity
by PodMaster (Abbot) on Oct 04, 2004 at 13:07 UTC
    It really depends. On perl5.6.x (and probably earlier), this will cause Attempt to free unreferenced scalar at - line 2.
    @a = (bless {}, 'X'); sub X::DESTROY { @a = () } @a=(); __END__
    Whats important to note is that the warning comes with a line number, which you can use to investigate (it should give away which variable caused it). If XS is to blame, it should be possible check wether SvREFCNT_dec() was called too many times, or that SvREFCNT_inc() was called too few times, or that the SV was mortalized when it shouldn't have been, or that memory has been corrupted.

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      Whats important to note is that the warning comes with a line number, which you can use to investigate (it should give away which variable caused it)

      As I said in my orginal post I suspect there is action-at-a-distance going on. The real culprit is in one place but the bug is manifesting with respect to another variable that's getting accidently aliased.

      When I run the code (which is in a module) in a standalone CGI script it doesn't give warnings. When I run the code under Apache::Registry then it starts giving warnings in many unrealated places after a few transactions.

      Here is an example of one of the places where it's giving a warning.

      for my $field ( @$self{@{$_{FIELDS}}} ) { $field->set_related(AFTER => \@keys ); $field->{MULTI} = 1 if $_{MULTICODE} && ! exists $field->{MUL +TI}; $field->{ORDERED} = 1 if $_{ORDER} && ! exists $field->{ORDERE +D}; $field->{CONTEXT_KEYS} = $_{CONTEXT_KEYS} if $_{CONTEXT_KEYS} +&& ! exists $field->{CONTEXT_KEYS}; if ( $table ) { $field->{TABLE} = $table unless exists $field->{TABLE}; $field->{COLUMN} = $fieldmap->{$field} || "$field" unless exists $field->{COLUMN}; } } if ( $structured_field ) { # Warning here! $fieldmap = {%$fieldmap}; my @pseudo_fields = map { @$_ } values %$structured_field; @$fieldmap{@pseudo_fields} = map { [ $fieldmap->{$_} || $_ ] } + @pseudo_fields; }

      As you see the line where the warning appears has no buisness unreferencing any scalars. The variable  $structured_field is, by the way, undef.

      The perhaps interesting thing is that the variables $self and $field are not a blessed hashes but rather are objects that overload '%{}'. Furthermore the '%{}' overload on $self is done via a transitory tied hash...

      use overload '%{}' => sub { my $self = shift; tie my(%h), $self; \%h }; sub TIEHASH { shift }

      UPDATE Replacing the transitory tied hash with a persistant one did not fix the problem.

Re: Mark-and-sweep-like mechanism to verify refcount integrity
by tilly (Archbishop) on Oct 05, 2004 at 16:27 UTC
    I'd think that this would be tricky. The problem is that while you might track what Perl is doing (be careful to not follow weak references!), various XS modules are going to have pointers into Perl data structures that come from non-Perl data structures. How do you find and account for those?

    A cheesy but possibly effective way to solve this problem is build a custom version of Perl that allows you to log all calls to increment/decrement a particular variable. Install that in a custom location, recompile all of your XS modules against that, and see what you get.

    Glancing at the code, you'd want to redefine SvREFCNT_inc and SvREFCNT_dec in sv.h. The latter is easier to add custom logic to - it calls sv_free in sv.c so you can edit that. The former is an inline macro that may be harder to edit.

    From an uninformed glance at the code, if you want a custom per-scalar flag the sv_flag's struct has 8 more flags available: 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000001, 0x00000002, 0x00000004, and 0x00000008. However seeing how much work is going into avoiding using those makes me suspect that someone, somewhere uses them in a way that is not obvious to me.

      I'd think that this would be tricky. The problem is that while you might track what Perl is doing (be careful to not follow weak references!), various XS modules are going to have pointers into Perl data structures that come from non-Perl data structures. How do you find and account for those?

      I wouldn't bother. The tool can simply report those as refcount inconsitancies. The tool could show all the paths if found to each SV together with the reference count on the SV. If there are legitmate refernce paths through non-perlish structures then these should show up.

      That said, when I write XS modules I avoid this problem. If I need my objects to contain hidden internal stuff that holds references to SVs I do so by hanging Perl structures off of the MAGIC list. In this way my private stuff is hidden from the Perl programmer but could still be traversed by the proposed tool.