jdb Debugger

jdb, the Java debugger, is a sophisticated tool that helps you find and fix bugs in Java programs. You can also use it to understand better what is taking place behind the scenes in the Java interpreter as a program is running. It has a large number of features, including some that might be beyond the expertise of a Java programmer who is new to the language. You don't need to use the debugger to debug Java programs. This is fairly obvious, especially if you've been creating your own Java programs as you read this tutorial. After the Java compiler generates an error, the most common response is to load the source code into an editor, find the line cited in the error message, and try to spot the problem. This dreaded compile-curse-find-fix cycle is repeated until the program compiles without complaint. After using this debugging method for a while, you might think that the debugger isn't necessary to the coding process because it's such a complicated tool to master. This reasoning makes sense when you're fixing problems that cause compiler errors. Many of these problems are simple things such as a misplaced semicolon, unmatched { and } brackets, or the use of the wrong type of data as a method argument. However, when you start looking for logic errors—more subtle bugs that don't stop the program from compiling and running—a debugger is an invaluable tool. The Java debugger has two features that are useful when you're searching for a bug that can't be found by other means: single-step execution and breakpoints. Single-step execution pauses a Java program after every line of code is executed. Breakpoints are points where execution of the program will pause. Using the Java debugger, these breakpoints can be triggered by specific lines of code, method calls, or caught exceptions. The Java debugger works by running a program using a version of the Java interpreter that it has complete control over. Before you use the Java debugger with a program, you will compile the program with the -g option, which causes extra information to be included in the class file. This information greatly aids in debugging. Also, you shouldn't use the -O option because its optimization techniques might produce a class file that does not directly correspond with the program's source code.

Debugging apps

If you're debugging an app, the jdb tool can be run with a Java class as an argument. This is shown in the following:

jdb WriteBytes

This example runs the debugger with WriteBytes.class, an app that's available from the tutorial's website at http://www.java24hours.com. Visit the site, select the Appendix B page, and then save the files WriteBytes.class and WriteBytes.java in the same folder that you run the debugger from. The WriteBytes app writes a series of bytes to disk to produce the file pic.gif. The debugger loads this program but does not begin running it, displaying the following output:

Initializing jdb...

The debugger is controlled by typing commands at the > prompt. To set a breakpoint in a program, the stop in or stop at commands are used. The stop in command sets a breakpoint at the first line of a specific method in a class. You specify the class and method name as an argument to the command, as in the following hypothetical example:

stop in SellItem.SetPrice

This command sets a breakpoint at the first line of the SetPrice method. Note that no arguments or parentheses are needed after the method name. The stop at command sets a breakpoint at a specific line number within a class. You specify the class and number as an argument to the command, as in the following example:

stop at WriteBytes:14

If you're trying this with the WriteBytes class, you'll see the following output after entering this command:

Deferring breakpoint WriteBytes:14
It will be set after the class is loaded.

You can set as many breakpoints as desired within a class. To see the breakpoints that are currently set, use the clear command without any arguments. The clear command lists all current breakpoints by line number rather than method name, even if they were set using the stop in command. By using clear with a class name and line number as an argument, you can remove a breakpoint. If the hypothetical SellItem.SetPrice method was located at line 215 of SellItem, you can clear this breakpoint with the following command:

clear SellItem:215

Within the debugger, you can begin executing a program with the run command. The following output shows what the debugger displays after you begin running the WriteBytes class:

run WriteBytes VM Started: Set deferred breakpoint WriteBytes:14
Breakpoint hit: "thread=main", WriteBytes.main(), line=14 bci=413
14 for (int i = 0; i < data.length; i++)

After you have reached a breakpoint in the WriteBytes class, experiment with the following commands:

  • list— At the point where execution stopped, this command displays the source code of the line and several lines around it. This requires access to the .java file of the class where the breakpoint has been hit, so that you must have WriteBytes.java in either the current folder or one of the folders in your CLASSPATH.
  • locals— This command lists the values for local variables that are currently in use or will soon be defined.
  • print text— This command displays the value of the variable, object, or array element specified by text.
  • step— This command executes the next line and stops again.
  • cont— This command continues running the program at the point it was halted.
  • !!— This command repeats the previous debugger command.

After trying out these commands within the app, you can resume running the program by clearing the breakpoint and using the cont command. Use the exit command to end the debugging session. The WriteBytes app creates a file called pic.gif. You can verify that this file ran successfully by loading it with a web browser or image-editing software. You'll see a small letter J in black and white. After you have finished debugging a program and you're satisfied that it works correctly, recompile it without the -g option.

Debugging Applets

You can't debug an applet by loading it using the jdb tool. Instead, use the -debug option of the appletviewer, as in the following example:

appletviewer -debug AppInfo.html

This will load the Java debugger, and when you use a command such as run, the appletviewer will begin running also. Try out this example to see how these tools interact with each other. Before you use the run command to execute the applet, set a breakpoint in the program at the first line of the getAppletInfo method. Use the following command:

stop in AppInfo.getAppletInfo

After you begin running the applet, the breakpoint won't be hit until you cause the getAppletInfo() method to be called. This is accomplished by selecting Applet, Info from the appletviewer's menu.

Advanced Debugging Commands

With the features you have learned about so far, you can use the debugger to stop execution of a program and learn more about what's taking place. This might be sufficient for many of your debugging tasks, but the debugger also offers many other commands. These include the following:

  • up— Moves up the stack frame so you can use locals and print to examine the program at the point before the current method was called.
  • down— Moves down the stack frame to examine the program after the method call.

In a Java program, often there are places where a chain of methods is called. One method calls another method, which calls another method, and so on. At each point where a method is being called, Java keeps track of all the objects and variables within that scope by grouping them together. This grouping is called a stack, as if you were stacking these objects as you would a deck of cards. The various stacks in existence as a program runs are called the stack frame. By using up and down along with commands such as locals, you can better understand how the code that calls a method interacts with that method. You can also use the following commands within a debugging session:

  • classes— Lists the classes currently loaded into memory.
  • methods— Lists the methods of a class.
  • memory— Shows the total amount of memory and the amount that isn't currently in use.
  • threads— Lists the threads that are executing.

The threads command numbers all the threads, which enables you to use the suspend command followed by that number to pause the thread, as in suspend 1. You can resume a thread by using the resume command followed by its number. Another convenient way to set a breakpoint in a Java program is to use the catch text command, which pauses execution when the Exception class named by text is caught. You can also cause an exception to be ignored by using the ignore text command with the Exception class named by text.