Title |
Debugging QNX4 Interrupt Handlers |
Ref. No. |
QNX.000009541 |
Category(ies) |
Utilities, Development |
Issue |
How to debug interrupt handler?
|
Solution |
Debugging an Interrupt Handler ==============================
In QNX4, interrupt handler code is called into by the kernel directly. This means that source level debugging (using wd to step through the code) is not possible for an interrupt handler. However, there are several approaches to debugging an interrupt handler in QNX4.
There are three standard approaches:
1. Use the Source Debugger 2. Use Trace() calls 3. Use the low-level Operating System Debbugger
1. Using the Source Debugger -------------------------
From the Source Debugger, values of global variables modified by the interrupt handler can be seen.
Here is a simple example of an interrupt handler:
volatile unsigned counter = 0; volatile unsigned it_count = 100;
/* The hardware interrupt handler */ #pragma off( check_stack ); pid_t far handler() { /* Kick a proxy every 'it_count' timer interrupts */ ++counter; if ( counter == it_count ) { counter = 0; return(proxy); } return(0); } #pragma on( check_stack );
'counter' is a global unsigned int that will be incremented each time the handler is run (and then reset to zero when it hits 100). If desired, display the value of 'counter' in a window, using:
DBG> Print/window counter
This variable window will display the current state of the 'counter' variable. However, it will NOT update each time the interrupt handler runs. The variable window will ONLY update when you step through the application program. This is because the interrupt handler code is not run from the application, but called from the kernel (within the application's context). The same information applies to watch points and break points on global variables updated by the interrupt handler.
2. Using Trace() calls -------------------
An easier approach to debugging an interrupt handler is to use Trace() calls.
Examine the following interrupt handler:
/* The hardware interrupt handler */ #pragma off( check_stack ); pid_t far handler() { unsigned char c;
c = inp(port); /* read in a byte from a port */ Trace1( _TRACE_TEMPORARY, _TRACE_TESTING, (int)c); return(0); } #pragma on( check_stack );
Every time the interrupt handler runs, a trace event will be logged with the data read from the port.
Before running the program, make sure that the following has been checked:
a) Proc's trace buffer is large enough to hold a large number of trace events.
Proc's trace buffer can be increased using the '-T' option to Proc32 in your build file (maximum is -T 63 which is a buffer of 63Kbytes). Any change to a system's build file will require a new boot image. The steps involved in changing the system's boot image can be found at:
http://support.qnx.com/support/bok/solution.qnx?9539
b) In the above example, a severity level of _TRACE_TESTING, or 7, is used. Normal trace logging will only save trace events of severity 5 and higher (0 is the highest). To change this, use the 'tracectrl' program.
->tracectrl -s 7
This must be done as superuser (root) and, it must be changed each time the computer is booted.
c) The trace events will be logged with a code of _TRACE_TEMPORARY which is 0xfffff (see /usr/include/sys/trace.h and /usr/include/sys/tracecod.h)
At this point, the interrupt handler program can be run. Let it run for a while and then stop it. To see the trace events in raw form, type 'traceinfo' and look for the trace events with a code of 0xfffff. 'traceinfo -M 0xfffff' can be used to see only the trace events that correspond to the trace code.
Millisecond timestamp resolution is available. Use the -m option on traceinfo.
To get even fancier, make a trace format string to match the trace events being generated. For example, make a file called 'tracetesting' that contains the lines:
#major 0xfffff /* Trace Temporary */ 0 Input from port is 0x%x
Then tell traceinfo to use this file for processing the trace event.
-> traceinfo -M 0xfffff -m -e tracetesting
The output will look something like:
Feb 15 11:48:53.189 7 fffff000 Input from port is 0x29
Using this approach can make very sophisticated logs of what is happening in the interrupt handler. See the documentation on traceinfo, tracectrl and tracelogger (as well as the Trace() functions in the Watcom docs) for more information.
3. Using the low-level Operating System Debugger ---------------------------------------------
The third approach is for those who want to do in-depth diagnostic work. It is possible to invoke the low-level Operating System Debugger from within the interrupt handler.
For example:
void ll_debug(void); #pragma aux ll_debug = "int 0f3h" parm [] modify exact nomemory [];
/* The hardware interrupt handler */ #pragma off( check_stack ); pid_t far handler() { unsigned char c;
ll_debug(); return(0); } #pragma on( check_stack );
The pragma ll_debug() is defined in /usr/include/sys/inline.h There are other useful pragmas in this file, as well.
Before running the handler program, make sure the Debugger is built into the boot image. Add the following two lines to the bottom of the build file.
sys/Debugger $ 1 Debugger
Instructions for building a new boot image can be found at:
http://support.qnx.com/support/bok/solution.qnx?9539
By default when booting an OS image that has the debugger built into it, the operating system will fall into the debugger 3 times when it boots. Press 'g' (for GO) at each of the debugger prompts to continue on.
To boot the image without dropping into the debugger, put a -D option on Proc32 in the build file.
sys/Proc32 $ Proc32 -l 1 -D
When booting with this new image, enter the low-level debugger at any time by hitting CTRL-ALT-ESC. To do this programmatically, issue an int f3h call (which is what the ll_debug pragma does).
*** IMPORTANT *** When in the low-level debugger, the entire QNX system is halted! No other processes will be running at this point.
See the section on 'Debugger' in the Utilities Reference for a list of available commands.
At this point, step through the assembly code of the interrupt handler, look at memory (using d), read in values from I/O ports (using i or I), etc. To continue on until the next interrupt occurs, type 'g' (for GO). This can be very handy for verifying the behaviour of hardware.
|
|