# Chapter 23

# Advanced Debugging and Troubleshooting

### CONTENTS

- Starting the Java Debugger
  - Starting the Debugger Directly
  - Attaching the Debugger to a Current Interpreter Session
  - Attaching the Debugger to an Applet Viewer Session
- Troubleshooting the Code with the Debugger
  - Examining Threads
  - Examining Methods and Classes
  - Examining Instance Variables
- Identifying Trouble with the Code
- Summary

Sometimes your code will compile just fine but will not run properly. As you begin to develop advanced applets and applications, this will happen more frequently. Instead of spending days poring over thousands of lines of code trying to pinpoint problem areas, you should spend an hour with a tool called the Java debugger. Use it whenever you have problems in your source code that you cannot solve without a little help.

Debugging code after a successful compile is quite different from debugging code that will not compile, primarily because of the type of errors you will come across at this stage of troubleshooting. When your program will not compile, the errors mostly relate to syntax, such as missing semicolons or bad references to objects. However, when your program does not run the way you expect or displays errors during execution of the code, the errors mostly relate to the program's logic, such as calling the wrong method or using a variable set to the wrong value.

Although the Java debugger is quite useful for debugging code interactively, sometimes you do not want to do that. For those times, a debugging utility called `HelpDebug` can be used to help debug your code using a noninteractive means. For this reason, this chapter covers both interactive and noninteractive debugging.

Advanced troubleshooting requires more logic than concept. Therefore, this chapter takes you step by step through the debugging process as it demonstrates how to use the Java debugger.

## Starting the Java Debugger

Anyone who has ever programmed in C/C++ knows that a good debugger can quite literally save the day. The Java debugger works much like the other debuggers you may have used to debug your C or C++ code. You invoke it from the command line. The name of the command-line program is `jdb`, which stands for Java debugger. When invoked, the debugger uses the Debugger API-`sun.tools.debug`-to help you troubleshoot problems in your code.

You can invoke the debugger from the command line in one of two ways: The first way to start a `jdb` session is to pass it the name of your Java class file without the `.class` extension, and the second is to attach it to a Java interpreter or applet viewer.

As with other tools in the Java Developer's Kit, the debugger allows you to use command-line options. These options depend on the way you use the debugger. If you use it directly, any options you specify on the command line are passed on to the interpreter the debugger invokes, so you can use any of the interpreter options when you start the debugger directly.

When you attach the debugger to an interpreter session, you are limited to three options: `-host`, `-password`, and `-classpath`. The `-host` option sets the name of the host machine on which the interpreter session is running. The `-password` option sets the password and enables you to attach the debugger to a particular session. The `-classpath` option lets you specify a path for your class files that overrides the default or current `CLASSPATH` setting. Because this option takes precedence, you should set it to include the current directory, the location of your personal or third-party class files, and the location of the Java API classes.

Tip
To aid the debugging process, compile your code with the `-g` option of the Java compiler. This option tells the Java compiler to generate debugging tables. Although debugging tables are not useful for troubleshooting syntax problems, they are useful for troubleshooting logic problems. Generally, you will use these tables to browse local variables on the stack.

### Starting the Debugger Directly

To start a debugging session directly, usually you would pass it the name of your Java class file without the `.class` extension by typing the following at the command line:

> `jdb classname`

where `classname` is the name of the class file that starts the program you want to debug.

When you start the debugger directly, it in turn starts the Java interpreter with any parameters you passed on the command line. Afterward, the debugger loads the specified class file.

Note
For the JDK version 1.0 or later, the debugger expects you to be on a networked host. If you are not, you may see the following error message or a similar one: `java.net.UnknownHostException` `at java.net.InetAddress.getLocalHost(InetAddress.java:276)` `at sun.tools.debug.RemoteDebugger.<init>(RemoteDebugger.java:61)` `at sun.tools.ttydebug.TTY.<init>(TTY.java:1263)` `at sun.tools.ttydebug.TTY.main(TTY.java:1387)` To clear up this problem, start a browser session that connects your computer to the Internet. Then you should be able to start the debugger.

### Attaching the Debugger to a Current Interpreter Session

Although most programmers prefer to invoke the debugger directly, you can also attach a debugging session to an interpreter that is already running. To do this, start the Java interpreter with the `-debug` option. When you use this option, the interpreter displays a password that must be used when starting the debugging session.

Note
Because the password is randomly allocated by session, you can use it only for the applet or application currently running on your system.

you should follow to attach the debugger to an interpreter:

1. Start the interpreter with the `-debug` option, such as
   `java -debug classname`
   where `classname` is the name of the class file that starts the application.
2. Invoke the debugger with the session password:
   `jdb -host hostname -password session_password`
   where `hostname` is the hostname of your computer and `session_password` is the password provided by the interpreter.

Note
When you start the Java interpreter or applet viewer with the `-debug` option under JDK version 1.0, they both check for the hostname and local Internet address of your system. If you are not on a networked host, you may see the following error message or a similar one: `java.net.UnknownHostException` `at java.net.InetAddress.getLocalHost(InetAddress.java:276)` `at sun.tools.debug.RemoteDebugger.<init>(RemoteDebugger.java:61)` `at sun.tools.ttydebug.TTY.<init>(TTY.java:1263)` `at sun.tools.ttydebug.TTY.main(TTY.java:1387)` To clear up this problem, start a browser session that connects your computer to the Internet. Then you should be able to start the interpreter or applet viewer with the `-debug` option.

### Attaching the Debugger to an Applet Viewer Session

The final way to use the debugger is to attach it to an applet viewer session by starting the applet viewer with the `-debug` option.

Here are the steps you would follow to attach the debugger to the applet viewer:

1. Start the applet viewer with the `-debug` option, such as
   `appletviewer -debug document.html`
   where `document.html` is the name of the HTML document with a valid `<APPLET>` tag.
2. Run the applet using the `run` command of the debugger.

## Troubleshooting the Code with the Debugger

After you start the debugger, an input prompt appears. You can enter commands directly to the debugger at this prompt. One of the most useful commands is `help`, which lists all the commands the debugger recognizes. The help listing for the debugger follows:

> `Initializing jdb…
> > 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
> 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`

The sections that follow examine how many of these options are used to debug your code.

### Examining Threads

The debugger includes a group of useful commands for examining threads. All Java programs have at least one thread of execution.

The `threads` command lists all the threads in use and their status. The type and number of threads running on the system vary according to the threads used by your system, how you start the debugger, and the threads used by your Java program. When you start the debugger directly, no Java program is running, and few threads are running on your system.

To follow along with this debugging session, start the debugger for use with the Jompanion editor developed in Chapter 21, "Designing and Implementing Advanced Applications." To do this, change to the directory containing the class files for Jompanion and type the following:

> `jdb Jompanion`

Your system should display output similar to this:

> `Initializing jdb...
> 0x13a41e0:class(Jompanion)
> >
> `

Note
The greater-than sign (`>`) is the input prompt for the debugger.

At this point in the debugging session, the Jompanion application is not running; I have merely invoked a session with the debugger. To examine the threads currently running on your system, type `threads` at the debugger input prompt. On a Windows 95 system using JDK 1.0, the following threads run whenever you start the debugger:

> `> threads
> Group system:
> 1. (java.lang.Thread)0x13931f8 Finalizer thread cond. waiting
> 2. (java.lang.Thread)0x1393918 Debugger agent running
> 3. (sun.tools.debug.BreakpointHandler)0x13a2668 Breakpoint
> Âhandler cond. waiting
> Group main:
> 4. (java.lang.Thread)0x13930a0 main running`

The output from the `threads` command tells you a lot about the threads running on your system. As you can see, each thread is listed by threadgroup and is numbered consecutively. This number is the ID of the thread. Following the thread ID is the class associated with the thread, its name, and its status.

Using the ID number, you can set the default thread using the `thread` command. To switch to the main thread, type the following:

> `>thread 4`

When you do this, the input prompt changes to this:

> `main[1]`

The new prompt, `main[1]`, tells you the current thread is the thread named `main`. All commands typed at the input prompt are taken in context of this thread. However, because no Java program is running on your system now, the debugger essentially behaves the same. To switch to the debugger agent thread, type its ID at the input prompt, like this:

> `main[1] thread 2`

When you do this, the input prompt changes to this:

> `Debugger agent[1]`

To start the application from within the debugging session, type `run` at the debugger input prompt. The following output should display in your command area:

> `> run
> run Jompanion
> running ...
> main[1]`

Now that a program is running, the `main[1]` prompt references the main thread of your program-in this case, Jompanion's main thread. Using the `threads` command, you can check the threads running in the context of the main thread. As you can see from the following listing, the threadgroup and threads are different when a program is running:

> `main[1] threads
> Group Jompanion.main:
> 1. (java.lang.Thread)0x13a6738 AWT-Win32 running
> 2. (java.lang.Thread)0x13a6790 AWT-Callback-Win32 running
> 3. (sun.awt.ScreenUpdater)0x13a6a38 Screen Updater cond. waiting
> main[1]`

The three threads listed previously are all the threads running in the context of the current thread. Obviously, there are more threads running on your system, and you may be wondering what happened to the threads the debugger is running. To see all the threads running on your system, change to the system threadgroup and then type the `threads` command:

> `main[1] threadgroup system
> main[1] threads
> Group system:
> 1. (java.lang.Thread)0x13931f8 Finalizer thread cond. waiting
> 2. (java.lang.Thread)0x1393918 Debugger agent running
> 3. (sun.tools.debug.BreakpointHandler)0x13a2668
> ÂBreakpoint handler cond. waiting
> Group main:
> 4. (java.lang.Thread)0x13930a0 main cond. waiting
> Group Jompanion.main:
> 5. (java.lang.Thread)0x13a6738 AWT-Win32 running
> 6. (java.lang.Thread)0x13a6790 AWT-Callback-Win32 running
> 7. (sun.awt.ScreenUpdater)0x13a6a38 Screen Updater cond. waiting
> main[1]`

To analyze the behavior of your program when certain threads are stopped, use the `suspend` command. Although this command is more useful when you have multiple background threads, it is used here to stop the `Screen Updater` thread of the Jompanion application. After stopping a thread, you can check its status using the `threads` command. Here's how:

> `main[1] suspend 7
> main[1] threads
> Group system:
> 1. (java.lang.Thread)0x13931f8 Finalizer thread cond. waiting
> 2. (java.lang.Thread)0x1393918 Debugger agent running
> 3. (sun.tools.debug.BreakpointHandler)0x13a2668
> ÂBreakpoint handler cond. waiting
> Group main:
> 4. (java.lang.Thread)0x13930a0 main cond. waiting
> Group Jompanion.main:
> 5. (java.lang.Thread)0x13a6738 AWT-Win32 running
> 6. (java.lang.Thread)0x13a6790 AWT-Callback-Win32 running
> 7. (sun.awt.ScreenUpdater)0x13a6a38 Screen Updater suspended
> main[1]`

After testing the behavior of your application with the thread suspended, you will usually want to restart the thread and go on to test the behavior of your application with other threads suspended. To restart a thread, use the `resume` command.

### Examining Methods and Classes

By examining the methods and classes used in your program, you can learn more about the behavior of your program, verify that sections of the code are being executed as you expect, and pinpoint problem areas in the code. The debugger provides many ways to examine methods and classes. However, before you can do so, you need to start a debugging session.

This section demonstrates how to attach the debugger to a running application. To follow along, change to the directory containing the Jompanion class files and start the Jompanion application using the interpreter as follows:

> `java -debug Jompanion`

Your system should display a password to be used with the debugger, such as the following:

> `Agent password=52d2zj`

If your password is `52d2zj`, you now open a new command window and type this:

> `jdb -password 52d2zj`

Your system should display output similar to the following:

> `Initializing jdb...
> >`

Now that your debugging session is started, you can examine the classes and methods. To list the classes the debugger currently knows about, use the debugger's `classes` command. If you use this command for Jompanion, the output from the debugger is similar to the following abbreviated listing:

> `> classes
> ** classes list **
> 0x1393008:class(java.lang.Thread)
> 0x1393018:class(java.lang.Object)
> 0x1393098:class(java.lang.Class)
> 0x1393028:class(java.lang.String)
> 0x1393038:class(java.lang.ThreadDeath)
> 0x1393048:class(java.lang.Error)
> 0x13932f0:class(Jompanion)
> 0x1393300:class(java.awt.Frame)
> 0x1393310:class(java.awt.Window)
> 0x1393320:class(java.awt.Container)
> 0x1393330:class(java.awt.Component)
> 0x1393ab0:class(java.awt.BorderLayout)
> 0x1393af8:class(java.awt.Color)
> 0x1393b70:class(java.awt.Font)
> 0x1393bd0:class(java.awt.MenuBar)
> 0x1393be0:class(java.awt.MenuComponent)
> 0x1393c08:class(sun.tools.java.ClassPath)
> 0x1393c18:class(java.io.File)
> 0x1393c60:class(sun.tools.java.ClassPathEntry)
> 0x1393cd0:class(sun.tools.zip.ZipFile)
> 0x1393d00:class(java.io.RandomAccessFile)
> 0x1393d70:class(java.util.Vector)
> 0x1393d90:class(java.awt.Menu)
> 0x1393da0:class(java.awt.MenuItem)
> 0x1393e90:interface(sun.tools.zip.ZipConstants)
> 0x1393ee0:class(sun.tools.zip.ZipEntry)
> 0x1393f30:interface(java.awt.peer.MenuBarPeer)
> 0x1394088:interface(java.awt.peer.FramePeer)
> 0x1394098:class(java.awt.Choice)
> 0x13940c0:interface(java.awt.peer.ChoicePeer)
> 0x13942d0:class(java.awt.FlowLayout)
> 0x13949a0:class(java.awt.TextArea)
> 0x13949b0:class(java.awt.TextComponent)
> 0x13949e0:interface(java.awt.peer.TextComponentPeer)
> 0x1394a08:class(java.awt.FileDialog)
> 0x1394a18:class(java.awt.Dialog)
> 0x1394a88:class(java.awt.Toolkit)
> 0x1395878:class(sun.awt.win32.MToolkit)
> 0x1398038:class(java.awt.Dimension)
> 0x1398050:class(sun.awt.win32.MFramePeer)
> 0x1398db8:class(sun.awt.win32.MPanelPeer)
> 0x1398dc8:class(sun.awt.win32.MCanvasPeer)
> 0x1398dd8:class(sun.awt.win32.MComponentPeer)
> 0x139df48:class(java.awt.Rectangle)
> 0x139df60:class(java.awt.Insets)
> 0x139e148:class(sun.awt.win32.MMenuBarPeer)
> 0x139e168:class(sun.tools.debug.BreakpointHandler)
> 0x139e1a0:class(sun.tools.debug.BreakpointQueue)
> 0x139e1e8:class(java.net.Socket)
> 0x139e220:class(sun.awt.win32.MMenuPeer)
> 0x139e230:class(sun.awt.win32.MMenuItemPeer)
> 0x139e2d0:class(sun.awt.win32.MChoicePeer)
> 0x139e300:class(sun.awt.win32.MTextAreaPeer)
> 0x139e330:interface(java.awt.peer.ContainerPeer)
> 0x139e340:interface(java.awt.peer.WindowPeer)
> 0x139e350:class(sun.awt.win32.Win32FontMetrics)
> 0x139e360:class(java.awt.FontMetrics)
> 0x139e3a8:interface(java.awt.peer.TextAreaPeer)
> 0x139e3d8:class(sun.awt.ScreenUpdater)
> 0x139e410:class(sun.awt.ScreenUpdaterEntry)
> 0x139e468:class(sun.awt.win32.Win32Graphics)
> 0x139e478:class(java.awt.Graphics)
> >`

As you can see, the list of classes known to the debugger includes all classes used by both the debugger and the program you are debugging. You can use the `methods` command to see the methods in any class shown in the classes list. To see a list of methods in the Jompanion class, type the following:

> `> methods Jompanion`

The debugger output should be similar to the following:

> `void AddDialog()
> boolean handleEvent(Event)
> void cut()
> void copy()
> void paste()
> void openFile()
> void saveAsFile()
> void saveOpenedFile()
> void startNewText()
> void showFindDialog()
> void showRepDialog()
> void showReplaceAllDialog()
> void showAboutDialog()
> void showErrorDialog(String)
> boolean read(String)
> boolean write(String)
> String check(String)
> void setFindString(String)
> void setReplaceString(String)
> void findSelectedText()
> void findText()
> void replaceSelectText()
> boolean replaceText()
> void main(String[])`

When you know the methods for a particular class, you can debug the method using breakpoints and steps. When the program you are debugging reaches a breakpoint, execution of the program stops. You can then step through the program line by line to examine what happens when the program executes specific lines within the method.

Warning
When your program reaches a breakpoint, all execution of the program stops. If the program occupies the entire screen when you hit the breakpoint, your workstation may lock up. To avoid this, resize the application or applet's frame so you can access the command area with the active debugging session.

To set a breakpoint, use the `stop` command and reference the method you want to put the breakpoint in at the class level. For example, to place a breakpoint in the `copy` method of the Jompanion class, you would reference the method as `Jompanion.copy`, like this:

> `> stop in Jompanion.copy
> Breakpoint set in Jompanion.copy`

When the breakpoint is reached, program execution stops. You can then examine the stack of the current thread using the `where` command or step through the method line by line using the `step` command. A useful command to use as you step through the program is `list`, which shows the current execution point in the program.

To cause Jompanion to reach the breakpoint set in the `copy` method, use the `copy` command to copy selected text in the text area. The debugger should print out the following:

> `Breakpoint hit: Jompanion.copy (Jompanion:244)
> AWT-Callback-Win32[1]`

This output tells you the method that reached the breakpoint is called `Jompanion.copy`, the parent program is called Jompanion, and the line number where the breakpoint was reached is 244. Notice that on the Windows system used in the example, the prompt changed to `AWT-Callback-Win32[1]`, which is the current thread. All commands issued to the debugger with this new prompt are in the context of the `AWT-Callback-Win32` thread.

Using the `where` command, you can see what the stack looks like:

> `AWT-Callback-Win32[1] where
> [1] Jompanion.copy (Jompanion:244)
> [2] Jompanion.handleEvent (Jompanion:187)
> [3] java.awt.Component.postEvent (Component:838)
> [4] java.awt.MenuComponent.postEvent (MenuComponent:94)
> [5] java.awt.MenuComponent.postEvent (MenuComponent:94)
> [6] java.awt.MenuComponent.postEvent (MenuComponent:94)
> [7] sun.awt.win32.MMenuItemPeer.action (MMenuItemPeer:50)
> AWT-Callback-Win32[1]`

The stack shows you a picture of the steps the program took to get to the breakpoint. In this case, you selected a menu item, which posted an event that must be handled by the event handler. The event handler in turn called the `copy` method, and the breakpoint was reached. You can move up and down the stack using the `up` and `down` commands.

Other breakpoint-related commands include `cont`, used to continue execution of the program after the breakpoint; `resume`, used to resume execution of the current thread; and `clear`, used to clear a breakpoint.

Note
Keep in mind that the Java stack is organized according to the last in, first out methodology (LIFO). Because the last item in the stack is the first to pop off it when execution resumes, you need to read the stack in reverse order. Therefore, to trace the path to the current event in time order, start at the bottom of the stack and work to the top.

Using the `list` command, you can see exactly where you are in the source code:

> `AWT-Callback-Win32[1] list
> 240 textArea.requestFocus();
> 241 }
> 242
> 243 //copies selected text to the buffer
> 244 => public void copy() {
> 245 copyString = textArea.getSelectedText();
> 246 textArea.requestFocus();
> 247 }
> 248
> AWT-Callback-Win32[1]`

The output of the `list` command tells you where you are in the program. In the previous listing, the program stopped on line number 244. You can then step through the current method line by line using the `step` command.

### Examining Instance Variables

By examining the instance variables in your program, you can check their values and ensure that they are set within the ranges you expect. Generally, you will want to check the status of instance variables at a specific point during the program's execution, so it's a good idea to set a breakpoint in a method whose instance variables you want to check. Before you can examine methods and classes used in your application, you need to start a debugging session.

This section demonstrates how to attach the debugger to an applet. If you want to follow along, change to the directory containing the `ScrollText` class files. (The `ScrollText` applet was created in Chapter 15, "Creating Java-Powered Web Presentations with Applets.") Next, create a new HTML document or use an existing one that references the `ScrollText` applet, such as the following:

> `<HTML>
> <HEAD>
> <TITLE>ScrollText Applet</TITLE>
> </HEAD>
> <BODY>
> <APPLET CODE="ScrollText.class" WIDTH=500 HEIGHT=300>
> </APPLET>
> </BODY>
> </HTML>`

Then start the `ScrollText` applet with the applet viewer:

> `appletviewer -debug ScrollText`

Your system should display output similar to the following:

> `appletviewer -debug test.html
> Initializing jdb...
> 0x13a41e0:class(sun.applet.AppletViewer)
> >`

Finally, start the `ScrollText` applet using the `run` command:

> `> run`

Your system should display output similar to the following:

> `run sun.applet.AppletViewer test.html
> running ...
> main[1]`

Now that the applet is started, you can use the `dump` command to check the status of a specified object's instance variables:

> `main[1] dump ScrollText
> ScrollText = 0x13a7ea8:class(ScrollText) {
> superclass = 0x13a7eb8:class(java.applet.Applet)
> loader = null
> interfaces:
> 0x13a2f70:interface(java.lang.Runnable)
> static final LayoutManager panelLayout = (java.awt.FlowLayout)0x13a7738
> }`

The `dump` listing tells you many things about the state of the class or object to which it relates. For the `ScrollText` class, the `dump` tells you the superclass is `java.applet.Applet`, the loader is null, the applet uses the runnable interface, and the layout setting is for `FlowLayout`. Provided that local variables are used in your program and are available, you can also check the status of local variables using the `locals` command:

> `main[1] locals
> Local variables and arguments:
> this = ScrollText[3000,3000,0x0,layout=java.awt.FlowLayout]`

You can set a breakpoint in the applet to check instance variables related to a specific method at a given point in time. Because the `scroll` method plays a key role in how the `ScrollText` applet works, it's a good idea to check variables in this method. First, place the breakpoint; then dump the variable you want to check, as in the following:

> `main[1] stop in ScrollText.scroll
> Breakpoint set in ScrollText.scroll
> main[1]
> Breakpoint hit: ScrollText.scroll (ScrollText:83)
> Thread-2[1] dump dist
> this.dist = -480`

Using the `cont` command, you can resume execution of the program. Then, when the next breakpoint is reached, you can dump the variable you want to check again, as in the following:

> `Thread-2[1] cont
> Thread-2[1]
> Breakpoint hit: ScrollText.scroll (ScrollText:83)
> Thread-2[1] dump dist
> this.dist = -481`

## Identifying Trouble with the Code

Debugging your code is not a straightforward process, so there are many ways to do it. There are two key methods used by Java programmers; one is highly interactive and one is mostly noninteractive. Just as you use the debugger to provide a highly interactive way to find out the status of variables and what the program is doing at a particular line of code, you can use the program itself to tell you what it's doing at any given time in a primarily noninteractive way simply by inserting `print` statements at key points in the logic of the program.

By adding `print` statements to key methods, you can ensure that the methods are executing. You can check the sequence of events that lead to the method being called and which methods are called after the method executes. Usually, this type of `print` statement simply echoes the name of the method being executed, as in the following:

> `System.out.println("In the run method.");`

By placing `print` statements in conditional loops, you can see what conditions are met on the basis of the current state of the program. You can then alter the program's state and see if other conditions of the loop are met. Usually, this type of `print` statement simply echoes the condition you are checking for, as in the following:

> `System.out.println("No parameter, using default.");`

By placing `print` statements before or after values associated with a variable are changed, you can ensure the value associated with the variable is what you expect it to be when called and after the change occurs. Usually, this type of `print` statement describes the variable and displays the value, such as

> `System.out.println("The location parameter is set to: " + x);`

After you use this mostly noninteractive method of debugging for a while, you may see why many programmers prefer it. The key concept is that you insert the `print` statements to help you debug and then either comment them out or delete them before you provide the finished version of the program.

The only bad thing about this method of debugging is that tracking the `print` statements that display to your screen is sometimes difficult, especially if your command window doesn't have a scroll feature. To make this style of debugging easier, you may want to create a utility to aid the debugging process, such as a window that displays all your debugging messages in reverse time order so you always see the most current messages but also have access to all previous messages.

Putting your debugging messages in a more usable and trackable format is exactly what the `HelpDebug` application shown in Listing 23.1 does. The application creates a window to which you can pass input and thus log your debugging information. The heart of the application is an overloaded method called `out`. The first version of the `out` method enables you to pass strings that you want displayed in the debugging window, the second enables you to display values associated with objects, and the final version enables you to pass values associated with integers.

> Listing 23.1. The `HelpDebug` application.

> `/**
> * The Complete Guide to HelpDebug Application
> * This application creates a window to log debugging information.
> */
> import java.applet.Applet;
> import java.awt.*;
> import java.util.*;
> public class HelpDebug extends Frame {
> TextArea textArea;
> void out(String str) {
> textArea.insertText(str + '\n',0);
> }
> void out(Object obj) {
> textArea.insertText(String.valueOf(obj) + '\n',0);
> }
> void out(int x) {
> textArea.insertText("The value of the integer is:" + x + '\n',0);
> }
> HelpDebug() {
> super("HelpDebug Output Window");
> pack();
> reshape(200,0,300,300);
> show();
> textArea = new TextArea();
> textArea.setText("[New Debug Session]");
> setLayout(new BorderLayout());
> add("Center",textArea);
> }
> public boolean handleEvent(Event e) {
> if (e.id == Event.WINDOW_DESTROY) {
> dispose();
> return true;
> }
> else return super.handleEvent(e);
> }
> }`

Before you can use the `HelpDebug` application, type in and compile the code or access the source code for the application on the CD-ROM. Then you need to let your application or applet know the `HelpDebug` application exists by doing the following:

1. Import the `HelpDebug` class:
   `import HelpDebug;`
2. Create a container for the `debug` object:
   `HelpDebug debug;`
3. Create an instance of the `HelpDebug` object in the `main()` method of an application or the `init()` method of an applet:
   `debug = new HelpDebug();`

When your program can access `HelpDebug`, you can direct your debugging output to the `HelpDebug` application itself. You do this by calling the `out()` method of the object you created in step 3, like this:

> `debug.out("The height parameter as read in is:" + hs);`

To show you how to use this method of debugging and gain practice with the `HelpDebug` class, I'll modify the `ScrollText` applet. I'll do this by directing debugging statements to `HelpDebug`'s `out()` method. As you examine the modified applet shown in Listing 23.2, look at how the debugging statements are added, where they are added, and what they output.

> Listing 23.2. The modified `ScrollText` applet with debugging.

> `import java.awt.Graphics;
> import java.awt.Font;
> import HelpDebug;
> /**
> * The Complete Guide to Java ScrollText Applet with Debugging information
> * This applet is used to scroll a text banner across the screen
> * The applet takes TEXT, WIDTH, and HEIGHT as parameters.
> */
> public class ScrollText extends java.applet.Applet implements Runnable {
> int h; // Height of applet in pixels
> int w; // Width of applet in pixels
> char separated[]; // Output string in array form
> String s = null; // Input string containing display text
> String hs = null; // Input string containing height
> String ws = null; // Input string containing width
> Thread ScrollThread = null; // Thread to control processing
> int speed=35; // Length of delay in milliseconds
> boolean threadSuspended = false;
> int dist;
> HelpDebug debug; //container for the debug object
> /* Setup width, height, and display text */
> public void init() {
> debug = new HelpDebug();
> ws = getParameter ("width");
> debug.out("The width parameter as read in is:" + ws);
> hs = getParameter ("height");
> debug.out("The height parameter as read in is:" + hs);
> if (ws == null){ // Read width as input
> w = 150; // If not found use default
> } else {
> w = Integer.parseInt(ws); // Convert input string to integer
> }
> if (hs == null){ // Read height as input
> h = 50; // If not found use default
> } else {
> h = Integer.parseInt (hs); // Convert input string to integer
> }
> resize(w,h); // Set font based on height
> setFont(new Font("TimesRoman",Font.BOLD,h - 6));
> s = getParameter("text");// Read input text, if null use default
> if (s == null) {
> s = " The Java ScrollText Applet at work.";
> debug.out("No text parameter, using default");
> }
> separated = new char [s.length()];
> s.getChars(0,s.length(),separated,0);
> }
> /* Start new thread to run applet */
> public void start() {
> if(ScrollThread == null)
> {
> debug.out("Thread is null");
> ScrollThread = new Thread(this);
> ScrollThread.start();
> }
> }
> /* End thread containing applet */
> public void stop() {
> debug.out("In the stop method.");
> ScrollThread = null;
> }
> // While applet is running pause then scroll text
> public void run() {
> debug.out("In the run method.");
> while (ScrollThread != null) {
> try {Thread.sleep(speed);} catch (InterruptedException e){}
> scroll();
> }
> ScrollThread = null;
> }
> // Scroll text by determining new location to draw text and redrawing
> synchronized void scroll () {
> debug.out("In the scroll method.");
> dist--; // Move string to left
> debug.out(dist);
> // If string has disappeared to the left, move back to right edge
> if (dist + ((s.length()+1)*(h *5 / 11)) == 0){
> dist=w;
> }
> repaint();
> }
> // Redraw string at given location
> public void paint(Graphics g) {
> g.drawChars(separated, 0, s.length(), dist,4 *h / 5);
> }
> // Suspend thread when mouse is pushed, resume when pushed again
> public boolean mouseDown(java.awt.Event evt, int x, int y) {
> debug.out("Detected mouseDown event");
> if (threadSuspended) {
> debug.out("Resuming Thread…");
> ScrollThread.resume();
> }
> else {
> debug.out("Stopping Thread…");
> ScrollThread.suspend();
> }
> threadSuspended = !threadSuspended;
> return true;
> }
> }`

Before you can use the modified applet, you have to type in and compile the source code or access the source code for the modified applet on the CD-ROM. You also need to create an HTML document that displays the applet, such as the one shown in Listing 23.3. This document is also available on the CD-ROM.

> Listing 23.3. The HTML document used to display the applet.

> `<HTML>
> <HEAD>
> <TITLE>Applet for Debugging</TITLE>
> </HEAD>
> <BODY>
> <APPLET CODE="ScrollText.class" WIDTH=500 HEIGHT=300>
> <PARAM NAME=TEXT VALUE="Applet Debugging Example…">
> </APPLET>
> </BODY>
> </HTML>`

After creating the necessary files, you can run the applet using the applet viewer or your Web browser. Figure 23.1 shows the applet and the debugging window. Note that the debugging output displayed in the `HelpDebug` window is in reverse time order, which ensures that the most current debugging information is always displayed at the top of the window.

Figure 23.1 : Using the HelpDebug application.

## Summary

Debugging logic problems in your code is more difficult than debugging syntax problems. Therefore, you need a set of advanced tools to aid the debugging process. When you want to debug your code in a highly interactive way, use the Java debugger. The Java debugger is one of the most advanced tools in the Java Developer's Kit and, as such, has many useful features for debugging your code. When you want to debug your code in a mostly noninteractive way, you may want to use `print` statements and possibly a utility that helps you track them. The `HelpDebug` utility presented in this chapter is designed to help you do just that.