Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this Perl BEGIN block act differently in the debugger?

I have some Perl code that runs fine outside the debugger:

% perl somefile.pl

but when I run it inside the debugger:

% perl -d somefile.pl

it behaves differently.

The files in question (there are several) are part of the test suite for a large Perl module (~20K lines of code). The tests do a lot of setup work at compile time and use BEGIN blocks. Here's some minimal reproduction code:

BEGIN
{
  package MyEx;

  sub new { bless {}, shift }

  package main;

  eval { die MyEx->new };

  if($@)
  {
    die "Really die"  unless($@->isa('MyEx'));
  }
}

print "OK\n";

If you put that in somefile.pl and run it, it prints "OK" as expected. If you run it in the debugger with perl -d somefile.pl, it dies with this error:

Can't call method "isa" without a package or object reference ...

The upshot is that $@ is not an object when the code runs under the debugger. Instead, it's an unblessed scalar containing this string:

" at somefile.pl line 9
    eval {...} called at somefile.pl line 9
    main::BEGIN() called at somefile.pl line 16
    eval {...} called at somefile.pl line 16
"

(Internal newlines and spacing preserved. That's the literal text, even the "..."s.)

I need code like this to run in the debugger. Using the debugger in the test suite is an important part of my workflow. The module uses exception objects and does a lot of stuff at compile time and expects an object thrown to be an object when caught.

My question (finally) is this: How can I get this to work? Is there a workaround? Is this a bug in the perl debugger module? What's the best way to go about getting this resolved? (I know that's several questions, but they're all related.)

I'm using perl 5.10.0 on Mac OS X 10.5.5.


The dieLevel thing suggested by Adam Bellaire looked promising, and indeed something (can't find out what) is setting it to 1 for me. But I set it to 0 using a ~/.perldb file and the problem persists. In fact, I set all three of the related settings to 0. My ~/.perldb file:

parse_options('dieLevel=0 warnLevel=0 signalLevel=0');

I confirmed that the settings are in effect by running the o command in the debugger. I see them all set to 0 when I run perl -de 0 and also when running the actual somefile.pl file.


Thanks, brian. I used perlbug to file a bug (RT 60890) and I've begun to sprinkle local $SIG{'__DIE__'} in all the appropriate places in my code. (I also noted in the bug that perldoc perldebug still seems to imply that the default dieLevel is 0.)

like image 271
John Siracusa Avatar asked Nov 26 '08 21:11

John Siracusa


1 Answers

This is a problem with perl5db.pl creating __DIE__ handlers. If I localize $SIG{__DIE__} in your eval, things work as you expect.

 eval { 
    local $SIG{__DIE__};
    die MyEx->new 
    };

If you don't do that, you're getting the handler from DB::dbdie, which uses Carp::longmess. That shouldn't happen if dieLevel is 0, but by default it is 1, and it gets set to 1 if it is not defined. This was a patch to perl5db.pl back in 2001, and previously the default had been 0.

You're supposed to turn this off with:

PERLDB_OPT="dieLevel=0" perl5.10.0 -d program

But there is still a code reference in $SIG{__DIE__} after that, and it's a reference to dbdie. I think this is a bug in handling the global variable $prevdie in perl5db.pl's dieLevel. At the end of that subroutine, there is:

# perl5db.pl dieLevel, around line 7777 
       elsif ($prevdie) {
            $SIG{__DIE__} = $prevdie;
            print $OUT "Default die handler restored.\n";
        }

But notice that after restoring $SIG{__DIE__}, it keeps the previous value in $prevdie, meaning whatever is in there leaks to another call. When I run that command line, there are two calls to dieLevel before it handles PERLDB_OPT, so $prevdie is probably dirty.

So, that's as far as I got before I didn't want to think about perl5db.pl anymore.

like image 58
brian d foy Avatar answered Sep 20 '22 20:09

brian d foy