Go to the previous, next section.

Debugging ILU Programs

This document describes some of the common errors that occur with the use of ILU, and some techniques for dealing with them.

Registration of interfaces

To use an interface with the Courier RPC protocol, a program number has to be specified for each class. This is done with the program courier-register-interface, which assigns every class in the interface a program number and records the number in a database. This database is searched by the runtime when a service or client attempts to export an instance of that class. If the class is not registered in the appropriate database, you will see the error message
_courier_FormProtocolHandle: Can't figure program#/version for class foo.bar.

courier-register-interface will clean out old assignments to the classes not now named in the interface, so you can run it as often as you like.

C++ static instance initialization

Our support for C++ currently depends on having the constructors for all static instances run before main() is called. If your compiler or interpreter doesn't support that, you will experience odd behavior. The C++ language does not strictly mandate that this initialization will be performed, but most compilers seem to arrange things that way. We'd like to see how many compilers do not; if your's doesn't, please send a note to [email protected] telling us what the compiler is.

ILU trace debugging

ILU contains a number of trace statements that allow you to observe the progress of certain operations within the ILU kernel. To enable these, you can set the environment variable ILU_DEBUG with the command setenv ILU_DEBUG "xxx:yyy:zzz:..." where xxx, yyy, and zzz are the names of various trace classes. The classes are (as of May 1994) packet, connection, incoming, export, authentication, object, sunrpc, courier, dcerpc, call, tcp, udp, xnsspp, gc, lock, and server. The special class ALL will enable all trace statements: setenv ILU_DEBUG ALL. The function ilu_SetDebugLevelViaString(char *trace_classes) may also be called from an application program or debugger, to enable tracing. The argument trace_classes should be formatted as described above.

ILU_DEBUG may also be set to an unsigned integer value, where each bit set in the binary version of the number corresponds to one of the above trace classes. For a list of the various bit values, see the file `ilu/include/iluDebug.h'. Again, you can also enable the tracing from a program or from a debugger, by calling the routine ilu_SetDebugLevel(unsigned long trace_bits) with an unsigned integer argument.

Use of islscan

The islscan program is supplied as part of the ILU release. It runs the ISL parser against a file containing an interface, and prints a "report" on the interface to standard output. It can therefor be used to check the syntax of an interface before running any language stubbers.

Bug Reporting and Comments

Report bugs (nah! -- couldn't be!) to the Internet address [email protected], or to the XNS address ILU-bugs:PARC:Xerox. Bug reports are more helpful with some information about the activity. General comments and suggestions can be sent to either [email protected] or ILU-bugs.

Use of gdb

When using ILU with C++ or C or even Common Lisp, running under the GNU debugger gdb can be helpful for finding segmentation violations and other system errors. ILU provides a debugging trace feature which can be set from gdb with the following command:

(gdb) p ilu_SetDebugLevel(0xXXX)
ilu_SetDebugLevel:  setting debug mask from 0x0 to 0xXXX
$1 = void
(gdb) 

The value XXX is an unsigned integer as discussed in section 3. The debugger dbx should also work.

Error handling

The ILU error kernel distinguishes between two classes of fatal errors, which are errors that have no pre-coded recovery code. The first type of fatal error is a failure to allocate heap-allocated memory in a case where the caller has indicated that memory must be allocated. The second type of fatal error is the violation of a kernel invariant. The application using ILU can specify one of three failure actions to be taken when either of these fatal errors is encountered:

  1. Explicitly trigger a SEGV signal by attempting to write to protected memory. This is useful for generating core dumps for later study of the error.
  2. Exit the program with an application-specified exit code.
  3. Enter an endless loop, which calls sleep(3) repeatedly. This option is useful for keeping the process alive but dormant, so that a debugger can attach to it and examine its "live" state. This is the default action.

An application can change the action taken on memory failures by calling ilu_SetMemFaultAction, and can change the action taken on invariant violations by calling ilu_SetAssertionFailureAction.

[ILU kernel]: void ilu_SetMemFaultAction ( int mfa )

Locking: unconstrained

Calling this tells the ILU kernel which drastic action is to be performed when ilu_must_malloc fails. -2 means to coredump; -1 means to loop forever in repeated calls to sleep(3); positive numbers mean to exit(mfa). The default is -1.

[ILU kernel]: void ilu_SetAssertionFailureAction ( int afa )

Locking: unconstrained

Calling this tells the ILU kernel which drastic action is to be performed when a kernel invariant assertion fails. -2 means to coredump; -1 means to loop forever in repeated calls to sleep(3); positive numbers mean to exit(afa). The default is -1.

Go to the previous, next section.