Debugging
Turn on strict and warnings
The very first thing you must always check when debugging your code is whether the strict and warnings pragmata are turned on.
Make sure you have
use strict;
use warnings;
at the top of any program you're trying to debug, or may want to debug in the future. That really means every program ever, no?
See http://www.perlmonks.org/?node_id=482733 for another story of strict and warnings.
Check the return of every open
It's amazing how often you'll see people complaining that a piece of code doesn't work:
open( my $file, $filename );
while ( <$file> ) {
...
}
and then complain that the while loop is broken. The problem here is often that the file $filename doesn't actually exist, and the open fails, but without a check, you'll never know. Replace it with:
open( my $file, '<', $filename ) or die "Can't open $filename: $!";
Expand warnings with diagnostics
Sometimes the warning message doesn't explain as much as you'd like. For instance, why might you get this warning?
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.
Try running the code again with
use diagnostics;
at the top of the program. Now the warning looks like this:
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm
line 695 (#1)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl tells you what operation
you used the undefined value in. Note, however, that perl optimizes your
program and the operation displayed in the warning may not necessarily
appear literally in your program. For example, "that $foo" is
usually optimized into "that " . $foo, and the warning will refer to
the concatenation (.) operator, even though there is no . in your
program.
That's a lot more information to help you on your way finding the program.
Remember that you can specify modules and pragmata to be included on the command line, so you don't even have to edit your source code to use diagnostics. Run it again with the -/code command line switch:
perl -Mdiagnostics mycode.pl
Get a stack trace with signal-tweaking
Sometimes you'll get a warning but you can't tell how it got there. It'll say something like:
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.
So you can go see what the code at line 93 of that module is doing, but it doesn't tell you what your code was doing to get to that point. What you'd like to see is a trace of what subroutines were called.
When Perl calls die or warn, it goes through the subroutines specified in $SIG{__DIE__} and $SIG{__WARN__}, respectively. If you modify those to be more useful functions than CORE::die and CORE::warn, you've got a handy debugging tool. In this case, use the Carp module's confess function.
At the top of your program, add these lines:
use Carp qw( confess );
$SIG{__DIE__} = \&confess;
$SIG{__WARN__} = \&confess;
Now, when the code calls warn or die, the Carp::confess function handles it. In this case, confess prints the original warning, followed by a stack trace, and then stops execution of the program.
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.
at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695
WWW::Mechanize::find_link('WWW::Mechanize=HASH(0x180e5bc)', 'n', 'undef') called at foo.pl line 17
main::go_find_link('http://www.cnn.com') called at foo.pl line 8
Now we have much more information to debug our code, including exactly what functions were called, and what parameters were passed. From here, we can easily see that the third argument to find_link is undef, which is a great place to start investigating.
Get stack traces with Carp::Always
If you don't want to go through all the hoohah with overriding signal handlers, you can install the CPAN module Carp::Always.
After installing Carp::Always, adding one line to your code
use Carp::Always;
or calling your program with -MCarp::Always on the command line will always give a stack trace.
