In a previous oreilly.com article, (Confessions
of an Accidental Programmer), I wrote about the phenomenon of accidental
programmers: people who come to programming from some other field, without
the benefit of formal instruction. Perl's popularity for Web-content
creation has led to an explosion in the number of accidental programmers.
As someone who followed that path, I'm sympathetic to the difficulties
beginners face in their efforts to become proficient with Perl. For someone
just starting out, even a simple "Hello, world" script can represent a real
challenge.
In my book,
Perl for Web Site
Management, which I wrote specifically for accidental programmers, I write
tongue-in-cheek of "The Joy of Debugging," and I warn novice programmers
that they're going to experience a lot of this particular joy. Whether you are
a beginning programmer or an expert, you're going to spend a great deal of time in the debugging phase: tracking down and squashing the silly mistakes that
keep your programs from running as you intended.
Sometimes the mistake will be obvious: you left off a semicolon or the
closing quotation mark in a quoted string. Other times the mistake will be
maddeningly obscure (at least until you identify it, at which point it,
too, will become obvious): you gave the wrong arguments to a function, or you
were confused about some aspect of Perl behavior.
Debugging is a specialized skill and it takes practice to become adept at
it. Debugging is somewhat like car repair; an experienced mechanic can ask a
few questions, listen to the engine for a second, and immediately tell you
what's wrong with your car and what it will take to fix it. Meanwhile, a
novice mechanic will pull apart the transmission when the real problem is a
broken light on the dashboard.
As you learn about programming you will frequently feel like that
novice mechanic, banging your head against the keyboard for what seems like
hours, only to discover the problem was actually in a completely
different part of your script than where you were looking. This is OK. It's
a good thing. It's how you learn.
With that said, I'd like to offer ten debugging tips gleaned from my own
experience that will help you learn more quickly.
Pay close attention to error messages.
Perl does its best to give you useful error messages. Read the message
carefully. It will usually report the line number in your script where the
problem occurred, so go to that line number and see if you can figure out
what's going on. (Be aware that the problem might be on an earlier line. This
is because you can make a mistake that looks like valid Perl code but which
doesn't trigger an error until later on in the script. Forgetting to properly
terminate quoted strings is a good example of this.)
Similarly, some mistakes will produce multiple error messages. In that
case, pay most attention to the first error message since the subsequent ones
may go away once the first problem is fixed.
Use the -w switch to turn on Perl's warnings feature.
By putting a space and -w at the end of the shebang line that
starts off your Perl script, you tell Perl to warn you about things it thinks
might be problems in your script. (With recent versions of Perl, 5.6.0 and
later, you can achieve the same thing by putting the line use
warnings near the top of the script.)
Use perl -c scriptname, or perl -wc scriptname,
(with scriptname replaced by the actual name of your script) to
do a compile check before running your script for real.
By entering the command perl -c scriptname or perl -wc
scriptname, you cause Perl to try to compile your script without actually
running it. The -c version compiles the script without invoking
the warnings feature; the -wc version compiles it with warnings
turned on. In either case, you can get feedback on any problems your script
might have, without having to actually run it.
Take a step back.
Vary your mental perspective. Try to consider the problem in a larger
context. Perhaps the problem is not in the chunk of code you think it is,
but in some untested assumption elsewhere in your script.
Try to isolate the problem.
By commenting out chunks of code, then rerunning the script, you can often
narrow down where the problem is occurring. Even better is to avoid the need
for this by building and debugging the script in small increments. Create a
simple framework first, get it working, then add increasingly complex features
on, testing each component before moving to the next. In this way you will
uncover bugs as you go, and it will usually be obvious where the bug
resides; it is in the small section of code you just added. While it may seem
faster to code up the whole thing first, then do all the
debugging at the end, it rarely works out that way.
Use print statements to test your assumptions.
By sprinkling temporary print statements in your script, you can often
track down where a problem is occurring. For example, if you think that at a
certain point a variable should contain one value, but the script is acting as
if it contains something else, add a statement to print out the contents of
the variable at that point and rerun the script. Similarly, you can use print
statements to indicate the flow of execution through the script: "I'm in the
if block now," "I'm looping through the foreach loop with a value of
$foo in my index variable," and so on.
Use the Data::Dumper module to get a snapshot of complex
data structures.
This is a slightly fancier version of the print-your-variable's-contents
suggestion from the previous tip. If your Perl programming has progressed
to the point where you are using multilevel data structures (like hashes of
hashes), you can use the standard Data::Dumper module's
Dumper function to print them out for debugging purposes. For
example, if you have a hash of hashes named %HoH in your script, you
can print it out with the following two lines:
use Data::Dumper;
print Dumper(\%HoH);
Use a test script to clarify confusing aspects of Perl
behavior.
As an alternative to the last several tips, which deal with altering your
main script to get a better picture of what's going on, you can often cut
to the heart of a debugging problem by writing a separate, stand-alone test
script. With a simplified test case, you can focus on one issue at a time.
This sort of divide-and-conquer approach is one of the keys to effective
debugging.
Use the strict pragma.
Perl is a great language for writing quick, one-off scripts, in part
because of its default behavior of having new variables simply spring into
existence on first mention. This can lead to problems as your script grows,
however. A typo in the name of a variable will mean that your script is
suddenly using a new, different variable from the one you intended, which can
be a real head-scratcher to debug. By putting the following line near the top
of the script
use strict;
you are telling Perl that you are willing
to be held to a higher standard. In particular, you're saying you are
willing to declare all your variables (typically, with a my
declaration) before using them. Besides protecting you from typos in your
variable names (because the script will abort with an error message
during the initial compilation phase if it encounters an undeclared
variable), this also lets you properly "scope" your variables, thereby
limiting their visibility, rather than letting them be "global" variables
that could conceivably interact with other variables of the same name
elsewhere in the script. All of this translates into big savings in debugging
time.
Resist the temptation to attribute the problem to some previously
undiscovered bug in Perl.
Every novice Perl programmer eventually comes up against a bug that defies
all efforts to identify and eradicate it. As the programmer's frustration
level mounts, an idea begins to creep into his or her head: it must not be
a problem in the script, but is something broken in Perl itself. I've thought
this at least a dozen times, and I was always wrong. Take my word on this
one: it's almost certainly a bug in your script, not in Perl.
One thing I haven't written about in this article is the Perl debugger
and I've done it for a reason: while useful, the debugger can be overwhelming
for a beginner. That's why I suggest you stick with things like embedded print
statements for the time being. Still, if you're curious, or if you have a
really thorny debugging problem and you think the debugger might help, see
the perldebug man page for more information. If your version of
Perl is recent enough (5.6.1 or later), the
perldebtut man page, which gives a beginner-friendly
introduction, will also be available. You invoke the debugger by running Perl
with the -d command-line switch by entering the following at the shell prompt:
[jbc@andros jbc] perl -d scriptname.plx
What this will do is put you in a special debugging mode,
where you can step through your program one statement at a time and do
interesting things like examine the current contents of variables.
That's it. Happy debugging!
John Callender is an independent consultant specializing in Web
development. He has been a teacher, a writer, an editor, and a network
administrator. In his spare time, he enjoys sailing, bird watching, and
learning more about computers.
O'Reilly & Associates will soon release (October 2001) Perl for Web Site
Management.
|