How can I debug/trace NSAPI applications?

NSAPI applications are run in separate processes on your zeus webserver, one process per virtual server:

bash$ ps -ef ¦ grep zeus root 24704 1 0 14:05:12 ? 0:02 zeus.admin root 24705 24704 0 14:05:12 ? 0:06 zeus.admin root 24708 1 0 14:05:13 ? 0:06 zeus.web root 24709 24708 0 14:05:13 ? 0:01 zeus.nsapi root 24710 24708 0 14:05:13 ? 0:00 zeus.web root 24711 24709 0 14:05:13 ? 0:01 zeus.nsapi test-1 root 24712 24709 0 14:05:13 ? 0:01 zeus.nsapi test-2
In this instance, process 24711 runs NSAPI applications on behalf of virtual server 'test-1'. Process 24709 (zeus.nsapi) is the 'master process' which creates and destroys the individual nsapi runners.

You can attach a system call tracer (truss, strace etc) to any of these processes. In particular, if an nsapi runner is prone to failure on startup, attach the tracer to the master process with 'follow fork' (often '-f') enabled (eg, 'strace -f -p 24709').

nsapisnoop

The nsapisnoop program in $ZEUSHOME/web/bin monitors the protocol between the webserver and the nsapi runner. Use as follows:

  1. Start 'nsapisnoop'
  2. Stop and start the virtual server you wish to examine (traffic lights!)
When the virtual server restarts, it will connect to the running 'nsapisnoop' and log the request-response protocol between the server and the NSAPI runner. From this information, you can derive what NSAPI applications are being run, what arguments they are being passed and what response they are returning.

In addition, 'nsapisnoop -d2' also dumps all function calls through the __nsapi30_table function table.

How can I attach a debugger to an NSAPI application?

When you next grab a web page that uses this function, the debugger will break in this function. You can step through the function, examining local variables etc.

If you want to break in a function that is called at Init, you need to be a bit more clever because the function is called just after the new zeus.nsapi process is forked and execed. Most debugger can't follow forks.

You can use a dummy Init function that blocks for long enough for you to attach a debugger to the new process. pause() and sleep() are good candidates.

/* sleep.c
 * Use as follows:
 *   Init fn="load-modules" funcs="mysleep" shlib="sleep.so"
 *   Init fn="mysleep" secs="20"
 */

#include <unistd.h>  \/* for sleep() */
#include <stdlib.h>  \/* for atoi()  */

char *pblock_findval( const char*, const void* );

int mysleep( void *args, void *sn, void *rq )
{
   const char *secs = pblock_findval( "secs", args );

   if( t ) sleep( atoi( secs ) );

   return 0;
}

Compile this up to a .so file, for example:
Using gcc:

gcc -g -c -fPIC sleep.c
gcc -shared -o sleep.so sleep.o
Using HP cc:
cc -Ae -g -c +z sleep.c
ld -b -o sleep.so sleep.o

Put the following in your obj.conf, before any other Init directives:

Init fn="load-modules" funcs="mysleep" shlib="sleep.so"
Init fn="mysleep" secs="20"

(shlib should include the full path to the sleep.so library)

Then when you start your virtual server, you have 20 seconds to figure out the pid and attach a debugger to it!