Home > Articles > Programming > Java

  • Print
  • + Share This
This chapter is from the book

Using a Debugger

Debugging with print statements is not one of life's more joyful experiences. You constantly find yourself adding and removing the statements, then recompiling the program. Using a debugger is better. A debugger runs your program in full motion until it reaches a breakpoint, and then you can look at everything that interests you.

The JDB Debugger

The JDK includes JDB, an extremely rudimentary command-line debugger. Its user interface is so minimal that you will not want to use it except as a last resort. It really is more a proof of concept than a usable tool. We nevertheless briefly introduce it because there are situations in which it is better than no debugger at all. Of course, many Java programming environments have far more convenient debuggers. The main principles of all debuggers are the same, and you may want to use the example in this section to learn to use the debugger in your environment instead of JDB.

Examples 11-9 through 11-11 show a deliberately corrupted version of the ButtonTest program from Chapter 8. (We broke up the program and placed each class into a separate file because some debuggers have trouble dealing with multiple classes in the same file.)

When you click on any of the buttons, nothing happens. Look at the source code—button clicks are supposed to set the background color to the color specified by the button name.

Example 11-9. BuggyButtonTest.java

 1. import javax.swing.*;
 2.
 3. public class BuggyButtonTest
 4. {
 5.   public static void main(String[] args)
 6.   {
 7.     BuggyButtonFrame frame = new BuggyButtonFrame();
 8.     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 9.     frame.setVisible(true);
10.   }
11. }

Example 11-10. BuggyButtonFrame.java

 1. import java.awt.*;
 2. import javax.swing.*;
 3.
 4. public class BuggyButtonFrame extends JFrame
 5. {
 6.    public BuggyButtonFrame()
 7.    {
 8.       setTitle("BuggyButtonTest");
 9.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
10.
11.       // add panel to frame
12.
13.       BuggyButtonPanel panel = new BuggyButtonPanel();
14.       add(panel);
15.    }
16.
17.    public static final int DEFAULT_WIDTH = 300;
18.    public static final int DEFAULT_HEIGHT = 200;
19. }

Example 11-11. BuggyButtonPanel.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import javax.swing.*;
 4.
 5. class BuggyButtonPanel extends JPanel
 6. {
 7.    public BuggyButtonPanel()
 8.    {
 9.       ActionListener listener = new ButtonListener();
10.
11.       JButton yellowButton = new JButton("Yellow");
12.       add(yellowButton);
13.       yellowButton.addActionListener(listener);
14.
15.       JButton blueButton = new JButton("Blue");
16.       add(blueButton);
17.       blueButton.addActionListener(listener);
18.
19.       JButton redButton = new JButton("Red");
20.       add(redButton);
21.       redButton.addActionListener(listener);
22.    }
23.
24.    private class ButtonListener implements ActionListener
25.    {
26.       public void actionPerformed(ActionEvent event)
27.       {
28.          String arg = event.getActionCommand();
29.          if (arg.equals("yellow"))
30.             setBackground(Color.yellow);
31.          else if (arg.equals("blue"))
32.             setBackground(Color.blue);
33.          else if (arg.equals("red"))
34.             setBackground(Color.red);
35.       }
36.    }
37. }

In a program this short, you may be able to find the bug just by reading the source code. Let us pretend that scanning the source code for errors is not practical. Here is how you can run the debugger to locate the error.

To use JDB, you must first compile your program with the -g option, for example:

javac -g BuggyButtonTest.java BuggyButtonFrame.java BuggyButtonPanel.java

When you compile with this option, the compiler adds the names of local variables and other debugging information into the class files. Then you launch the debugger:

jdb BuggyButtonTest

Once you launch the debugger, you will see a display that looks something like this:

Initializing jdb...
>

The > prompt indicates the debugger is waiting for a command. Table 11-4 shows all the debugger commands. Items enclosed in [...] are optional, and the suffix (s) means that you can supply one or more arguments separated by spaces.

Table 11-4. Debugging Commands

threads [threadgroup]

Lists threads

thread thread_id

Sets default thread

suspend [thread_id(s)]

Suspends threads (default: all)

resume [thread_id(s)]

Resumes threads (default: all)

where [thread_id] or all

Dumps a thread's stack

wherei [thread_id] or all

Dumps a thread's stack and program counter info

threadgroups

Lists thread groups

threadgroup name

Sets current thread group

print name(s)

Prints object or field

dump name(s)

Prints all object information

locals

Prints all current local variables

classes

Lists currently known classes

methods class

Lists a class's methods

stop in class.method

Sets a breakpoint in a method

stop at class:line

Sets a breakpoint at a line

up [n]

Moves up a thread's stack

down [n]

Moves down a thread's stack

clear class:line

Clears a breakpoint

step

Executes the current line, stepping inside calls

stepi

Executes the current instruction

step up

Executes until the end of the current method

next

Executes the current line, stepping over calls

cont

Continues execution from breakpoint

catch class

Breaks for the specified exception

ignore class

Ignores the specified exception

list [line]

Prints source code

use [path]

Displays or changes the source path

memory

Reports memory usage

gc

Frees unused objects

load class

Loads Java class to be debugged

run [class [args]]

Starts execution of a loaded Java class

!!

Repeats last command

help (or ?)

Lists commands

exit (or quit)

Exits debugger

We cover only the most useful JDB commands in this section. The basic idea, though, is simple: you set one or more breakpoints, then run the program. When the program reaches one of the breakpoints you set, it stops. You can inspect the values of the local variables to see if they are what they are supposed to be.

To set a breakpoint, use


stop in class.method

or the command


stop at class:line

For example, let us set a breakpoint in the actionPerformed method of BuggyButtonTest. To do this, enter

stop in BuggyButtonPanel$ButtonListener.actionPerformed

Now we want to run the program up to the breakpoint, so enter

run

The program will run, but the breakpoint won't be hit until Java starts processing code in the actionPerformed method. For this, click on the Yellow button. The debugger breaks at the start of the actionPerformed method. You'll see:

Breakpoint hit: thread="AWT-EventQueue-0", BuggyButtonPanel$ButtonListener.actionPerformed
ccc.gif(), line=28, bci=0
   28          String arg = event.getActionCommand();
   

The list command lets you find out where you are. The debugger will show you the current line and a few lines above and below. You also see the line numbers. For example:

24      private class ButtonListener implements ActionListener
25      {
26         public void actionPerformed(ActionEvent event)
27         {
28=>          String arg = event.getActionCommand();
29            if (arg.equals("yellow"))
30               setBackground(Color.yellow);
31            else if (arg.equals("blue"))
32               setBackground(Color.blue);
33            else if (arg.equals("red"))

Type locals to see all local variables. For example,

Method arguments:
  event = instance of java.awt.event.ActionEvent(id=698)
Local variables:

For more detail, use


dump variable

For example,

dump event

displays all instance fields of the evt variable.

event = instance of java.awt.event.ActionEvent(id=698) {
   SHIFT_MASK: 1
   CTRL_MASK: 2
   META_MASK: 4
   ALT_MASK: 8
   ACTION_FIRST: 1001
   ACTION_LAST: 1001
   ACTION_PERFORMED: 1001
   actionCommand: "Yellow"
   modifiers: 0
   serialVersionUID: -7671078796273832149
   . . .

There are two basic commands to single-step through a program. The step command steps into every method call. The next command goes to the next line without stepping inside any further method calls. Type next twice and then type list to see where you are.

The program stops in line 31.

27         {
28            String arg = event.getActionCommand();
29            if (arg.equals("yellow"))
30               setBackground(Color.yellow);
31=>          else if (arg.equals("blue"))
32               setBackground(Color.blue);
33            else if (arg.equals("red"))
34               setBackground(Color.red);
35         }

That is not what should have happened. The program was supposed to call setColor(Color.yellow) and then exit the method.

Dump the arg variable.

arg = "Yellow"

Now you can see what happened. The value of arg was "Yellow", with an uppercase Y, but the comparison tested

if (arg.equals("yellow"))

with a lowercase y. Mystery solved.

To quit the debugger, type:

quit

As you can see from this example, the jdb debugger can be used to find an error, but the command-line interface is very inconvenient. Remember to use list and locals whenever you are confused about where you are. But if you have any choice at all, use a better debugger for serious debugging work.

The Eclipse Debugger

Eclipse has a modern and convenient debugger that has many of the amenities that you would expect. In particular, you can set breakpoints, inspect variables, and single-step through a program.

To set a breakpoint, move the cursor to the desired line and select Run -> Toggle Line Breakpoint from the menu. The breakpoint line is highlighted (see Figure 11-8).

11fig08.jpgFigure 11-8 Breakpoints in the Eclipse debugger

To start debugging, select Run -> Debug As -> Java Application from the menu. The program starts running. Set a breakpoint in the first line of the actionPerformed method.

When the debugger stops at a breakpoint, you can see the call stack and the local variables (see Figure 11-9).

11fig09.jpgFigure 11-9 Stopping at a breakpoint

To single-step through the application, use the Run -> Step into (F5) or Run -> Step over (F6) commands. In our example, press the F6 key twice to see how the program skips over the setBackground(Color.yellow) command. Then watch the value of arg to see the reason (see Figure 11-10).

11fig10.jpgFigure 11-10 Inspecting variables

As you can see, the Eclipse debugger is much easier to use than JDB because you have visual feedback to indicate where you are in the program. Setting breakpoints and inspecting variables is also much easier. This is typical of debuggers that are a part of an integrated development environment.

This chapter introduced you to exception handling and gave you some useful hints for testing and debugging. The next chapter covers streams.

  • + Share This
  • 🔖 Save To Your Account