DXREADME 9.96 KB

DX DEBUG PACKAGE for IDL
MARKWARDT IDL PROGRAMS

Craig Markwardt
craigm@cow.physics.wisc.edu
17 Apr 2000

The following instructions apply to the DXDEBUG package of procedures
for command line debugging under IDL, available from the Markwardt IDL
Library.

The procedures in the package supply convenience routines for
navigating up and down the IDL call stack (DXUP and DXDOWN); for
interogating and modifying IDL variables at any levels in the call
stack (DXHELP, DXPRINT, DXGET, DXSET); and for quickly setting and
clearing breakpoints (DXBREAK, DXFINISH and DXCLEAR).

These routines are really only meant to be useful when actively
debugging a stopped program -- they are probably *not* acceptable to
be called as library routines from other procedures.


DOWNLOADING

Download new versions of from Craig Markwardt's web page:

http://cow.physics.wisc.edu/~craigm/idl/idl.html

Program modification dates appear on the web page, which you can
compare agains your own copy.  You can also check the modification
history of the file itself to see how recent it is.

Please see the file INSTALL for installation instructions.


MANIFEST

DXREADME - this file - package description and instructions
INSTALL - general installation instructions

DXUP - navigate up the IDL call stack (toward outermost level)
DXDOWN - navigate down the IDL call stack (toward innermost level)

DXGET - function to retrieve variable value at any call level
DXSET - set a variable value at any call level
DXPRINT - equivalent of PRINT for any call level
DXHELP - equivalent of HELP for any call level

DXBREAK - set breakpoints more conveniently than BREAKPOINT
DXFINISH - set breakpoint at return of current procedure
DXCLEAR - clear breakpoints

DXLRESET - internal routine
DXPLEVEL - internal routine
DXPTRACE - internal reoutine


INTRODUCTION

Command line debugging with IDL has always been difficult.  It does
provide excellent abilities to interactively query and set variables,
but only at one level -- the deepest level.  Generally, when debugging
a larger suite of programs one can have procedures and functions that
nest several levels deep.  Since one procedure can call another, or
even itself, this is a natural effect.  If an error occurs in a
procedure several levels deep, knowing the values of variables at
higher levels (i.e., the calling routines) can be useful and time
saving.  Unfortunately IDL has no documented provisions for doing
this.

The DXDEBUG package provides several convenience routines to do
examine variables at any call level.  The package is based upon
invaluable discussions about the undocumented ROUTINE_NAMES() function
on the comp.lang.idl-pvwave Usenet newsgroup.

DXDEBUG also has convenience routines which aid in setting
breakpoints.  I have always found it difficult to deal with
breakpoints for because:

  * it is cumbersome to type long commands;
  * it is difficult to remember or type full path to procedure name;
  * there is no easy way to break when the current procedure finishes.

I have tried to remedy this with the DXBREAK and DXFINISH procedures.


THE IDL CALL STACK

When your program crashes, and you type HELP, you get a report similar
to the following:

% At  XTE_OPEN           15 /home/craigm/lib/idl/xte/xte_open.pro
%     XSORTINPUT         76 /home/craigm/lib/idl/idlextract/xsortinput.pro
%     BINLC             299 /home/craigm/lib/idl/idlextract/binlc.pro

This output indicates that you are currently stopped in a program
which is three "levels" deep.  That is, the BINLC procedure has called
XSORTINPUT, which in turn has called XTE_OPEN.  In fact the stopped
program is *four* levels deep, if you consider that the $MAIN$ level
as another level (which you should).

This set of levels is the IDL *call stack*.  It describes from where
each procedure has called the next.  The *deepest* level is considered
to be innermost.  When IDL crashes or stops, it is always at the
deepest level.  The *upper- or outermost* level is the main level that
you started at.  Now let's try the DXHELP command to see the call
stack again:

IDL> dxhelp
     1 $MAIN$             0                                                    
     2 BINLC            299 /home/craigm/lib/idl/idlextract/binlc.pro          
     3 XSORTINPUT        76 /home/craigm/lib/idl/idlextract/xsortinput.pro     
 >>  4 XTE_OPEN          15 /home/craigm/lib/idl/xte/xte_open.pro           << 

This is essentially the same information as produced by HELP, but the
one important difference is the highlighting of the deepest level with
angled brackets (>> <<).  While IDL itself always stays at the deepest
level, you can use DXUP and DXDOWN to move up and down the stack and
examine variables.


MOVING UP and DOWN THE STACK

By typing DXUP, we can move one level up the call stack, like this:

IDL> dxup
 >>  3 XSORTINPUT        76 /home/craigm/lib/idl/idlextract/xsortinput.pro  << 

DXUP reports that we have moved up to level 3, XSORTINPUT.  If we want
to see the entire call stack we can always type DXHELP again.

*What* exactly has moved up?  IDL itself always stays at the lowest
level, but all of the DXDEBUG commands maintain a separate notion of
what level you are currently examining -- which can be any level in
the call stack.  This level is referred to the "debugging focus"
level.

As you might suspect, DXDOWN will move us down one level.  In this
case it would return us to the lowest XTE_OPEN level we started at.
Let's not do that right now.  It should also be obvious that you
cannot go any deeper than the deepest level, or any higher than the
uppermost $MAIN$ level.  You will be warned if you try.


EXAMINING VARIABLES

Now that we have moved the debugging focus to the level of XSORTINPUT,
we can try looking at the variables there.  Periscope up!

You can use DXHELP and DXPRINT like the standard HELP and PRINT
commands.

IDL> dxhelp, sz
 >>  3 XSORTINPUT        76 /home/craigm/lib/idl/idlextract/xsortinput.pro  << 
SZ              LONG      = Array[4]

DXHELP always prints at least the current level to prevent confusion.
In this case it also shows that SZ is a 4-element long integer array.
DXPRINT actually prints the values:

IDL> dxprint, sz
 >>  3 XSORTINPUT        76 /home/craigm/lib/idl/idlextract/xsortinput.pro  << 
           1           1           7           1

You can use the standard FORMAT keywords if you need to.


CHANGING VARIABLES

Sometimes you may need to actually change the value of a variable.
Two routines, DXGET and DXSET will help you do this.  If you first
need to retrieve the value of a variable, use DXGET.

IDL> x = dxget('sz')
IDL> print, x
           1           1           7           1

In this case we have retrieved the value of SZ from the current
debugging focus level (level 3, XSORTINPUT), and put the value in the
variable X.  Notice that it is not required to type quotes around the
variable name, but it is good practice to do so.

After setting any new values, use DXSET to actually alter the upper
level value:

IDL> x(2) = 8
IDL> dxset, 'sz', x

Now the new value will be established.

One note: you have to DXGET an entire variable.  You will not be able
to retrieve subscripted arrays or structure tags.


SETTING BREAKPOINTS

Until now, there was one thing I never did when debugging a program:
set breakpoints.  The BREAKPOINT command just never seemed convenient.
Why?  Well, you just couldn't avoid typing a lot of letters.  The
BREAKPOINT command itself is pretty long, and then you have to type
either /SET or /CLEAR.  The worst offender, however, was having to
type the complete path to the procedure being debugged.  In the era of
multi-gigabyte hard drives, that path was often a pretty long one to
type.  So I just never got excited about breakpoints and instead put
in manual debugging statements.

I am trying to change, and I hope you will too.

The DXBREAK command has fewer characters to type, and *most*
importantly, DXBREAK does not required to type the complete path name.
If you don't type the full path, DXBREAK will automatically scan your
IDL_PATH to find the right file.  In fact, if you only enter a line
number, then DXBREAK will automatically set the breakpoint in the
current file!

Remember, breakpoints can only be set in procedures or files that are
compiled from disk.  This means that RESTORE'd procedures, or
procedures compiled from the console, cannot have breakpoints.

As an example, try:

IDL> dxbreak, 'xsortinput', 70
Breakpoint set at: XSORTINPUT    70  (/home/craigm/lib/idl/idlextract/xsortinput.pro)

This sets a breakpoint at line 70 in the xsortinput.pro file.  As you
can see, the full path to the file was discovered automatically.  To
confirm that the breakpoint was set, we can do the standard HELP
command:

IDL> help, /break
Breakpoints:
  Index  Module           Line  File
  -----  ------           ----  ----
      0  XSORTINPUT         70  /home/craigm/lib/idl/idlextract/xsortinput.pro

Clear breakpoints with BREAKPOINT, /CLEAR or DXCLEAR.

The other thing I found wanting in debugging was the ability to let
the current procedure finish, BUT THEN STOP.  Often the true bug is in
the calling procedure, so we want to let the current deepest level
finish so we can do a more detailed analysis one level up.  

DXFINISH is the answer to these desires.  Just type it by itself, and
it will automatically set a breakpoint in the procedure or function
one level up, at the next line of execution.  Do it like this:

IDL> dxfinish
Breakpoint set at: XSORTINPUT    77

Then we simply continue and IDL will stop when the breakpoint is hit:

IDL> .continue
% Breakpoint at:  XSORTINPUT         77 /home/craigm/lib/idl/idlextract/xsortin

Note that by default DXFINISH uses the /ONCE keyword to set these
kinds of breakpoints.  This means that the breakpoint will be cleared
after it has been hit once.  This behavior is usually what is
intended, but it can be overridden by using the ONCE=0 keyword to
DXFINISH.


CONCLUSION

I hope that this package will help you debug smarter and faster.  I've
just begun to use them myself, so there may still be some bugs.
Please let me know if you have any to report, or any other comments.
Thanks.