Debugging

Turn on strict and warnings

Whenever debugging code,make sure the strict and warnings pragmata are turned on.

Put these two lines

  1. use strict;
  2. use warnings;

at the top of any program you're trying to debug, or may want to debug in the future. (If you think about it, that's every program ever, isn't it?)

The http://perldoc.perl.org/strict.html strict pragma forces you to use a number of features that allow Perl to find errors during compile time. First and foremost, under strict, variables must be declared before they are used; in most cases, this means using my:

  1. use strict;
  2. my $foo = 7; # OK, normal variable
  3. print "foo is $fooo\n"; # Perl complains and aborts compilation

Without strict, Perl would happily run the above programming, and you would be sad, wondering why $foo didn't have a value. Enabling strictures can save a lot of headache induced by typos.

Additionally, strict disallows the use of most barewords:

  1. no strict;
  2. $foo = Lorem;
  3. print "$foo\n"; # Prints "Lorem"
  4.  
  5. use strict;
  6. my $foo = ipsum; # Complains about bareword
  7. $foo = (
  8. Lorem => 'ipsum' # OK, barewords allowed on left of =>
  9. );
  10.  
  11. $SIG{PIPE} = handler; # Complains
  12. $SIG{PIPE} = \&handler; # OK
  13. $SIG{PIPE} = "handler"; # Also, OK, but above is preferred

Finally, enabling strict throws a runtime error if you use http://perldoc.perl.org/perlref.html#Symbolic-references symbolic references:

  1. no strict;
  2. $name = "foo";
  3. $$name = "bar"; # Sets the variable $foo to 1
  4. print "$name $$name\n"; # Prints "foo bar"
  5.  
  6. use strict;
  7. my $name = "foo";
  8. $$name = "bar"; # Complains: can't use "foo" as ref

The warnings pragma enables a whole host of useful complaints from Perl, letting you know about things in your program that you most likely didn't intend:

  1. use warnings;
  2. my $foo = ;
  3. $foo += 3;
  4. my $foo = 1; # Compains: redeclaration of variable
  5.  
  6. my $bar = '12fred34';
  7. my $baz = $bar + 1; # Complains: Argument "12fred34" isn't numeric
  8. # Complains: Name "main::baz" used only once

See the strict documentation strict and warnings for futher information. For some horror stories about leaving out strict, see http://www.perlmonks.org/?node_id=482733.

Check the return of every open

It's amazing how often you'll see people complaining that a piece of code doesn't work:

  1. open( my $file, $filename );
  2. while ( <$file> ) {
  3. ...
  4. }

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:

  1. 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?

  1. Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.

Try running the code again with

  1. use diagnostics;

at the top of the program. Now the warning looks like this:

  1. Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm
  2. line 695 (#1)
  3. (W uninitialized) An undefined value was used as if it were already
  4. defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
  5. To suppress this warning assign a defined value to your variables.
  6.  
  7. To help you figure out what was undefined, perl tells you what operation
  8. you used the undefined value in. Note, however, that perl optimizes your
  9. program and the operation displayed in the warning may not necessarily
  10. appear literally in your program. For example, "that $foo" is
  11. usually optimized into "that " . $foo, and the warning will refer to
  12. the concatenation (.) operator, even though there is no . in your
  13. 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 -M command line switch:

  1. 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:

  1. 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:

  1. use Carp qw( confess );
  2. $SIG{__DIE__} = \&confess;
  3. $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.

  1. Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.
  2. at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695
  3. WWW::Mechanize::find_link('WWW::Mechanize=HASH(0x180e5bc)', 'n', 'undef') called at foo.pl line 17
  4. 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

  1. use Carp::Always;

or calling your program with -MCarp::Always on the command line will always give a stack trace.


Want to contribute?

Submit a PR to github.com/petdance/perl101