Devel--Cover icon indicating copy to clipboard operation
Devel--Cover copied to clipboard

Devel::Cover interfers with Scope Guards

Open kentfredric opened this issue 10 years ago • 6 comments

use strict;
use warnings;

{
  my $scope;

  BEGIN {
    require Scope::Guard;
    $scope = Scope::Guard::scope_guard(sub { print "scope clean\n"; });
    print "End of scope creator\n";
  }

  print "End of guarded scope\n";
}  # Should harvest here

print "Beginning of new scope\n";


END {
  print "End of program\n";
}

# Scope harvests here

Without Devel::Cover, the above code does:

End of scope creator
End of guarded scope
scope clean
Beginning of new scope
End of program

With Devel::Cover, the above code does:

End of scope creator
End of guarded scope
Beginning of new scope
End of program

scope clean

kentfredric avatar Feb 20 '15 00:02 kentfredric

Ouch, my mistake, this seems to be a general problem of the perl debugger ! :(. perl -d:Confess is enough to trigger this.

kentfredric avatar Feb 20 '15 00:02 kentfredric

Ok, weeded out the logic that trips into fails on -d and -d:Confess. However, still have one test case that works on both of those, and still fails on -MDevel::Cover

use strict;
use warnings;

BEGIN {
  package Foo;
  sub DESTROY {
    $_[0]->[0]->();
  }
}
sub safely {
  my ($var) = @_;
  return bless [ sub { print "$var\n"} ], 'Foo';
}

{

  my $scope;

  BEGIN {
    $scope = safely("Destruction");
    print "End of scope creator\n";
  }

  print "End of guarded scope\n";
}

print "Beginning of new scope\n";


END {
  print "End of program\n";
}

kentfredric avatar Feb 20 '15 02:02 kentfredric

Further whittled the problem down to appearing somewhere in XS.

Putting FAIL=1 in ENV makes instance destruction wait till GlobalDestruction

use strict;
use warnings;

BEGIN {

    package Devel::Cover;

    use strict;
    use warnings;

    use DynaLoader ();
    our @ISA = "DynaLoader";

    if ( $ENV{"FAIL"} ) {
        bootstrap Devel::Cover;
    }
}

BEGIN {

    package Foo;

    sub DESTROY {
        $_[0]->[0]->();
    }
}

sub safely {
    my ($var) = @_;
    return bless [ sub { print "$var\n" } ], 'Foo';
}

{

    my $scope;

    BEGIN {
        $scope = safely("Destruction");
        print "End of scope creator\n";
    }

    print "End of guarded scope\n";
}

print "Beginning of new scope\n";

END {
    print "End of program\n";
}

kentfredric avatar Feb 20 '15 05:02 kentfredric

Digging down to the roots, the smallest blob of XS that replicates this behaviour is this:


#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

MODULE = Devel::Cover PACKAGE = Devel::Cover

BOOT:
    {
        MY_CXT_INIT;
#if PERL_VERSION > 6
        PL_savebegin = TRUE;
#endif
    }

Seems PL_savebegin breaks things :/

kentfredric avatar Feb 20 '15 06:02 kentfredric

Thanks for doing so much to track this down!

The PL_savebegin being set is to be able to get coverage of BEGIN blocks. But yes, this is going to affect behaviour so it's not a good solution.

What I really need (I think!) is a hook that gets called just before a block is destroyed. This would also solve other problems where coverage cannot be collected at the moment.

pjcj avatar Feb 22 '15 18:02 pjcj

The opfreehook branch is work in this direction, but it's not there yet.

pjcj avatar Aug 03 '15 22:08 pjcj