» Back to table of contents

debugging techniques

This chapter contains the following debugging techniques:

» using the jdb debugger
» sample jdb debugging session
» jdb quick reference
» debugging an already running process
» debugging Java™ applets
» debugging native methods

using the jdb debugger

The Java™ debugger, jdb, debugs byte-code in standalone Java™ applications and applets. It provides a command line interface. For best results, if you are using Java™ version 1.1.5.x, turn off the JIT compiler by specifying JIT_OPTIONS="-nojit" before invoking jdb. If you are using Java™ version 1.1.6 or later, you can leave JIT_OPTIONS on.

To debug a standalone Java™ program on HP-UX:
  1. Compile it with the javac -g (debug) option. For example:
    $ javac -g ArrayException.java
    
  2. Next, invoke the jdb debugger. For example:
    $ java -version
    java version "HP-UX Java™ C.01.15.05 08/06/98"
    $ export JIT_OPTIONS="-nojit"        Turn off the JIT compiler.
    $ jdb ArrayException                 Start jdb.
    > help                               List jdb commands.
    > exit                               Quit jdb.
    

sample jdb debugging session

In this example, the program to be debugged is called Average. This program finds the average of an array of 10 integers.

You can copy this program to a file, and compile it for debugging by using the -g option:
$ javac -g Average.java
Now run the program:
$ java Average
  4
  3

The results should actually be 3 and 2, not 4 and 3.

To get started, invoke the jdb debugger and load the class to be debugged:

$ jdb Average
Initializing jdb...
0xe84828:class(Average)
> stop in Average.main                 Set breakpoint in main.
Breakpoint set in Average.main
> run
run Average                            Start program.
running ...
Breakpoint hit: Average.main (Average:41)
main[1] main[1] list                   Show source code.
37                      int i;
38
39                   // Instantiate 2 objects of type Average.
40                      Average Avg1, Avg2;
41      =>              Avg1=new Average();
42                      Avg2=new Average();
43
44                   // Set start and end points in arrays for both objects.
45                      Avg1.low=FIRST;
main[1] list 60          Show source code.
56                      for (i=FIRST; i< ="LAST-3;" i++)="i++)" 
57="57" {="{" 58="58" Avg2.list[i]="my_list[i];" 59="59"}="}"
60=""> 61 // Use calculate_avg methods in both objects 62 // to find Average value of arrays 63 System.out.println(Avg1.calculate_avg()); /* Prints 4 */ 64 System.out.println(Avg2.calculate_avg()); /* Prints 3 */ main[1] stop at Average:63 Break at line 63. Breakpoint set at Average:63 main[1] cont Run. Breakpoint hit: Average.main (Average:63) main[1] dump Avg1 Display Avg1 object. Avg1 = (Average)0xe80c00 { int list[] = { 3, 4, 2, ... } int low = 0 int high = 9 } main[1] print Avg1.list[9] Display array element. Avg1.list[9] = 7 main[1] step Step into calculate_avg(). main[1] main[1] list 24 public int calculate_avg() 25 26 { 27 int total, num_elements, l_average; 28 => total = sum(list, low, high); 29 num_elements = high-low; //Note, this is an off-by-1-byte bug 30 l_average = total / num_elements; 31 return(l_average); 32 } main[1] stop at Average:31 Set breakpoint at end of method. Breakpoint set at Average:31 main[1] cont Run. Breakpoint hit: Average.calculate_avg (Average:31) main[1] main[1] locals Display local variables in method. Method arguments: this = Average@1d0180 Local variables: total = 36 num_elements = 9 Here's the problem; should be 10! l_average = 4 main[1] stop Show breakpoints. Current breakpoints set: Average:31 Average:63 Average:41 main[1] clear Average:31 Remove a breakpoint. Breakpoint cleared at Average: 31 . . . main[1] cont Finish running. main[1] 4 3 Average exited

debugging Java™ applets

To debug a Java™ applet:
  1. First, cd to the directory containing the HTML file for the Java™ applet. For example, on HP-UX:
    $ cd /opt/java/demo/TicTacToe
    
  2. Next, invoke the appletviewer program in debug mode:
    $ appletviewer -debug example1.html
    Initializing jdb...
    0x7a268148:class(sun.applet.AppletViewer)
    >
    

debugging an already running process

You can use the jdb debugger to attach to a Java™ applet or Java™ application running in a seperate process. If you launch a Java™ application using the -debug option, you connect to the virtual machine by using jdb in another console session or window. To launch a Java™ process to be debugged in this way:
  1. In one window, start the Java™ interpretter using java_g -debug. The Java™ interpretter will return a session-specific password for debugging. For example:

    $ export JIT_OPTIONS="-nojit"
    $ java_g -debug myclassfile 
    Agent password=5i8tp4
    

    The java_g command invokes a debug version of the Java™ virtual machine.

    The application to be debugged starts executing immediately. This program must pause for user input or another external event, in order for you to initiate the next step. Otherwise, the program may run to completion.

  2. To connect to the Java™ process started in step 1, open a second window and start jdb using the password from step 1:

    $ jdb -password 5i8tp4 
    . . initializing jdb... 
    

debugging native methods

Java™ main programs can call C or C++ routines and C or C++ main programs can call Java™ code. Methods implemented in a language other than Java™ are called native methods.

Calls between Java™ and C or C++ are made by using Java™ Native Interface (JNI).

To debug C or C++ native methods, use the HP Wildebeest Debugger (HP WDB). This debugger is an HP-supported implementation of the gdb debugger. HP DDE also supports debugging of native methods, but for best results, use HP WDB. Debugging Shared Library Code with HP WDB (gdb)

In this example, the Java™ program is named TestJavaCallingNative. This program calls a C native method named Java_TestJavaCallingNative_sayHelloWorld. To view the example source code, see /opt/java/docs/hpux/HPUX_JNI.html.

To debug native methods in shared libraries called by Java™ programs, follow these steps:
  1. Verify that you have HP WDB version 0.75.04 or later.
    $ gdb -version

    You can download HP WDB from HP for no charge.
  2. Compile your Java™ source files with the -g debug option. For example:
    $ javac -g TestJavaCallingNative.java
  3. Use javah to create native method header files. For example:
    $ javah -jni TestJavaCallingNative
  4. Compile your C or C++ code with the -g option. The following example additionally compiles for the kernel-threaded JVM:
    $ export JNI_INCLUDE="-I/opt/java/include
      -I/opt/java/include/hp-ux"
    $ cc -g -Ae +u4 +z -c $JNI_INCLUDE -DNATIVE
      -D_POSIX_C_SOURCE=199506L cImpl.c
    

    Or, compile for a user-threaded JVM:
    $ cc -g -Ae +ur +z -c $JNI_INCLUDE -D_HPUX cImpl.c
  5. Build the shared library for the C or C++ routines. For example:
    ld -b -o libcImpl.sl cImpl.o
  6. Create a shell script to load gdb. You can use the following script, called mygdb, as an example:
     
    #!/bin/ksh
    # This script is usually invoked as
    #   mygdb java <arguments to="to" java="java">
    #
    JAVA_ARGS=/tmp/java_args.$$
    EXECNAME=$1
    GDB=/opt/langtools/bin/gdb
    #
    # move past the VM name itself (1st argument
      = usually "java")
    shift  
    #
    # put the arguments to java in the java arguments
      file trap "rm -f $JAVA_ARGS"
      0 1 2 3 4 8 15
    #
    echo "set args" "$@"    > $JAVA_ARG
    #
    $GDB $GDBOPTS --command=$JAVA_ARGS $EXECNAME
    #
    
  7. Set the environment variable DEBUG_PROG to point to the location of your mygdb script.
    $ export DEBUG_PROG=/projects/working/mygdb
  8. Run your Java™ main program and begin debugging your C or C++ native code with gdb:
    $ chmod +x mygdb
    $ java JavaCallingNative cImpl
               :
               :
    (gdb) break Java_TestJavaCallingNative_initialize
    Breakpoint 1 (deferred) at
     "Java_TestJavaCallingNative_initialize"
    ("Java_TestJavaCallingNative_initialize"
    was not found). Breakpoint deferred until a shared
    library containing "Java_TestJavaCallingNative
    _initialize" is loaded.
    (gdb) run
    Starting program: /opt/java/bin/PA_RISC/
    green_threads/java TestJavaCallingNative cImpl
    Library cImpl successfully loaded
    initialize C++ runtime (nop for C)
    Breakpoint 1, Java_TestJavaCallingNative_initialize
    (env=0x7ad4a274, class=0x3) at cImpl.c:7
    7       }
    
Debugging Shared Library Code with HP DDE

HP DDE supports the debugging of native methods written in C and C++. To debug native methods in shared libraries called by Java™ programs, follow these steps:
  1. Compile your Java™ and C or C++ source files with the -g debug option and build your test application. (See steps 2-5 under "Debugging Shared Library Code with HP WDB (gdb).")
  2. Set the DEBUG_PROG environment variable to dde.
    $ export DEBUG_PROG="/opt/langtools/bin/dde"
    
  3. Run your Java™ application. For example:
    $ java TestJavaCallingNative cImpl
    

    The HP DDE main menu appears.
  4. In HP DDE, type init -alt in the Debugger Input window.
    dde> init -alt
    

    This loads all Java™ binary symbols.
  5. Type bre __map_dld (--use a double underscore before map--), followed by go in the Debugger Input window:
    dde> bre __map_dld
    dde> go
    Break at: \\__map_dld # `va(00003204)
    

    This stops the debugger after the dynamic loader, dld initializes events. The __map_dld symbol is defined in the crt0 program startup file.
  6. Type go -return in the Debugger Input Window.
    Stopped at: \\$START$ (0000306C)
    

    This causes the debugger to stop immediately after it loads shared libraries.
  7. Type prop lib nativemethodlib -add in the Debugger Input window. For example:
    dde> prop lib libcImpl.sl -add
    Loading symbol information for
     "/home/gene/JavaCallingNative/libcImpl.sl".
    

    This causes the debugger to open the shared library and make symbols available for setting breakpoints.
  8. Type int load, followed by go:
    dde> int load
    dde> go
    Intercepted event type "load ".
    Library loaded by process 9763:
     "/home/gene/JavaCallingNative/libcImpl.sl".
    

    This command generates a debug inteinterruptn a shared library is loaded. The debugger displays the shared library name when it finishes loading it.
  9. You can now begin setting breakpoints in your shared library code. For example:
    dde> bre Java_TestJavaCallingNative_sayHelloWorld
    dde> go
    Library cImpl successfully loaded
    

debugging tip

You can combine the above debugging commands, shown in steps 4-9, on one command line:
init -alt ;bre __map_dld -do [go];int load -do
    [bre Java_TestJavaCallingNative_sayHelloWorld ] ;
	prop lib libcImpl.sl -add;go 

jdb quick reference

To set a breakpoint:
stop in class.main
stop in class.method
stop at class:line #

To run the program:
run

To list source code:
list
list line #

To display variables:
locals
print variable
dump variable

To continue running:
cont

To step through program:
step   (to next line)
next   (step over calls)
step up (run until method returns to caller)
stepi (to next instruction)

To list breakpoints:
stop
clear

To clear a breakpoint:
clear class:line #

To list threads:
threads (shows application threads)
threadgroup system  
threads             (shows all threads)

To list methods in executing class:
methods

To show a stack trace:
where

» Please let us know additional information you'd like to see in the programmer's guide.

Java™ and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Hewlett-Packard is independent of Sun Microsystems, Inc.