Debugging Java Programs on Linux with jdb

Allen Tucker
2/4/00

This tutorial provides an introduction to debugging Java programs on Linux machines using jdb, the standard Java debugger.  More detailed information can be found in the following documents:

  • The jdb man page
  • The javac man page
  • The java man page
  • The programs and data file discussed in this tutorial is in the Linux directory /home/allen/cs250/java/jdb.

    To illustrate debugging, we use a simple Java program called removeDuplicates, which reads integers from a file called 'numbers' and displays one instance of every different integer in the file.  For instance, if the file contains the integers

    2
    3
    4
    5
    6
    3
    4
    5
    6
    7
    then the program displays the following output:
    The unique integers in this series are:
    2
    3
    4
    5
    6
    7
    Here is a listing of the program, with line numbers added on the left:

    1  public class removeDuplicates {
    2   // problem 1.1 on page 21
    3   static int[] N = new int[1000];
    4   static int x, m;
    5
    6   public static boolean xinN() {
    7     boolean result = false;
    8     for (int i=0; i<m; i++)
    9       if (N[i]==x)
    10        result = true;
    11    return result;
    12  }
    13
    14  public static void main (String[] args) {
    15    pseudoKeyboard k = new pseudoKeyboard("numbers");
    16    m = 0;
    17    x = k.readInt();
    18    while (!k.eof()) {
    19      if (!xinN()) {
    20        N[m] = x;
    21        m++;
    22      }
    23      x = k.readInt();
    24    }
    25    System.out.println("The unique integers in this series are:");
    26    for (int i=0; i<m; i++)
    27      System.out.println(N[i]);
    28  }
    29 }

    This program uses an auxiliary class pseudoKeyboard for reading integers, but the details of that class are not necessary to this jdb illustration.

    1.  Compiling the source program and starting jdb

    To debug a program, it first must be recompiled with the -g option.  In this example we would write:
    [allen@lynx20 bozo]$ javac -g removeDuplicates.java
    [allen@lynx20 bozo]$ jdb removeDuplicates 
    Initializing jdb...
    0xb0:class(removeDuplicates)
    >
    Here, the second line starts the Java debugger with this program.  The last line gives the jdb prompt >, which will appear until you exit the debugger by typing exit.
     

    2.  Onscreen Listing of the jdb commands

    A summary of the jdb commands can be had by typing help at the prompt.

    > help
    ** command list **
    threads [threadgroup]     -- list threads
    thread <thread id>        -- set default thread
    suspend [thread id(s)]    -- suspend threads (default: all)
    resume [thread id(s)]     -- resume threads (default: all)
    where [thread id] | all   -- dump a thread's stack
    wherei [thread id] | all  -- dump a thread's stack, with pc info
    threadgroups              -- list threadgroups
    threadgroup <name>        -- set current threadgroup

    print <id> [id(s)]        -- print object or field
    dump <id> [id(s)]         -- print all object information

    locals                    -- print all local variables in current stack frame

    classes                   -- list currently known classes
    methods <class id>        -- list a class's methods

    stop in <class id>.<method>[(argument_type,...)] -- set a breakpoint in a methodstop at <class id>:<line> -- set a breakpoint at a line
    up [n frames]             -- move up a thread's stack
    down [n frames]           -- move down a thread's stack
    clear <class id>.<method>[(argument_type,...)]   -- clear a breakpoint in a method
    clear <class id>:<line>   -- clear a breakpoint at a line
    step                      -- execute current line
    step up                   -- execute until the current method returns to its caller
    stepi                     -- execute current instruction
    next                      -- step one line (step OVER calls)
    cont                      -- continue execution from breakpoint

    catch <class id>          -- break for the specified exception
    ignore <class id>         -- ignore when the specified exception

    list [line number|method] -- print source code
    use [source file path]    -- display or change the source path

    memory                    -- report memory usage
    gc                        -- free unused objects

    load classname            -- load Java class to be debugged
    run <class> [args]        -- start execution of a loaded Java class
    !!                        -- repeat last command
    help (or ?)               -- list commands
    exit (or quit)            -- exit debugger
    >

    3.  Setting a breakpoint

    Any line of the program can be flagged as a "breakpoint" where execution will stop whenever it reaches that line.  For example, if we want to stop the program removeDuplicates at line 23 every time it is reached, we would type:

    > stop at removeDuplicates:23
    Breakpoint set at removeDuplicates:23
    > run

    The last line begins execution of the program, which will go until the breakpoint is reached.  The following message will appear:

    > run
    run removeDuplicates ls
    running ...
    main[1]
    Breakpoint hit: removeDuplicates.main (removeDuplicates:23)
    main[1]
     

    4.  Displaying local variables

    At this point, you might want to display the values of local variables in an active class, which can be done using the dump command with the name of the class in question.  For instance,

    main[1] dump removeDuplicates
    removeDuplicates = 0xb0:class(removeDuplicates) {
        superclass = 0x2:class(java.lang.Object)
        loader = (sun.misc.Launcher$AppClassLoader)0xb1

        static int m = 1
        static int x = 2
        static int N[] = { 2, 0, 0, ... }

    main[1]

    gives the values of the variables m, x, and N just before the second number is read from the file "numbers".  To continue execution, type "cont" which will lead to another break at the next execution of line 23:

    main[1] cont
    main[1]
    Breakpoint hit: removeDuplicates.main (removeDuplicates:23)
    main[1] dump removeDuplicates
    removeDuplicates = 0xb0:class(removeDuplicates) {
        superclass = 0x2:class(java.lang.Object)
        loader = (sun.misc.Launcher$AppClassLoader)0xb1

        static int m = 2
        static int x = 3
        static int N[] = { 2, 3, 0, ... }

    main[1]

    This process can be repeated as long as desired, and for as many breakpoints as desired.  When execution is to be resumed without any breakpoints, they must be cleared before the "cont" instruction is issued.

    main[1] clear
    Current breakpoints set:
            removeDuplicates:23
    main[1] clear removeDuplicates:23
    Breakpoint cleared at removeDuplicates: 23
    main[1] cont
    The unique integers in this series are:main[1]
    2
    3
    4
    5
    6
    7

    Current thread "main" died. Execution continuing...
    >
    removeDuplicates exited
    [allen@lynx20 bozo]$

    Note here that the command "clear" without any arguments simply lists all the breakpoints that are currently set.  When program execution terminates, the output is displayed and the debugger exited, just as if a normal run of the program had taken place.

    5.  Additional Ideas

    During debugging, it's a good idea to open a second window, using emacs and the text of the program being debugged inside it.  This will give you easy access to the line numbers that form the basis for the debugging commands that you are using in the shell window.  The current line number of a source text is always displayed in the information bar at the bottom of the emacs window.

    Other jdb functions can be learned by experimentation, using the help command as a starting point.