Debugging Perl CGI Scripts

By: Jeff Boes

This month, we're going to dig deeply into my bag of tricks and learn how to debug a Perl CGI script. All the tips and techniques here are straightforward; most of them are things you probably know from your first course in programming-that is, if you had one! And that just may be your problem: if you are a self-taught Perl programmer, you may not have received any formal education in debugging.

So, let's set the stage. You've just written your magnum Perlus, a 1000-line masterpiece CGI script that takes orders, processes credit cards, tracks inventory, and calculates satellite orbits. You upload it to the server, and fire up your browser to put it through its paces. And you get something like what appears below:

500 Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator, webmaster@foo.com and inform them of the time the error occurred, and anything you might have done that may have caused the error.

More information about this error may be available in the server error log.

Um, oops. Now what?

An excellent resource for beginning (and even experienced) CGI programmers is The Idiot's Guide to Solving Perl CGI Problems. And don't be offended by the name: we're all idiots, at times. Let's try a few of their suggestions, and some others as we go, to see what's wrong.

Our first step: verify that the script is available for execution.

This last one needs some extra work. What I usually do is to keep a couple of scripts around for uploading onto brand-new servers as I encounter them (and as a freelancer, I'm encountering about one or two new ones per week&emdash;sometimes I'm the first person to attempt to run a Perl CGI script on that server!). The simplest is:
#!/usr/bin/perl -w
use diagnostics;
print 'Content-type: text/html

Hello, world.';
This will serve to let you know that Perl's working, and the web server is set up correctly to handle Perl.

You may want to take this a bit further though, and for that I can recommend nothing better than perldiver. It's a very useful script that you upload and run to get a comprehensive report on the server: where Perl is, which modules are installed, and a wealth of other information. (You should probably remove this script once you are finished checking your site, as it provides a lot of information that an unscrupulous user might use to break in.)

So, you've taken these steps, and you still can't figure out what's wrong? The next logical step is to verify that the script compiles cleanly. You should as a matter of course start ALL your perl scripts this way:

#!/usr/bin/perl -w
use diagnostics;
These two little lines will save you a heap of trouble, but you have to use them every time to ensure that they'll be there to help you when you need them. The first line is the standard signal to the operating system that the Perl interpreter gets the rest of this file as source code. But the addition of the "-w" switch turns on compile-time and run-time warnings. The second line turns on a "pragma" (a compile-time option, if you will) that causes all errors and warnings to be spelled out in newbie-level, painfully explicit detail. Such detailed warnings sometimes get in the way if you are debugging from the command line, as your output scrolls off the screen, chased by a hundred lines of diagnostic output. But in the CGI world, we'll put up with this, nay even welcome it, as you will soon see.

There's a third line that you may wish to add to your code.

use strict;
I hesitate to push this on anyone because I don't hold myself to this standard (which most Perl experts would say reflects poorly on me, and I have no defense for that). All I'll say is that you should try it, until such point as you understand what it does: then, make up your own mind. Basically, this pragma makes Perl more like other languages that require you to pre-declare variables. (There's much more to this, but for our purposes this will do.)

So, now you've added these lines, and perhaps run the code from the command line (if available) using

$ perl -c myscript.cgi
Perhaps now you'll see a few syntax errors, or other problems, that point you toward your problem. (The most common one I face here is lack of a particular optional library element, like Mail::Mailer.) If so, great: you have more information than you did when the dreaded "500" popped up on your browser.

But what if you don't have access to a Perl interpreter at your command prompt? (This is becoming less and less likely, as Perl is available for almost any commonly used platform, and differences between native versions are likely to be slight or esoteric. But perhaps you are developing on a Windows system, and haven't taken the plunge to put Perl on your Microsoft slavebox yet. Or perhaps you are quick-fixing a script using a borrowed computer, and you don't want to inflict a huge Perl installation for a few minutes work.

This is where we get into the meat of what I do for a living: debugging Perl scripts with nothing more than an FTP client and a browser. First, we have to make some alterations to your script:

use CGI qw(:standard);
use CGI::Carp qw(fatalsToBrowser);
BEGIN {
  print CGI::header();
  open (STDERR, '>myscript.log');
}
These lines, or ones very much like them, go into just about every CGI script I write. The first imports the standard CGI.pm package. Yes, it's a large package, and it can be a bit like navigating an aircraft carrier around the local marina. But it saves so much of your time, it's worth it. Unless your web server is running on a 486, the couple of extra microseconds you need to load this package will never be missed. And if your script is part of a site that's being hit like Yahoo!, you can still use CGI.pm or similar add-ons, but you just have to learn how to do it with mod_perl. (Which is out of scope for this column, and so we'll wave hands in the air and dismiss all this. Use CGI.pm, it's good for you. Trust me, I'm a professional.)

The second line imports another package called "CGI::Carp", which is an error handling package that replaces and extends the more frequently seen Carp. We grab the CGI family's Carp because we want nicely-formatted error messages, but even more we want the error-trapping facilities provided by the internal routine fatalsToBrowser. This routine will intercept control of our script in the event of a fatal error, and display that error (instead of the "500") in the browser. Think of it as a "fail-safe".

The third line signals a BEGIN block. This is a syntactic element of Perl programs that lets you insert code blocks anywhere in your program (say, in include files, or modules). These blocks only get executed once, as they are parsed (so, they are compile-time code). In what I've shown, the code will redirect any further output via STDERR to a log file. This is quite handy in the CGI environment, because many times your CGI program's errors are either suppressed, or written to a centralized site error log that is very large and may not be readable by you.

There's another thing slipped into the BEGIN block which is intended to save you grief. That's the print CGI::header statement. This forces a compliant HTTP header back to the browser before anything else can slip in there and spoil your day. If you aren't aware of it, CGI scripts must return a proper header at the top of their "output document" so the browser knows what to do with the output. The most common header looks like

Content-type: text/html

(including that empty line), and that's what we produce. If your script already produces a header, then you'll see an "extra" one appear as plain text at the top of your document. Ignore it; when you finish debugging, you can remove the print from the BEGIN block and go back to using the regular header.

Of course, if your script produces a header other than Content-type (say, a cookie, or a redirection), that header will be ignored, pushed down into the document by our BEGIN block header. Again, we'll remove the BEGIN's header at some point when we're convinced the bugs are all dead.

Now, with these lines in place, you have a very good start towards catching and even preventing those frustrating "500"s.



The author, an independent consultant and freelance CGI programmer, has been a professional programmer for 20 years, and a Perl programmer for 5 years. Once upon a time, but long before Perl was even a gleam in Larry's eye, he even did a little teaching at the college level, breaking in brand-new programmers. Learn more about him. Write him, if you have questions or comments about these articles.