Now we'll look at how the interactive debugger is used in a mod_perl environment. The Apache::DB module available from CPAN provides a wrapper around perldb for debugging Perl code running under mod_perl.

The server must be run in non-forking (single-process) mode to use the interactive debugger; this mode is turned on by passing the -X flag to the httpd executable. It is convenient to use an IfDefinesection around the Apache::DB configuration; the example below does this using the name PERLDB. With this setup, debugging is turned on only when starting the server with the httpd -X -DPERLDB command.

This configuration section should be placed before any other Perl code is pulled in, so that debugging symbols will be inserted into the syntax tree, triggered by the call to Apache::DB->init. The Apache::DB::handler can be configured using any of the Perl*Handler directives. In this case we use a PerlFixupHandler so handlers in the response phase will bring up the debugger prompt:

<IfDefine PERLDB>

        use Apache::DB ( );

    <Location />
        PerlFixupHandler Apache::DB


Since we have used "/" as the argument to the Location directive, the debugger will be invoked for any kind of request, but of course it will immediately quit unless there is some Perl module registered to handle these requests.

In our first example, we will debug the standard Apache::Status module, which is configured like this:

PerlModule Apache::Status
<Location /perl-status>
    SetHandler perl-script
    PerlHandler Apache::Status

When the server is started with the debugging flag, a notice will be printed to the console:

panic% ./httpd -X -DPERLDB
[notice] Apache::DB initialized in child 950

The debugger prompt will not be available until the first request is made (in our case, to http://localhost/perl-status). Once we are at the prompt, all the standard debugging commands are available. First we run window to get some of the context for the code being debugged, then we move to the next statement after a value has been assigned to $r, and finally we print the request URI. If no breakpoints are set, the continue command will give control back to Apache and the request will finish with the Apache::Status main menu showing in the browser window:

Loading DB routines from version 1.07
Emacs support available.

Enter h or `h h' for help.

55:         my($r) = @_;
  DB<1> w
52      }
54      sub handler {
55=  =>       my($r) = @_;
56:         Apache->request($r); #for Apache::CGI
57:         my $qs = $r->args || "";
58:         my $sub = "status_$qs";
59:         no strict 'refs';
61:         if($qs =~ s/^(noh_\w+).*/$1/) {
  DB<1> n
56:         Apache->request($r); #  for Apache::CGI
  DB<1> p $r->uri
  DB<2> c

All the techniques we saw while debugging plain Perl scripts can be applied to this debugging session.

Debugging Apache::Registry scripts is somewhat different, because the handler routine does quite a bit of work before it reaches your script. In this example, we make a request for /perl/, which consists of the code shown in Example 21-8.

Example 21-8.

use strict;

my $r = shift;

print "mod_perl rules";

When a request is issued, the debugger stops at line 28 of Apache/ We set a breakpoint at line 140, which is the line that actually calls the script wrapper subroutine. The continue command will bring us to that line, where we can step into the script handler:

28:       my $r = shift;
  DB<1> b 140
  DB<2> c
140:            eval { &{$cv}($r, @_) } if $r->seqno;
  DB<2> s
Apache::ROOT::perl::test_2epl::handler((eval 87):3):
3:        my $r = shift;

Notice the funny package name—it's generated from the URI of the request, for namespace protection. The filename is not displayed, since the code was compiled via eval( ), but the print command can be used to show you $r->filename:

  DB<2> n
Apache::ROOT::perl::test_2epl::handler((eval 87):4):
4:        $r->send_http_header('text/plain');
  DB<2> p $r->filename

The line number might seem off too, but the window command will give you a better idea of where you are:

  DB<4> w
1:      package Apache::ROOT::perl::test_2epl;use Apache qw(exit);
sub handler {  use strict;
3:        my $r = shift;
4=  =>      $r->send_http_header('text/plain');
6:        print "mod_perl rules";
8       }
9       ;

The code from the file is between lines 2 and 7. The rest is the Apache::Registry magic to cache your code inside a handlersubroutine.

It will always take some practice and patience when putting together debugging strategies that make effective use of the interactive debugger for various situations. Once you have a good strategy, bug squashing can actually be quite a bit of fun!