| ## Java 1.1 Unleashed # - 28 - Java Debugging by Tim Park IN THIS CHAPTER Debugging with JDB Debugging with Symantec Café Bugs are an unfortunate fact of life in software design. As most development environments have done, Sun has included a debugger with the Java Development Kit (JDK) to help you fix your Java applets and applications. JDB (short for Java DeBugger) isn't a fancy visual debugging environment like the debuggers you may be familiar with from other professional development systems, but it does make the task of finding and exterminating bugs in your Java programs much easier. Most of this chapter shows you how to use JDB to debug your programs. The last section uses Symantec Café to show you how to debug your Java programs using Café's very popular visual environment. ## Debugging with JDB In the first part of the chapter, we use a simple Java applet to explain how to use JDB to help you debug your programs. As you can see from the output of the JDB `help` command in Listing 28.1, the range of commands available in JDB is extensive. #### Listing 28.1. Output from the JDB help command.
    > 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
    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> -- set a breakpoint in a method
    stop 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>:<line>   -- clear a breakpoint
    step                      -- execute current line
    cont                      -- continue execution from breakpoint

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

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

    memory                    -- report memory usage
    gc                        -- free unused objects (gc means garbage collection)

    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
    >
### Using JDB to Debug Your Program From our experience, the easiest way to learn JDB is by using it. Without further ado, then, let's use JDB to debug the simple class that follows. We chose a simple class for the benefit of people skipping ahead to learn how to use the basic features of the debugger. But don't worry if you've read all the previous chapters of this book--we'll still cover all the advanced features of JDB! `AddNumbers` is a simple class that implements both the user interface and the algorithm to add two numbers together. Well, at least this is what it's supposed to do. In reality, the class has a simple bug that will be located using JDB (see Listing 28.2). On the CD-ROM that accompanies this book, you will find two Java classes: `StartApplet`, which is the requisite subclass of the `Applet` class that instantiates the `AddNumbers` class, and `StartApplet.html`, which is used by the applet viewer to load the applet. Use the bundled installation program to install the files for this chapter onto your hard drive. #### Listing 28.2. The AddNumbers.java file--complete with bug.
    import java.awt.*;
    import java.awt.event.*;

    public class AddNumbers extends Frame implements ActionListener {
      int LeftNumber = 5;
      int RightNumber = 2;
      TextArea taResult;

      public AddNumbers() {
        setTitle("JDB Sample Java Program");
        setLayout(new BorderLayout());
        Panel p = new Panel();
        Button b = new Button("5");
        b.addActionListener(this);
        p.add(b);
        b = new Button("1");
        b.addActionListener(this);
        p.add(b);
        add("West", p);
        Panel g = new Panel();
        b = new Button("2");
        b.addActionListener(this);
        g.add(b);
        b = new Button("3");
        b.addActionListener(this);
        g.add(b);
        add("East", g);
        taResult = new TextArea(2,1);
        taResult.setEditable(false);
        add("South", taResult);
        pack();
        setSize(300,200);
        show();
      }

      public void ComputeSum () {
        int Total = LeftNumber - RightNumber;
        String ConvLeft  = String.valueOf(LeftNumber);
        String ConvRight = String.valueOf(RightNumber);
        String ConvTotal = String.valueOf(Total);
        taResult.setText(ConvLeft + " + " + ConvRight + " = " +  ConvTotal);
      }

      public void paint(Graphics g) {
        ComputeSum();
      }

      public void actionPerformed(ActionEvent e) {
        // Was the "5" button pressed?
        if (e.getActionCommand().equals("5")) {
          LeftNumber = 5;
          ComputeSum();
        }

        // Was the "1" button pressed?
        if (e.getActionCommand().equals("1")) {
          LeftNumber = 1;
          ComputeSum();
        }

        // Was the "2" button pressed?
        if (e.getActionCommand().equals("2")) {
          RightNumber = 2;
          ComputeSum();
        }

        // Was the "3" button pressed?
        if (e.getActionCommand().equals("3")) {
          RightNumber = 3;
          ComputeSum();
        }
      }
    }
### Compiling for JDB Before starting the debugging session, you must first compile the Java applet to include extra information needed only for debugging. This information is required so that the debugger can display information about your applet or application in a human-comprehensible form instead of a confusing wash of hexadecimal numbers. (Don't laugh--the first debuggers required you to do this translation, so count your lucky stars!) To compile your program with debugging information enabled, change to the `\java\classes\AddNumbers` directory and issue the following commands:
    C:\java\classes\AddNumbers> javac_g -g AddNumbers.java
    C:\java\classes\AddNumbers> javac_g -g StartApplet.java
The `javac_g` compiler is functionally identical to the `javac` compiler used in previous chapters, except that it doesn't perform any optimizations to your applet or application. Optimizations rearrange the statements in your applet or application to make them faster. This rearrangement makes it more difficult to conceptualize program flow when you are debugging, so using `javac_g` in conjunction with JDB is useful for keeping your program in the order in which you wrote it. The `-g` command-line option tells the compiler to include line number and object names in the output file. This option allows the debugger to reference objects and line numbers in a program by source code names instead of using the Java interpreter's internal representations. ### Setting Up a Debugging Session The next step in debugging a Java application or applet is to start JDB. There are two ways to do this, depending on whether you are debugging an applet or an application. Because we are debugging an applet in our example, we will use the applet viewer program supplied in the Java Development Kit to load JDB indirectly. If we were trying to debug an application instead, we would use the following command to start JDB:
    C:\java\classes\AddNumbers> jdb MyApplication
Because we are debugging an applet in our example and not an application, do not start JDB in the preceding manner. However, for future reference, after you invoke the debugger, using JDB on a Java application is identical to using it on an applet. With that important distinction covered, start the applet viewer with the following command:
    C:\java\classes\AddNumbers> appletviewer -debug StartApplet.html
The `-debug` flag specifies to the applet viewer that it should start up in JDB instead of by directly executing the `AddNumbers` class. Once the applet viewer loads, it opens its applet window and displays something similar to the following in the command-line window:
    C:\java\classes\AddNumbers> appletviewer -debug AddNumbers.html
    Initializing jdb...
    0x139f2f8:class(sun.applet.Appletviewer)

    >_
The first thing you should notice about JDB is that it is command-line based. Although this makes the learning curve for JDB a little steeper, it doesn't prevent you from doing anything you may be familiar with in a visual debugging environment. Before going further, examine the third line of the preceding output. This line indicates where the debugger stopped in its execution of the applet. In this case, it stopped during the execution of Sun's `applet.Appletviewer` class. This is logical, because `applet.Appletviewer` is the class that is transparently loaded and executed to load an applet. (See, you're learning things by using JDB already!) The hexadecimal number that prefixes this on the third line is the ID number assigned to the `sun.applet.Appletviewer` object by the Java interpreter. (Aren't you glad now that you can see the English version because you used the `-g` option for `javac`?) The `>` prompt on the fourth line indicates that there is currently no default thread that we are watching--more on this later. To understand the bug in `AddNumbers`, we will start the applet running in the debugger as follows:
    > run
    run sun.applet.AppletViewer MA.html
    running ...
    main[1]
The debugger should open the applet's frame and start executing it. Because the debugger and the applet are in different threads of execution, you can interact with the debugger and the applet at the same time. The preceding `main[1]` prompt indicates that the debugger is monitoring the applet thread (the `main` thread); the `[1]` indicates that we are currently positioned at the topmost stack frame on the method call stack (we'll explain what this means later). The `AddNumbers` applet contains two buttons on each side of the applet window with numbers as their labels (see Figure 28.1). The applet is supposed to use the number of the button pressed on the left and add it to the number of the button pressed on the right. Try this out: Press some of the buttons and check the applet's math. Figure 28.1. The `AddNumbers` applet with its numbered buttons. Hmm--unless you learned math differently than we did, there seems to be something wrong with the computation of the applet. (Maybe we found another Pentium processor flaw?) #### Basic Debugger Techniques To find out what is going on, let's examine the `ComputeSum()` method of the `AddNumbers` class. We would like to stop directly in this method without having to move slowly and tediously through the rest of the code. Setting and Clearing Breakpoints Fortunately, JDB has a set of commands called breakpoints that let you stop at particular places in your code. We'll use breakpoints to stop program execution in the `ComputeSum()` method; but first, press the `5` and `3` buttons on the applet to make sure that you see the same things as we do when the program is stopped. After pressing `5` and `3`, type the following in the debugger window:
    main[1] stop in AddNumbers.ComputeSum
    Breakpoint set in AddNumbers.ComputeSum
    main[1]
As you probably can guess, the `stop in` command tells JDB to stop when the `ComputeSum()` method in the `AddNumbers` class is entered. This is convenient to do because the computation part of method we are interested in is very close to the start of the method. If the statement was farther down in the method, it would be tedious to manually move down to the statement of interest every time we hit the breakpoint at the beginning of the method. In this case, we would want to use the `stop at` command in JDB. The `stop at` command works exactly like the `stop in` command in JDB, except that you specify the line number you want JDB to stop on instead of the method. For example, look at the `handleEvent()` method of `AddNumbers` class. If you want to stop at the `if` statement where the program checked for a push of the number `2` button, enter the following:
    main[1] stop at AddNumbers:90
    Breakpoint set in AddNumbers:90
    main[1]
However, don't do this because we want to examine the `ComputeSum()` method and not the `handleEvent()` method. We can verify this and see all the breakpoints currently set by using the `clear` command as follows:
    AWT-Callback-Win32[1] clear
    Current breakpoints set:
           AddNumbers:37
    AWT-Callback-Win32[1]
As expected, there is only one breakpoint set. Note that it is specified as `AddNumbers:37` instead of as `AddNumbers:ComputeSum`. JDB converts the command `stop in AddNumbers.ComputeSum` to the command `stop at AddNumbers:37` to make its internal bookkeeping easier. Of course, the real use of the `clear` command is to clear breakpoints when they have outgrown their usefulness. Don't do this now, but if you have to clear the breakpoint you just set, enter the following:
    AWT-Callback-Win32[1] clear AddNumbers.ComputeSum
    Breakpoint cleared at AddNumbers.ComputeSum
    AWT-Callback-Win32[1]
Let's get back to debugging our applet. When we left our applet, it was still running and there was no prompt in the JDB window. Why hasn't JDB stopped at `ComputeSum()`? If you look at the applet code, you notice that the `ComputeSum()` method is called only when you press a button in the applet. Press the `2` button to provide this `ComputeSum()` method call:
    main[1]
    Breakpoint hit: AddNumbers.ComputeSum (AddNumbers: 37)
    AWT-Callback-Win32[1]
As you can see, when you press the `2` button, the debugger stops at the `ComputeSum()` method as instructed. Note that we are now in a different thread (as shown by the change in prompts from `main[1]` to `AWT-Callback-Win32[1]`) because the AWT window manager thread calls the `handleEvent()` method in the `AddNumbers` class when a button is pressed in the applet. Although we know we are stopped in the `ComputeSum()` method, let's get a better sense of our bearings and refresh our memory by looking at where this method is in the source code. Fortunately, the line-number information is stored in the class when you compile with the `-g` option; you can access this information by using the JDB `list` command as follows:
    AWT-Callback-Win32[1] list
    33          }
    34     35          public void ComputeSum() {
    36
    37     =>       int Total = LeftNumber - RightNumber;
    38
    39            String ConvLeft  = String.valueOf(LeftNumber);
    40            String ConvRight = String.valueOf(RightNumber);
    41            String ConvTotal = String.valueOf(Total);
    AWT-Callback-Win32[1]
As expected, this output shows that we are stopped in `ComputeSum()` on the first statement--and as luck would have it, right before the computation statement. The observant reader probably already can tell what is wrong, but just pretend that it's a much more complicated computation and that you can't, okay? ### Examining Objects First, let's check our operands for the computation to make sure that they are correct. JDB provides three commands to display the contents of objects: `locals`, `print`, and `dump`. The `locals` command displays the current values of all the objects defined in the local scope. The `print` and `dump` commands are very similar and are used to display the contents of any object in any scope, including objects defined in the interface for the class. The main difference is that `dump` displays more information about complex objects (objects with inheritance or multiple data members) than `print` does. Because `LeftNumber` and `RightNumber` are class members, we'll have to use `print` to display them, as follows:
    AWT-Callback-Win32[1] print LeftNumber
    this.LeftNumber = 5
    AWT-Callback-Win32[1] print RightNumber
    this.RightNumber = 2
    AWT-Callback-Win32[1]
The operands seem to be exactly as we entered them in the applet. Let's take a look at the local objects to get a feeling for where we are by using the `locals` command as follows:
    AWT-Callback-Win32[1] locals
      this = AddNumbers[0,0,300x200,layout=java.awt.BorderLayout,
    resizable,title=JDB Sample Java Program]
      Total is not in scope.
      ConvLeft is not in scope.
      ConvRight is not in scope.
      ConvTotal is not in scope.
    AWT-Callback-Win32[1]
As expected, JDB is telling us that none of the local objects have been instantiated yet, so none of the objects are within the local scope yet. Let's move the execution of the method along one statement so that we can see what the value of the computation is. To do this, use the JDB `step` command as follows:
    AWT-Callback-Win32[1] step
    AWT-Callback-Win32[1]
    Breakpoint hit: AddNumbers.ComputeSum (AddNumbers:39)
    AWT-Callback-Win32[1]
JDB moves the execution along one statement and stops. Doing this also triggers another breakpoint, because we are still in `AddNumbers.ComputeSum()`. Look at the following output to determine how the computation turned out:
    AWT-Callback-Win32[1] locals
    this = AddNumbers[0,0,300x200,layout=java.awt.BorderLayout,
    resizable,title=JDB Sample Java Program]
      Total = 3
      ConvLeft is not in scope.
      ConvRight is not in scope.
      ConvTotal is not in scope.
    AWT-Callback-Win32[1]
We see that `Total` was instantiated, the addition carried out, and the result put in `Total`. But wait: `5` + `2` doesn't equal `3`! Take a look at the following source code:
    AWT-Callback-Win32[1] list
    35          public void ComputeSum() {
    36
    37            int Total = LeftNumber - RightNumber;
    38
    39     =>       String ConvLeft  = String.valueOf(LeftNumber);
    40            String ConvRight = String.valueOf(RightNumber);
    41            String ConvTotal = String.valueOf(Total);
    42
    43            taResult.setText(ConvLeft + " + " + ConvRight + " = " +
    AWT-Callback-Win32[1]
Oops--a minus sign was used instead of a plus sign! So much for finding another bug in the Pentium processor! But congratulations--you've found your first applet bug in JDB. #### Additional JDB Functions We've found our bug, but don't quit out of the applet viewer yet. We'll use it and the `AddNumbers` class to demonstrate a few more features of JDB that you might find useful in future debugging sessions. #### Walking the Method Call Stack with JDB In the previous section, we used the JDB `locals` command to look at the objects in the current scope. Using the JDB `up` command, you can also look at the local objects in previous stack frames (which consist of all the methods that either called `ComputeSum()` or called a method that called `ComputeSum()`, and so on). For example, look at the following output to see the state of the `handleEvent()` method right before it called the `ComputeSum()` method:
    AWT-Callback-Win32[1] up
    AWT-Callback-Win32[2] locals
    this = AddNumbers[0,0,300x200,layout=java.awt.BorderLayout,
    resizable,title=JDB Sample Java Program]
    evt = java.awt.Event[id=1001,x=246,y=28,
    target=java.awt.Button[5,5,20x24,label=2],arg=2]
    AWT-Callback-Win32[2]
As you can see, the `handleEvent` stack frame has two objects in its local frame: the pointer to this `AddNumbers` instance and the `Event` object passed to `handleEvent()`. It's possible to use the `up` command as many times as your method call stack is deep. To undo the `up` function and return to a higher method call in the stack, use the JDB `down` command as follows:
    AWT-Callback-Win32[2] down
    AWT-Callback-Win32[1] locals
    this = AddNumbers[0,0,300x200,layout=java.awt.BorderLayout,
    resizable,title=JDB Sample Java Program]
      Total = 3
      ConvLeft is not in scope.
      ConvRight is not in scope.
      ConvTotal is not in scope.
    AWT-Callback-Win32[1]
As expected, we are now back in the `ComputeSum()` local stack frame. #### Using JDB to Get More Information about Classes JDB also has two commands for getting more information about classes: `methods` and `classes`. The `methods` command enables you to display all the methods in a class. For example, you can examine the `AddNumbers` class with the `methods` command:
    AWT-Callback-Win32[1] methods
    void <init>()
    void ComputeSum()
    void paint(Graphics)
    boolean handleEvent(Event)
    AWT-Callback-Win32[1]
The `classes` function lists all the classes currently loaded in memory. Here is partial output from the execution of `classes` on `AddNumbers` (the actual output listed more than 80 classes):
    AWT-Callback-Win32[1] classes
    ...
    ...
    0x13a5f70:interface(sun.awt.UpdateClient)
    0x13a6160:interface(java.awt.peer.MenuPeer)
    0x13a67a0:interface(java.awt.peer.ButtonPeer)
    0x13a6880:class(java.lang.ClassNotFoundException)
    0x13a6ea8:class(sun.tools.debug.Field)
    0x13a7098:class(sun.tools.debug.BreakpointSet)
    0x13a7428:class(sun.tools.debug.Stackframe)
    0x13a7478:class(sun.tools.debug.LocalVariable)
    AWT-Callback-Win32[1]
#### Monitoring Memory Usage and Controlling finalize() For some large applets or applications, the amount of free memory may become a concern. JDB's `memory` command enables you to monitor the amount of used and free memory during your debugging session, as follows:
    AWT-Callback-Win32[1] memory
    Free: 2554472, total: 3145720
    AWT-Callback-Win32[1]
JDB also lets you explicitly demand that the `finalize()` method be run on all freed objects through the `gc` (garbage collection) command. This is useful for proving that your applet or application correctly handles deleted objects--which can be difficult to prove normally with small applets and applications because the `finalize()` methods are normally called only when the applet or application has run out of free memory. #### Controlling Threads of Execution As you know from Chapter 6, "Threads and Multithreading," Java applets have multiple threads of execution. Using JDB and the `threads` command, you can view these threads as follows:
    AWT-Callback-Win32[1] threads
    Group sun.applet.AppletViewer.main:
     1.  (java.lang.Thread)0x13a3a00           AWT-Win32                    running
     2.  (java.lang.Thread)0x13a2a58            AWT-Callback-Win32      running
     3.  (sun.awt.ScreenUpdater)0x13a2d98     Screen Updater         running
    Group group applet-StartApplet.class:
     4.     (java.lang.Thread)0x13a28f0 class running
    AWT-Callback-Win32[1]
As you can see from this output, there are four threads of simultaneous applet execution. Two correspond to the AWT window management system (threads 1 and 2), one is for updating the screen (thread 3), and one is for the actual applet itself (thread 4). JDB provides two commands for controlling the execution of threads: `suspend` and `resume`. Suspending a thread isn't very worthwhile in our simple example, but in multithreaded applications, it can be very worthwhile--you can suspend all but one thread and focus on that thread. But let's try `suspend` and `resume` on our applet to get a feel for their use. To suspend the AWT-Win32 thread, you should note its ID from the `threads` list and then use this information as the argument to `suspend`, as follows:
    AWT-Callback-Win32[1] threads
    Group sun.applet.AppletViewer.main:
     1.  (java.lang.Thread)0x13a3a00            AWT-Win32                    running
    ...
    AWT-Callback-Win32[1] suspend 1
    AWT-Callback-Win32[1] threads
    Group sun.applet.AppletViewer.main:
     1.  (java.lang.Thread)0x13a3a00       AWT-Win32                  suspended
     2.  (java.lang.Thread)0x13a2a58       AWT-Callback-Win32     running
     3.  (sun.awt.ScreenUpdater)0x13a2d98 Screen Updater          running
    Group group applet-StartApplet.class:
     4.    (java.lang.Thread)0x13a28f0 class                          running
    AWT-Callback-Win32[1]
As expected, the AWT-Win32 thread is now suspended. Threads are resumed in a completely analogous manner--with the `resume` command, as follows:
    AWT-Callback-Win32[1] resume 1
    AWT-Callback-Win32[1] threads
    Group sun.applet.AppletViewer.main:
     1.  (java.lang.Thread)0x13a3a00        AWT-Win32                  running
     2.  (java.lang.Thread)0x13a2a58        AWT-Callback-Win32      running
     3.  (sun.awt.ScreenUpdater)0x13a2d98 Screen Updater          running
    Group group applet-StartApplet.class:
     4.     (java.lang.Thread)0x13a28f0 class                     running
    AWT-Callback-Win32[1]
#### Using use to Point the Way to Your Java Source Code To execute the `list` command, JDB takes the line number and grabs the required lines of Java from the source file. To find that source file, JDB reads your `CLASSPATH` environment variable and searches all the paths contained in it. If that path doesn't contain your source file, JDB is unable to display the source for your program. This wasn't a problem for this example because the search path contained the current directory, but if you set up your applet or application and the source is located in a directory outside the search path, you'll have to use the `use` command to add to your path. The `use` command without any arguments displays the current search path as follows:
    AWT-Callback-Win32[1] use
    \java\classes;.;C:\JAVA\BIN\..\classes;
    AWT-Callback-Win32[1]
Appending a directory to the search path is unfortunately slightly tedious. You have to retype the entire current path and add the new path. To add the path `\myclasses` to the preceding path, do the following:
    AWT-Callback-Win32[1] use \java\classes;.;C:\JAVA\BIN\..\classes;\myclasses
    AWT-Callback-Win32[1]
#### Getting More Information about Your Objects with dump Earlier in this chapter, you saw an example of how to display an object's value using the `print` command. In this section, we'll look at JDB's `dump` command, which is a more useful display command for objects that contain multiple data members. The `AddNumbers` class is a good example (note that `this` in this case refers to the instantiation of `AddNumbers` for our applet), as follows:
    AWT-Callback-Win32[1] dump this
    this = (AddNumbers)0x13a3000 {
        ComponentPeer peer = (sun.awt.win32.MFramePeer)0x13a31b0
        Container parent = null
        int x = 0
        int y = 0
        int width = 300
        int height = 200
        Color foreground = (java.awt.Color)0x13a2bb0
        Color background = (java.awt.Color)0x13a2b98
        Font font = (java.awt.Font)0x13a31d0
        boolean visible = true
        boolean enabled = true
        boolean valid = true
        int ncomponents = 3
    AWT-Callback-Win32[1] _
Contrast this with the output from print:
    AWT-Callback-Win32[1] print this
    this = AddNumbers[0,0,300x200,layout=java.awt.BorderLayout,
    resizable,title=JDB Sample Java Program]
    AWT-Callback-Win32[1]
As you can see, the `dump` command displays the data members for the class but `print` displays only the key attributes for the class. #### Handling Exceptions with catch and ignore JDB has two functions for dealing with exceptions: `catch` and `ignore`. The `catch` function, similar to a breakpoint, enables you to trap exceptions and stop the debugger. The `catch` command is useful when debugging because it is much easier to diagnose an exception when you know the conditions under which it occurred. To catch an exception, simply type `class` and the name of the exception class. To trap any exception in the `Exception` base class, do the following:
    AWT-Callback-Win32[1] catch Exception
    AWT-Callback-Win32[1]
The `ignore` function does exactly the opposite of `catch`. It squelches the specified class of exceptions raised by an applet or application. The use of `ignore` is completely analogous to `catch`, as shown by the following example:
    AWT-Callback-Win32[1] ignore ArrayOutOfBoundsException
    AWT-Callback-Win32[1]
#### Continuing Program Execution with cont You may be wondering how to restart execution once you reach a breakpoint and execution has stopped. Why, you use the `cont` command, of course!
    AWT-Callback-Win32[1] cont
The program resumes execution and the JDB prompt does not return until another breakpoint or exception is reached. #### Leaving JDB Using exit Although it may be obvious to you already, there is one final command that comes in handy at least once in any debugging session. The `exit` command lets you out of the debugger and back to the operating system command prompt. ## Debugging with Symantec Café If you want to be a more efficient Java developer, it is hard to find anything as important as adopting an integrated development environment for your development platform. One of the best such visual environments is Symantec Café for Java. In addition to excellent debugging support, it also features project management, a visual GUI builder, and object-oriented browsing of your Java classes. Because of its solid debugger, Café is the IDE we'll use in the next sections to teach you about graphical debugging. ### Loading the AddNumbers Project The first thing we have to do to use Symantec Café's debugger is to load the project file for the `AddNumbers` applet. This project file contains all the information required to build `AddNumbers.java` and `StartApplet.java`. After launching the Symantec Café IDE, use the mouse to pull down the Project menu and select the Open option. The Open Project dialog box appears. In this dialog box, type the path or navigate to the directory containing the same `AddNumbers` applet you used in the first part of this chapter. From this directory, you should be able to see a file named `AddNumbers.prj`. Select this file and click OK to load the project. ### Building the AddNumbers Project The `AddNumbers.prj p`roject should have come precompiled on the CD-ROM that accompanies this book. In case you want to do it yourself, let's rebuild it in Café. Pull down the Project menu again and select Build. Café pops up a build status window and builds the files one by one. ### Running the AddNumbers Project Before we jump into debugging the `AddNumbers` project, let's run it first and make sure that it still adds numbers wrong. To run it, pull down the Project menu and select Execute Program (or press its accelerator key, Ctrl+F5). #### Debugging the AddNumbers Project After you play with the `AddNumbers` applet for a while and confirm that it is broken, stop it at the applet's execution: Open the Debug menu and select Start/Restart Debugging. This command should bring up five windows: a source code window displaying `StartApplet.java`, a Data/Object window, a Call window, a Thread window, and an Output window. A thorough coverage of all these windows would require more than a single chapter. For the purposes of this chapter, we'll explain what all the windows do, but focus on the source code window and the Data/Object window--these are the only windows you'll use for 90 percent of your debugging tasks anyway. At this point, the debugger is stopped at the first program statement and is waiting for your command. Suppose that, from your last test run, you have guessed that the error lies somewhere within the `ComputeSum()` method in the `AddNumbers` class. Let's set a breakpoint here using Café and run until we get to this point in the program. To set a breakpoint, you must first open the `AddNumbers.java` file. With the `AddNumbers` file in the editor, move down in the file until you reach the `ComputeSum()` method. Pull down the Window menu in Café and make sure that the Debug Toolbox option is selected. Next, move the cursor down to line 37, which is the computation of `TotalSum`. Finally, find the Debug toolbar on your icon bar, which looks like the bar in Figure 28.2. Figure 28.2. Café's Debug toolbar. The breakpoint toggle button is the one with the red and green flags on it. Click this button to toggle the breakpoint on for the computation line. A little red diamond should appear next to that line (see Figure 28.3). Figure 28.3. Setting a breakpoint in Café. With the breakpoint set, we're now ready to let the program execute freely until it reaches the breakpoint. To do this, pull down the Debug menu and select Go Until Breakpoint. This action starts the applet viewer; the `AddNumbers` applet runs until it reaches the `ComputeSum()` method as part of the redraw. Execution stops at the line on which we put the breakpoint--as we expect (see Figure 28.4). Notice, however, that Café doesn't execute the line we set as the target of the breakpoint. Figure 28.4. Café stopped at a breakpoint. Café's Call window (shown in Figure 28.5) shows the stack of method calls that have been made to reach this point in program execution. We know that the `ComputeSum()` method was reached during the repaint of the window because it was called from the `AddNumbers` `paint()` method. Figure 28.5. Café's Call window. Café's Breakpoint window (shown in Figure 28.6) shows which breakpoint caused the stop in execution. In this example, there is only one breakpoint, so this information isn't very helpful--but for bigger debugging situations, the Breakpoint window is more useful as a central reference for all the breakpoints you have set in your program. Figure 28.6. Café's Breakpoint window. The Thread window shows all the threads in operation for your program. Note that its output is exactly the same, if in a somewhat more readable format, as what we saw when we were using JDB. But what we're really interested in is the Data/Object window (shown in Figure 28.7). This window contains the current values of all the data members for local objects. In looking at the values of `lLeftNumber` and `lRightNumber`, we can tell that the problem isn't caused by the operands to the computation because they are what we expect them to be. Figure 28.7. Café's Data/Object window before the `Total` computation. Let's move execution ahead one line so that we can see the result of the execution. To do this, press the F10 key or click the button in the Debug toolbar with the foot "stepping into" a yellow hole. The "stepping into" button differs from the "stepping over" button (the next button on the Debug bar) in that "stepping into" shows the execution of any and all function calls incurred on this line; "stepping over" shows only the output of the function. Looking at the Data/Object window, we can now tell that something is wrong with the computation of `Total`: `5` + `2` certainly doesn't equal `3`, as shown in the display of the Data/Object window in Figure 28.8. Hopefully, with this information, the bug is now obvious to you. Figure 28.8. Café's Data/Object window after the `Total` computation. To complete the debug cycle, take Café out of debugging mode using the Stop Debugging option in the Debug menu. Then change the `-` operator in the `Total` computation to a `+` and rebuild using the Build option in the Project menu. Run the program again to prove that the problem has been fixed. ## Summary This chapter helped you learn how to debug by giving you hands-on experience with Sun's Java debugger, JDB, and Symantec's visual environment, Café. Debugging is a learned skill; don't be discouraged if it takes you a long time to debug your first applet or application. As you gain experience doing it, you'll start to recognize the effects of different classes of bugs and be able to solve each bug in shorter amounts of time. Patience is definitely a virtue in software debugging.
| ©Copyright, Macmillan Computer Publishing. All rights reserved.