Debugging Perl - Doing Whatever I Want
(Page 3 of 5 )
I can change the warn and die functions myself by messing with %SIG. I like to use these to peer into code I’m trying to figure out, but I don’t use these to add features to code. It’s just part of my debugging toolbox.
The pseudokeys __WARN__ and __DIE__ hold the functions that perform those actions when I use the warn or die functions. I can use a reference to a named subroutine or an anonymous subroutine:
$SIG{__DIE__} = \&my_die_handler;
$SIG{__DIE__} = sub { print "I'm about to die!" )
Without going through the entire code base, I can change all of the die calls into the more informative croak calls.§ In this example, I preface the subroutine call with an & and no parentheses to trigger Perl’s feature to pass on the current argument list to the next subroutine call so croak gets all of the arguments I pass:
use Carp;
$SIG{__DIE__} = sub { &Carp::croak };
die "I'm going now!"; # really calls croak now
If I only want to do this for part of the code, I can use local (since %SIG is a special variable always in main::). My redefinition stays in effect until the end of the scope:
local $SIG{__DIE__} = sub { &Carp::croak };
After either of my customized routines runs, the functions do what they would otherwise do; warn lets the program continue, and die continues its exception processing and eventually stops the program.‖
Since croak reports each level of the call stack and I called it from an anonymous subroutine, I get an artifact in my output:
use Carp;
print "Starting program...\n";
$SIG{__DIE__} = sub {
local $Carp::CarpLevel = 0;
&Carp::croak;
};
foo(); # program dies here
sub foo { bar() }
sub bar { die "Dying from bar!\n"; }
In the stack trace, I see a subroutine call from __ANON__ followed by the subroutine calls I expect to bar() and foo():
Starting program...
Dying from bar!
at die.pl line 12
main::__ANON__('Dying from bar!\x{a}') called at die.pl line 20
main::bar() called at die.pl line 18
main::foo() called at die.pl line 16
I change my anonymous subroutine to adjust the position in the stack where croak starts its report. I set the value of $Carp::CarpLevel to the number of levels I want to skip, in this case just 1:
$SIG{__DIE__} = sub {
local $Carp::CarpLevel = 1;
&Carp::croak;
};
Now I don’t see the unwanted output:
Starting program...
Dying from bar!
at die.pl line 12
main::bar() called at die.pl line 18
main::foo() called at die.pl line 16
For a real-life example of this in action, check out the CGI::Carp module. Lincoln Stein uses the %SIG tricks to redefine warn and die in a web-friendly way. Instead of an annoying “Server Error 500” message, I can get useful error output by simply loading the module. While loading, CGI::Carp sets $SIG{__WARN__} and $SIG{__DIE__}:
use CGI::Carp qw(fatalsToBrowser);
The fatalsToBrowser function takes over the resulting page to show me the error, but the module has other interesting functions such as set_message, which can catch compile-time errors and warningsToBrowser, which makes the warnings in HTML comments embedded in the output.
Of course, I don’t recommend that you use this in production code. I don’t want users to see the program’s errors. They can be handy when I have to debug a program on a remote server, but once I figure out the problem, I don’t need it anymore. By leaving it in there I let the public figure out how I’m doing things, and that’s bad for security.
Next: Program Tracing >>
More Perl Articles
More By O'Reilly Media
|
This article is excerpted from chapter four of Mastering Perl, written by Brian D Foy (O'Reilly; ISBN: 0596527241). Check it out today at your favorite bookstore. Buy this book now.
|
|