Show Contents Previous Page Next Page
Chapter 2 - A First Module / Troubleshooting Modules Perl-Level Debugging In this section... Introduction Show Contents Go to Top Previous Page Next Page Ironically, debugging misbehaving Apache Perl modules is not as straightforward
as debugging C modules. This is because the current version of the Perl source-level
debugger can't work when the Perl interpreter is embedded in another program.
As of this writing, there is a pre-alpha version of a mod_perl -compatible
Perl debugger in the works; it could very well be available from the CPAN by
the time you read this. If you are using the Apache::Registry CGI emulation layer, then
one way to debug your module is to run and debug it as a standalone CGI script.
Further, if you use the CGI.pm module in your scripts, you can take advantage
of its ability to run CGI scripts from the command line and to seed the script
with test parameters. You can then walk through the script with the Perl debugger
(perl -d ), examine variables, and execute snippets
of Perl code to home in on what your program is doing wrong. Using Apache::FakeRequest for debugging Show Contents Go to Top Previous Page Next Page If you are using the full mod_perl API, or if the bug appears
when running under Apache but not when running as a standalone script, then
you may be able to track down the problem using Apache::FakeRequest ,
a tiny module that comes with the mod_perl distribution. Apache::FakeRequest
sets up an empty Apache request object that your module can use in lieu of a
real request object. Apache::FakeRequest methods don't do very
much: all they do is get and set internal variables of the same name as the
method. However, you can customize the fake request's methods to return test
data to your script. Example 2-5 shows how Apache::FakeRequest
can be used to debug the Apache::Hello module. This example shows
the code for a small wrapper script that invokes Apache::Hello 's
content handler, much as Apache invokes Apache::Hello::handler() when
a page request comes in. We begin by loading both Apache::FakeRequest
and Apache::Hello (after adjusting the library path so that Perl
can find the latter module). Next, we create a new fake request object. Apache::FakeRequest::new()
can be called with a series of name=value pairs. Each name corresponds to a
method that a real Apache request object would respond to. When your module
calls the method without any arguments, Apache::FakeRequest just
returns the value that you specified. Your module can also call the phony method
with an argument, in which case its value will be replaced. Methods that aren't
mentioned in new() will return undef. In our case, we only
care about feeding get_remote_host to Apache::Hello , so
we set that method to return foobar.com. Now it's simply a matter of calling our module's handler with the fake request
object. If you're using the Perl debugger, you can step into the module's code
and watch what it does. Should you want to customize Apache::FakeRequest 's behavior,
you can always subclass it and override one or more of its methods.
Example 2-5. This Apache::FakeRequest
Wrapper Can Be Used to Debug Apache::Hello
#!/usr/local/bin/perl
use lib '/usr/local/apache/lib/perl';
use Apache::FakeRequest ();
use Apache::Hello ();
my $request = Apache::FakeRequest->new('get_remote_host'=>'foobar.com'); Apache::Hello::handler($request);
Using Apache::Debug Show Contents Go to Top Previous Page Next Page Another useful debugging tool for Apache Perl modules is Apache::Debug .
This debugging facility is only available when you use Apache::Registry .
It's not of use to modules written to use the Apache Perl API directly. Apache::Debug defines a single subroutine named dump().
When dump() is called it sends copious debugging information to the
remote browser. Hopefully some of the information will help you figure out what's
going on. It's very simple to use Apache::Debug . Just add the command use
Apache::Debug() to the top of your module. Then, when you encounter an
unexpected error and want to print out current status information, add a line
like the following:
Apache::Debug::dump($r, SERVER_ERROR, "Can't find configuration file!");
The three arguments to dump() are the request object, an error code
to return to Apache (usually SERVER_ERROR), and an error message to print at
the top of the debugging output. Apache::Debug also allows you to activate some debugging messages
generated by Apache::Registry . This can sometimes help you track
down obscure problems that relate to the way that mod_perl loads
and executes your code. To increase the debugging level, add use Apache::Debug('level' => $level) to
the top of your module, where $level is a bit mask generated by
ORing together some combination of the following values: 1 | Makes a note in the error log whenever your module is recompiled | 2 | Calls Apache::Debug::dump() whenever your module
dies or an eval fails | 4 | Turns on verbose tracing |
Environment variables for debugging Show Contents Go to Top Previous Page Next Page A pair of environment variables control various aspects of the embedded Perl
interpreter's execution and can be used to help debug particularly obstinate
problems. -
MOD_PERL_TRACE When mod_perl is built with the PERL_TRACE option,
a special environment variable, MOD_PERL_TRACE , can be used to
enable debugging information. This variable should be set before the server
is started and should contain one or more of the letters described below for
tracing the various mod_perl features. The trace information
will be written to the server ErrorLog . For example:
% setenv MOD_PERL_TRACE dh
% ~www/bin/httpd -X
The first line sets MOD_PERL_TRACE to record trace information
during mod_perl directive handling (d ) and while executing
handlers (h ). The second line launches Apache in single process
mode. Here's the complete list of trace options:
c | Enables tracing during configuration directive handling | d | Enables tracing during mod_perl directive processing
during configuration read | s | Enables tracing during processing of <Perl> sections
| h | Enables tracing of Perl handlers during the processing of incoming
requests | g | Enables tracing of global variable handling, such as Perl interpreter
construction and execution of END blocks | all | Enables all of the options listed above |
-
PERL_DESTRUCT_LEVEL With Apache Versions 1.3 and higher, mod_perl will call the
perl_destruct() Perl API function during the child exit phase. This
will cause proper execution of END blocks found during server startup,
along with invoking the DESTROY method on global objects that are
still alive. It is possible that this operation may take a long time to finish,
causing problems during a restart. The symptom of this is a series of messages
that appear in the server log warning that certain child processes did not
exit as expected.
If you are experiencing this problem and your code does not contain any END
blocks or DESTROY methods that need to be run during child server shutdown,
you can avoid this problem by setting the PERL_DESTRUCT_LEVEL environment
variable to -1 :
PerlSetEnv PERL_DESTRUCT_LEVEL -1
Common Apache Perl module problems Show Contents Go to Top Previous Page Next Page Certain types of problems are common in Apache Perl modules. One common pattern
is that the code will seem to fail at random. The first time you fetch a page
generated by an Apache Perl module, it will work fine. The second time you fetch
it, it won't. If you reload repeatedly, it will sometimes work and sometimes
fail, seemingly haphazardly. This pattern is usually due to Apache's preforking
behavior. Multiple instances of your module are running, each one in a separate
process. In one or more of the processes, the module has crashed because some
unexpected sequence of inputs has led it to corrupt a data structure (or something
similar). In other processes, the module is still functioning (so far). You'll
never be able to figure out what's going on under these circumstances. Kill
httpd and relaunch it with the -X flag. With only one process
running, you can more easily figure out what inputs cause the module to misbehave.
Many Apache Perl module bugs are due to a wanton use of global variables.
The very first time the module is called, globals are initialized to their undefined
states in the way that conventional Perl scripts expect. However, in subsequent
calls the globals will contain information left over from previous invocations
of the script. This will cause scripts that depend on globals being initially
undefined to fail. Suspect this problem if your pages exhibit a pattern of progressive
decay in which they seem to work at first and then fail with increasing frequency.
Also be aware that certain actions that are second nature to Perl programmers,
such as calling die() or exit() to abort a script prematurely,
may not have quite the result you expect in the context of an Apache Perl module.
Under some circumstances a call to exit() within a module has been
known to make the server misbehave in strange ways. Use Apache::exit()
instead. die() should be reserved for truly unrecoverable errors. die()
generally causes the browser to display an "Internal Error" message. It's better
to replace die() with a procedure that displays a helpful error message
in the browser window and returns control to Apache. Several techniques for
doing this appear in the examples in subsequent chapters. The next chapter takes you on a tour through the innards of the Apache module
API. You'll learn everything you ever wanted to know about request records,
connection records, and transaction handlers. Footnotes 9 There are several directives related to managing
the number of servers on the farm; these include StartServers, MaxSpareServers,
MinSpareServers, and MaxClients. Show Contents Go to Top Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |