home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright © 1999 by O'Reilly & Associates, Inc.


 


   Show Contents   Previous Page   Next Page

Chapter 2 - A First Module / Troubleshooting Modules
Perl-Level Debugging

In this section...

Introduction
Using Apache::FakeRequest for debugging
Using Apache::Debug
Environment variables for debugging
Common Apache Perl module problems

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