Home
Developer Resources
QNX RTOS v4
QNX RTOS v4 Knowledge Base

QNX RTOS v4 Knowledge Base

Foundry27
Foundry27
QNX RTOS v4 project
Resources

QNX RTOS v4 Knowledge Base

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.