The use of an interpreter engine enables some unique approaches to the process of debugging and testing software. This section describes some of the tools and techniques for debugging an application.
Gamma can provide an active view-port into the running application. Another task (or shell) can, at any time, interact with a running Gamma program, without halting it or otherwise disturbing its real-time response. This provides an approach to debugging that is much more powerful than adding debug print statements.
For example, suppose that we started a process with the name "my_task" interactively:
Gamma> init_ipc ("my_task");
t
The gsend (for Gamma) or lsend (for Lisp) utility is used to interact with a running application from a shell:
[sh]$ gsend my_task
my_task>
The lsend utility accepts Lisp input as the default and gsend accepts Gamma input as its default.
Once connected to a running Gamma program using gsend/lsend, the developer can:
query/set variables/objects/instance_vars in the global scope
call functions/methods
re-define function definitions
run any Gamma command interactively
The syntax for starting the gsend utility is as follows:
gsend [-l] [-g] [program] [pid]
Accept Lisp input from the keyboard.
Accept Gamma input from the keyboard.
a Gamma program name, attached by name_attach, init_ipc, or qnx_name_attach
(QNX 4 only) a task ID
gsend and lsend attach to a running Gamma program and allow the user to send commands without exiting the event loop of the attached process. Any statement may be issued, including changing the definitions of existing functions. In our simple example we can call the princ function for my_task to execute:
[sh]$ gsend my_task
my_task> princ ("Hello!\n");
t
Notice that event processing stops for the duration of the command. Now let's look at my_task. In order to respond to requests from gsend/lsend, my_task must be executing an event loop. We can start one using the next_event function in a while statement:
Gamma> init_ipc ("my_task"); t Gamma> while(t) next_event(); Hello!
The my_task program continues to run as normal during this operation.
This presents an excellent opportunity for rapid development by programmers. Typically developers are used to the [quot ] code, compile, link, run, debug, code...[quot ] iterative approach to programming. Once a Gamma developer makes a program with a well written event loop, such as the one shown in the section below on trapping errors, programming and testing can become operations that happen in parallel.
Programmers will find that after a piece of code has been written, it can be uploaded to an already running Gamma process with gsend/lsend by using a simple [quot ]cut and paste[quot ]. The code is automatically assimilated into Gamma and ready to run. Better yet, if there is a problem with the code, the programmer receives immediate feedback and can track the problem down through an interactive debugging prompt that can be built right into the event-loop.
Gamma provides a pair of functions referred to as the try/catch mechanism, that is very important for debugging. Consider the following simple event loop:
while (t) { next_event(); }
This will run until the program exits or an event triggers some code that produces an error condition. There is no protection against errors. Now consider the following setup:
while (t) { try { next_event(); } catch { princ("error occurred\n"); } }
This setup of try/catch will try to evaluate the block of code contained in the [quot ]try[quot ] portion and jumps to the [quot ]catch[quot ] portion when an error occurs. A more effective example of the catch code block is:
while (t) { try { next_event(); } catch { princ("internal error: ", _last_error_, " calling stack is: ",stack(),"\n"); } }
This setup provides the developer with information about the last error and the calling stack which led to the last error. Tutorial II provides an example which illustrates the try/catch and protect/unwind mechanisms to get reports on an error.
Another setup that the developer may find useful is to automatically start an interactive session in the case of an error. The example of such a setting can be found in Tutorial II.
The stack function will show the current function calling stack, expressed as a list of functions that the interpreter is currently evaluating. To trace the execution path of parts of a program it is useful to print out the code as it is evaluated. The trace and notrace functions act as delimiters to areas when tracing should occur. The tracing information is delivered to standard output.
The following table of predefined global variables provides additional information useful for debugging:
Table 10.1. Global Variables in Gamma
Global Variable | Description |
---|---|
_error_stack_ | The stack at the time the last error occured. |
_unwind_stack_ | The stack at the time that an error was discovered. |
_last_error_ | A string containing the last error. |
Gamma permits the user to control the level of detail reported, and the format used, when an object is queried. This is done by defining a function named _ivar_filter with two arguments. For example, each class instance has a number of instance variables that are reported during interactive mode in the format:
Gamma> stats = qnx_osinfo(0);
{Osinfo (bootsrc . 72) (cpu . 586) (cpu_speed . 18883) (fpu . 587)
(freememk . 16328) (machine . "PCI") (max_nodes . 7) (nodename . 2)
(num_handlers . 64) (num_names . 100) (num_procs . 500)
(num_sessions . 64) (num_timers . 125) (pidmask . 511) (release . 71)
(reserve64k . 0) (sflags . 28675) (tick_size . 9999) (timesel . 177)
(totmemk . 32384) (version . 423)}
The following example provides a function named _ivar_filter that controls the output format. Note that each instance variable consists of a name and a value. If we define the following:
function _ivar_filter (!instance,!value) { princ(format("\n%-20s %-20s", string(car(value)), string(cdr(value)))); nil; }
then the output for Gamma in interactive mode now looks like:
Gamma> stats;
{Osinfo
bootsrc 72
cpu 586
cpu_speed 18883
fpu 587
freememk 15544
machine PCI
max_nodes 7
nodename 2
num_handlers 64
num_names 100
num_procs 500
num_sessions 64
num_timers 125
pidmask 511
release 71
reserve64k 0
sflags 28675
tick_size 9999
timesel 177
totmemk 32384
version 423
}
Copyright © 1995-2010 by Cogent Real-Time Systems, Inc. All rights reserved.