views:

752

answers:

3

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.)

+3  A: 

Is it possible you have an RC file or environment variable (PERLDB_OPTS) that is modifying the dieLevel option of the debugger? I personally haven't used dieLevel but apparently when it's set to a value greater than zero it can force stack unwinding and "tends to hopelessly destroy any program that takes its exception handling seriously." (Quote from here).

Adam Bellaire
No env vars with PERL in them are set, but it seems my dieLevel is indeed set to 1! What could be doing that?
John Siracusa
Hmm, the rc file is located in ./.perldb or ~/.perldb on Unix systems, I think OS X would probably use the same file. As far as the documentation says, the option has to be set there if it's not on the command line or in the env. Maybe you can try explicitly setting dieLevel to zero?
Adam Bellaire
+5  A: 

I consider it a bug any time code behaves differently in the debugger.

Your problem might be related to this: Debugger corrupts symbol table munging. Essentially, the debugger appears to play some tricks with local -- presumably as part of sandboxing things to provide interactivity. Obviously, messing with the symbol table can have unexpected side-effects. I'd guess that the debugger is localizing $@ and thus obscuring your object. I can't think of a work-around.

Michael Carman
You mean you consider it a bug in the debugger?
brian d foy
+12  A: 

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.

brian d foy
You're a braver man than I am, brian. Every time I think about poking around in perl5db I get to the comments about what a tangled mess it is as lose my motivation.
Michael Carman