Fix intermittent failures in the LeakTracer on 5.18+, remove all workarounds
Peter Rabbitson [Thu, 22 Jan 2015 22:27:22 +0000 (23:27 +0100)]
ARGH! In the end the issue turned out to be stupid-simple: when PadWalker is
called it always returns us a hashref. This hashref is traversed, with its
own address recorded as seen. Once processed, the hashref itself is GCed,
leaving a stale entry in $seen_refs.

Up until 5.18 this was not a problem, likely due to no address reuse. However
after 5.18 PadWalker is very happy to return different hashrefs under the same
address over and over again. The $seen_refs entry leads to a skip, and the
actual contents of the pad are never examined - we've seen this hash already.

ARGH!

The decisive change is the single line around PadWalker - everything else is
cleanup of workarounds for this problem.

Changes
t/52leaks.t
t/lib/DBICTest/Util/LeakTracer.pm

diff --git a/Changes b/Changes
index 40f7c6b..0128cf2 100644 (file)
--- a/Changes
+++ b/Changes
@@ -14,6 +14,7 @@ Revision history for DBIx::Class
           opener: RT#99503)
         - Depend on newer Moo, fixing some interoperability issues:
           http://lists.scsys.co.uk/pipermail/dbix-class/2014-October/011787.html
+        - Fix intermittent failures in the LeakTracer on 5.18+
 
 0.082801 2014-10-05 23:55 (UTC)
     * Known Issues
index 95e8bd0..fb8c1c0 100644 (file)
@@ -339,9 +339,6 @@ unless (DBICTest::RunMode->is_plain) {
       ! DBICTest::RunMode->is_plain
         and
       ! $ENV{DBICTEST_IN_PERSISTENT_ENV}
-        and
-      # FIXME - investigate wtf is going on with 5.18
-      ! ( $] > 5.017 and $ENV{DBIC_TRACE_PROFILE} )
     ) {
 
       # FIXME - ideally we should be able to just populate an alternative
index ad61cd3..1f2caf5 100644 (file)
@@ -11,7 +11,6 @@ use Data::Dumper::Concise;
 use DBICTest::Util 'stacktrace';
 use constant {
   CV_TRACING => DBIx::Class::Optional::Dependencies->req_ok_for ('test_leaks_heavy'),
-  SKIP_SCALAR_REFS => ( $] > 5.017 ) ? 1 : 0,
 };
 
 use base 'Exporter';
@@ -43,10 +42,6 @@ sub populate_weakregistry {
       for keys %$reg;
   }
 
-  # FIXME/INVESTIGATE - something fishy is going on with refs to plain
-  # strings, perhaps something to do with the CoW work etc...
-  return $target if SKIP_SCALAR_REFS and reftype($target) eq 'SCALAR';
-
   if (! defined $weak_registry->{$refaddr}{weakref}) {
     $weak_registry->{$refaddr} = {
       stacktrace => stacktrace(1),
@@ -145,7 +140,7 @@ sub visit_refs {
       elsif (CV_TRACING and $type eq 'CODE') {
         $visited_cnt += visit_refs({ %$args, refs => [ map {
           ( !isweak($_) ) ? $_ : ()
-        } scalar PadWalker::closed_over($r) ] }); # scalar due to RT#92269
+        } values %{ scalar PadWalker::closed_over($r) } ] }); # scalar due to RT#92269
       }
       1;
     } or warn "Could not descend into @{[ refdesc $r ]}: $@\n";
@@ -183,8 +178,6 @@ sub symtable_referenced_addresses {
 
   my $refs_per_pkg;
 
-  my $dummy_addresslist;
-
   my $seen_refs = {};
   visit_namespaces(
     action => sub {
@@ -200,14 +193,7 @@ sub symtable_referenced_addresses {
       $refs_per_pkg->{$pkg} += visit_refs (
         seen_refs => $seen_refs,
 
-        # FIXME FIXME FIXME
-        # This is so damn odd - if we feed a constsub {1} (or in fact almost
-        # anything other than the actionsub below, any scalarref will show
-        # up as a leak, trapped by... something...
-        # Ideally we should be able to const this to sub{1} and just return
-        # $seen_refs (in fact it is identical to the dummy list at the end of
-        # a run here). Alas this doesn't seem to work, so punt for now...
-        action => sub { ++$dummy_addresslist->{ hrefaddr $_[0] } },
+        action => sub { 1 },
 
         refs => [ map { my $sym = $_;
           # *{"$pkg$sym"}{CODE} won't simply work - MRO-cached CVs are invisible there