Mod Perl Icon Mod Perl Icon Debugging mod_perl


[ Prev | Main Page | Next ]

Table of Contents:


The Writing Apache Modules with Perl and C book can be purchased online from O'Reilly and Amazon.com.
Your corrections of either technical or grammatical errors are very welcome. You are encouraged to help me to improve this guide. If you have something to contribute please send it directly to me.

[TOC]


Sometimes script works, sometimes does not

See Sometimes it Works Sometimes it does Not

[TOC]


Non-interactive debugging when running under mod_perl

To debug scripts running under mod_perl either use Apache::DB (interactive Perl debugging) or an older non-interactive method as described below.

NonStop debugger option enables us to get some decent debug info when running under mod_perl. For example, before starting the server:

  % setenv PERL5OPT -d
  % setenv PERLDB_OPTS "NonStop=1 LineInfo=db.out AutoTrace=1 frame=2"

Now watch db.out for line:filename info. This is most useful for tracking those core dumps that normally leave us guessing, even with a stack trace from gdb. db.out will show you what Perl code triggered the core. 'man perldebug' for more PERLDB_OPTS. Note, Perl will ignore PERL5OPT if PerlTaintCheck is On.

[TOC]


Apache::DB - Run the interactive Perl debugger under mod_perl

Perl ships with a very useful interactive debugger, however, it does not run ``out-of-the-box'' in the Apache/mod_perl environment. Apache::DB makes a few adjustments so the two will cooperate.

To configure it use:

  <Location /perl>
  PerlFixupHandler +Apache::DB
  
  SetHandler perl-script
  PerlHandler +Apache::Registry
  Options +ExecCGI
  </Location>

You must run the server in the single mode (with -X) to use Apache::DB.

When you run the script for the first time, you should let it run until it finishes. Starting from the second run you can run it as if it was a regular perl script.

Module and Scripts that were preloaded and compiled during the server startup will be not debuggable.

The filename and lines of Apache::Registry scripts are not displayed.

[TOC]


Debugging Core Dumps

See the SUPPORT doc for hints on getting a stacktrace. To do this with make test, build mod_perl giving Makefile.PL the PERL_DEBUG=1 flag. Now execute

  gdb ../apache_x.x.x/src/httpd 
  (gdb) thttpd

(thttpd is defined in .gdbinit)

then, in another term run make run_tests, and at the gdb prompt:

  (gdb) bt

[TOC]


Debug Tracing

To enable mod_perl debug tracing configure mod_perl with the PERL_TRACE option:

 perl Makefile.PL PERL_TRACE=1

The trace levels can then be enabled via the MOD_PERL_TRACE environment variable which can contain any combination of:

  d - Trace directive handling during configuration read
  s - Trace processing of perl sections
  h - Trace Perl*Handler callbacks
  g - Trace global variable handling, interpreter construction, END blocks, etc.
  all - all of the above

add to httpd.conf:

PerlSetVar MOD_PERL_TRACE all

For example if you want to see a trace of the PerlRequire's and PerlModule's as they are loaded, use:

PerlSetVar MOD_PERL_TRACE d

[TOC]


gdb says there are no debugging symbols

As you know you need an unstriped executable to be able to debug it. While you can compile the mod_perl with -g (or PERL_DEBUG=1) the apache install strips the symbols.

Makefile.tmpl contains a line:

  IFLAGS_PROGRAM  = -m 755 -s 

Removing the -s does the trick.

[TOC]


Monitoring error_log file

As I have mentioned it before, error_log file is your best friend in CGI code debugging process.

While debugging my mod_perl and general CGI code, I keep my error_log file open in a dedicated terminal window (xterm), so I can see what errors/warnings are being reported by server right away. I do it with:

  tail -f /usr/local/apache/logs/error_log

which shows all the lines that are being added to the file.

If you cannot access your error_log file because you are unable to telnet to your machine (generally a case with some ISPs who provides user CGI support but no telnet access), you might want to use a CGI script I wrote to fetch the latest lines from the file (with a bonus of colored output for an easier reading). You might need to ask your ISP to install this script for a general usage. See Watching the error_log file without telneting to the server.

[TOC]


Debugging Signal Handlers ($SIG{FOO})

Current perl implementation does not restore the original apache's C handler when you use local $SIG{FOO} clause. While save/restore of $SIG{ALRM} was fixed in the mod_perl 1.19_01 (CVS version), other signals are not yet fixed. The real fix should probably be in Perl itself.

Untill recent local $SIG{ALRM} restored the SIGALRM handler to Perl's handler, not the handler it was in the first place (apache's alrm_handler()). if you build mod_perl with PERL_TRACE=1 and set the MOD_PERL_TRACE environment variable to g, you will see this in the error_log file:

  mod_perl: saving SIGALRM (14) handler 0x80b1ff0
  mod_perl: restoring SIGALRM (14) handler from: 0x0 to: 0x80b1ff0

If nobody touched $SIG{ALRM}, 0x0 would be the same address as the others.

If you work with signal handlers take a look at Sys::Signal module, which solves the problem:

Sys::Signal - Set signal handlers with restoration of existing C sighandler. Get it from the CPAN.

The usage is simple, if the original code was:

  eval {
    local $SIG{ALRM} = sub { die "timeout\n" };
    alarm $timeout;
    ... db stuff ...
    alarm 0;
  };
   
  die $@ if $@;

If a timeout happens and SIGALRM is thrown, the alarm() will be reset, otherwise alarm 0 is reached and timer is being reset as well.

Now you would write:

  use Sys::Signal ();
  eval {
    my $h = Sys::Signal->set(ALRM => sub { die "timeout\n" });
    alarm $timeout;
    ... do something thay may timeout ...
      alarm 0;
  };
  die $@ if $@;

[TOC]


Spinning httpds

To see where an httpd is ``spinning'', try adding this to your script or a startup file:

  use Carp ();
  $SIG{'USR1'} = sub { 
     Carp::confess("caught SIGUSR1!");
  };

Then issue the command line:

  kill -USR1 <spinning_httpd_pid>

[TOC]


PROFILING

It is possible to profile code run under mod_perl with the Devel::DProf module available on CPAN. However, you must have apache version 1.3b3 or higher and the PerlChildExitHandler enabled. When the server is started, Devel::DProf installs an END block to write the tmon.out file, which will be run when the server is shutdown. Here's how to start and stop a server with the profiler enabled:

 % setenv PERL5OPT -d:DProf
 % httpd -X -d `pwd` &
 ... make some requests to the server here ...
 % kill `cat logs/httpd.pid`
 % unsetenv PERL5OPT
 % dprofpp

See also: Apache::DProf

[TOC]


examples of strace (or truss) usage

(META: below are some snippets of strace outputs from list's emails)

[there was a talk about Streaming LWP through mod_perl and the topic was suggested optimal buffer size]

Optimal buffer size depends on your system configuration, watch apache with strace -p (or truss) when its sending a static file, here perlfunc.pod on my laptop (linux 2.2.7):

  writev(4, [{"HTTP/1.1 200 OK\r\nDate: Wed, 02"..., 289}, {"=head1
  NAME\n\nperlfunc - Perl b"..., 32768}], 2) = 33057
  alarm(300)                              = 300
  write(4, "m.  In older versions of Perl, i"..., 32768) = 32768
  alarm(300)                              = 300
  write(4, "hout waiting for the user to hit"..., 32768) = 32768
  alarm(300)                              = 300
  write(4, ">&STDOUT") || die "Can't dup "..., 32768) = 32768
  alarm(300)                              = 300
  write(4, "LEHANDLE is supplied.  This has "..., 32768) = 32768
  alarm(300)                              = 300
  write(4, "ite>,\nC<seek>, C<tell>, or C<eo"..., 25657) = 25657

[TOC]


Devel::Peek

Devel::Peek - A data debugging tool for the XS programmer

Let's see an example of Perl allocating buffer size only once, regardless of my() scoping, although it will realloc if the size is > SvLEN:

  use Devel::Peek;
  
  for (1..3) {
      foo();
  }
  
  sub foo {
      my $sv;
      Dump $sv;
      $sv = 'x' x 100_000;
      $sv = "";
  }

The output:

  SV = NULL(0x0) at 0x8138008
    REFCNT = 1
    FLAGS = (PADBUSY,PADMY)
  SV = PV(0x80e5794) at 0x8138008
    REFCNT = 1
    FLAGS = (PADBUSY,PADMY)
    PV = 0x815f808 ""\0
    CUR = 0
    LEN = 100001
  SV = PV(0x80e5794) at 0x8138008
    REFCNT = 1
    FLAGS = (PADBUSY,PADMY)
    PV = 0x815f808 ""\0
    CUR = 0

We can see that on subsequent calls (after the first one) $sv already has a preallocated memory.

so, if you can afford the memory, the larger the buffer means less brk() syscalls. if you watch that example with strace, you will only see calls to brk() in the first time through the loop. So, this is a case where you module might want to pre-allocate the buffer for example for LWP, a file scope lexical, like so:

  package Your::Proxy;
  
  my $buffer = ' ' x 100_000;
  $buffer = "";

This way, only the parent has to brk() at server startup, each child already will already have an allocated buffer, just reset to ``'', when you are done.

[TOC]


The Writing Apache Modules with Perl and C book can be purchased online from O'Reilly and Amazon.com.
Your corrections of either technical or grammatical errors are very welcome. You are encouraged to help me to improve this guide. If you have something to contribute please send it directly to me.
[ Prev | Main Page | Next ]

Written by Stas Bekman.
Last Modified at 09/22/1999
Mod Perl Icon Use of the Camel for Perl is
a trademark of O'Reilly & Associates,
and is used by permission.