Trying to work out why your code has gone wrong is one of the most time consuming tasks that consume a programmer's daily grind. It's also the most frustrating.
There's nothing worse when you're trying to solve a problem than not understanding an error message that a script is spitting out. Perl has to draw the line somewhere between explaining what the error it's printing out really means and printing out so much info that you get swamped. But sometimes you find it's drawn the line on the wrong side of the sand.
diagnostics can help explain what's going on by printing out explanations of each of the error messages as they occur in a overly verbose fashion. These explanations are taken from perldiag, and have been carefully crafted to answer the 'why is it complaining about that' question that you'll often find yourself asking.
Diagnostics can be enabled at the start of your script in the same way that you might turn warnings and strict on:
#!/usr/bin/perl
# turn on perl's safety features use strict; use warnings;
# turn on extra debug info use diagnostics;
# do something naughty ($foo not declared yet) $foo = "bar";
This prints out a fully descriptive lump of text that's explaining what's exactly going on:
Global symbol "$foo" requires explicit package name at - line 11. Execution of - aborted due to compilation errors (#1) (F) You've said "use strict vars", which indicates that all variables must either be lexically scoped (using "my"), declared beforehand using "our", or explicitly qualified to say which package the global variable is in (using "::").
Horay! I now understand. These error messages are extracted from the handy 'what does that mean' page that is the
diagnostics prints out useful information for warnings too
#!/usr/bin/perl
# turn on perl's safety features use strict; use warnings;
# turn on extra debug info use diagnostics;
# do something naughty ($foo undef) my $foo; my $bar = $foo . $foo;
Which prints:
Use of uninitialized value in concatenation (.) or string at - line 12 (#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.
This is fine, until you get about a million of these in your program. Since warnings aren't fatal you can quickly amass screenfuls and screenfuls of text One solution is to turn warnings on for just a little bit of your code:
#!/usr/bin/perl
# turn on perl's safety features use strict; use warnings;
# turn on extra debug info use diagnostics; disable diagnostics; # do something naughty ($foo undef) my $foo; my $bar = $foo . $foo; # no diagnostics
enable diagnostics; $bar .= gmtime . "fred"; # prints out warning
This prints out the long and helpful warning:
Warning: Use of "gmtime" without parentheses is ambiguous at - line 16 (#1) (S ambiguous) You wrote a unary operator followed by something that looks like a binary operator that could also have been interpreted as a term or unary operator. For instance, if you know that the rand function has a default argument of 1.0, and you write rand + 5; you may THINK you wrote the same thing as rand() + 5; but in actual fact, you got rand(+5); So put in parentheses to say what you really mean.
But we didn't get any diagnostics about the concatenation. Nice.
Another possibility, one I use a lot, is have my program grind to a halt as soon as it sees a warning. This can be done by installing a signal handler that dies when it finds a problem:
use diagnostics; BEGIN { $SIG{'__WARN__'} = sub { die $_[0] }; }
splain is a little program that allows you to create diagnostics for code that's already been executed. It works by taking the error output from our code's and reprocessing it into more useful output. Let's remove diagnostics from our previous example:
#!/usr/bin/perl
# turn on perl's safety features use strict; use warnings; # do somethings naughty my $foo; my $bar = $foo . $foo; $bar .= gmtime . "fred";
And capture the warnings to a file (This is the syntax for bash, you'll have to work out how to redirect STDERR on your system yourself.)
bash-2.05b$ perl script 2>mystderr
And now at any point in the future I can run splain over that output and it'll print out handy debug messages for me:
bash-2.05b$ splain mystderr
Which spits out the same errors as above.