Chapter 12

Debugging with the Visual J++ Debugger

by Bryan Morgan


CONTENTS

One of the most important and useful tools included with Visual J++ is its integrated visual debugger. This debugger supports conditional and unconditional breakpoints, Watch windows for examining variables at runtime, and a Call Stack window for examining the order in which methods are called within a program. All of these tools make full use of the Windows user interface and are extremely easy to learn and use.

This chapter introduces the many tools that make up the Visual J++ debugging system and presents their configuration and use. Instead of simply presenting the wide variety of dialog boxes, windows, and menu options, this chapter focuses on the fundamental knowledge (such as breakpoints, variables, watches, and call stacks) that is required for debugging. In the course of the explanation of these topics, the specific Visual J++ graphical user interface (GUI) items associated with a topic are shown.

Using Breakpoints to Examine Program Execution

The simplest form of debugging is to print out messages to the screen informing you of the status of variables or a method call within a program. Although this has the advantage of keeping you informed of the internal operations of a program, it is very inflexible and limiting. Whenever you need to examine a new item, you must stop the program, remove println statements, and add new println statements where needed. Using this method completely bypasses the wealth of debugging tools available in the Visual J++ product. Instead of printing a variable's value out to the screen, you could perform this same operation with two mouse clicks using Visual J++ and breakpoints.

Breakpoints are "stop signs" at lines of code within a program that inform the runtime interpreter to halt execution at the breakpoint location. (In fact, some IDEs even display breakpoints using a tiny stop sign in the left margin!) When the program has stopped execution at a breakpoint, the source code of the class containing the breakpoint displays in the Visual J++ editor. At this point, you can use the Visual J++ GUI to examine variables, view which methods have been called and in what order, and then step through the code one line at a time. All of this can be done using either the keyboard or the mouse.

NOTE
If you have had a bad experience using the Sun Java Development Kit jdb debugger, do not be intimidated by the Visual J++ debugger. Whereas the jdb debugger is a character-based, command-line tool, Visual J++ offers a rich set of GUI tools that can be used to examine code at runtime. In other words, the goal of the Visual J++ debugger is to provide an extremely low learning curve so that the developer can instead focus on the task at hand: finding and removing bugs in code.

Because of their importance, breakpoints can be set using a variety of methods within Visual J++.

Setting Breakpoints

The simplest way to set a breakpoint at a line of source code is to use the right mouse button pop-up menu selection Insert/Remove Breakpoint. This option adds a breakpoint at the line of code selected. A breakpoint is denoted by a small red circle in the left margin of the editor window. In Figure 12.1 you can see three breakpoints set in the editor window.

Figure 12.1 : Breakpoint symbols in Visual J++.

Using the same pop-up menu, you can right-click a line containing a breakpoint to either disable or remove the breakpoint completely. When the pop-up menu appears, you can choose to either Remove Breakpoint or Disable Breakpoint. Disabling a breakpoint leaves a red circle outline in the left margin. Disable a breakpoint when you would like to temporarily go past it but still leave a marker denoting where the breakpoint was set for future sessions. After a breakpoint has been disabled, it can be enabled once again by selecting the pop-up menu option Enable Breakpoint.

One other simple way to set and remove breakpoints is to use the Project. (The Project toolbar can be seen in Figure 12.1 just below the Standard toolbar containing the File Save and File Open buttons.) Holding the mouse over any of the toolbar buttons will display a ToolTip explaining what that button is used for. To add or remove a breakpoint from a line of code, simply position the cursor at the code line of interest, and click on the toolbar button containing the hand icon. (Pressing the f9 key will produce the same result.) Clicking the button containing the crossed-out hands will remove all breakpoints within a file.

Breakpoints can also be controlled from the Edit main menu. Selecting the Edit | Breakpoints... menu option will produce the Breakpoints dialog box shown in Figure 12.2.

Figure 12.2 : The Breakpoints dialog box.

Clicking the Break At: right-arrow button will produce a list containing the current line in the editor window and the Advanced... word. Selecting the current line will add a breakpoint at that point in the editor. (This is equivalent to selecting the Add Breakpoint pop-up menu option, clicking the Add Breakpoint toolbar button, or pressing the f9 key.)

Setting Advanced Breakpoints

Selecting the Advanced... option brings up the Advanced Breakpoints dialog box (see Figure 12.3).

Figure 12.3 : The Advanced Breakpoints dialog box.

The Advanced Breakpoints dialog box allows you to set breakpoints at specific lines of code within a class in the current source file or any source file. If the source file is not located in the current project workspace directory, you must enter the full pathname and filename in the Source file: text box.

Setting Conditional Breakpoints

After selecting a line of code where you want to set the breakpoint, click the Condition... button (refer to Figure 12.2) in the Breakpoints dialog box to produce the Breakpoint Condition dialog box (see Figure 12.4).

Figure 12.4 : The Breakpoint Condition dialog box.

The expression to be evaluated can be a boolean expression or can return a normal value. The following expressions are valid within this text field:

If the result of the expression is a boolean value (true or false), the debugger will stop when the expression evaluates to true. If the result of the expression is any other type of value, the debugger will stop execution whenever the value changes. In instances (such as within a loop) in which the debugger may stop hundreds or thousands of times, this dialog box also conveniently lets you set the number of times the expression should be skipped while debugging. If the value is set to 0, the debugger will stop each time the condition is met. If the value is set to 99, the debugger will stop each 100th time the condition is met.

You can set or remove breakpoints using the following methods:

After a breakpoint has been set, the program can be run in debug or normal mode. If the program is not run in debug mode, the breakpoints will be ignored. If the program is run in debug mode, program execution will be suspended when it encounters a line of code containing a breakpoint. At this point, you can choose to continue or step through the code.

Stepping Through Code at a Breakpoint

After a breakpoint has been set, the program must be run in debug mode for debugging to take place. This can be done using the following operations:

Note that selecting the Build | Execute menu option will run the program but will not run it in debug mode.

NOTE
As soon as a Java program begins to execute in Visual J++, the Build menu will change to the Debug menu. The Debug menu contains a number of commonly executed debugging commands, all of which are duplicated elsewhere using toolbar buttons or pop-up menu options.

After program execution begins, whenever a breakpoint is encountered, the program will be suspended temporarily while awaiting your commands. At this point, you can undertake several actions depending on the task at hand. After examining variables (which are discussed in the section "The Variables Window"), you can continue to run the application, step through the code one line at a time, step into each method encountered on each line, or jump to the current cursor location on a line of code located later in the class.

Controlling Program Execution at a Breakpoint

The easiest of these operations is to continue with the execution of the program after it has been stopped at a breakpoint. Pressing f5, selecting the Debug | Go menu option, or selecting the Go button from the Project toolbar will cause the program to continue with its normal execution. You can also stop or restart the execution.

To stop execution of a program while debugging, select the Debug | Stop Debugging menu option, press Alt+f5, or select the Stop Debugging button on the Debug toolbar. To view the Debug toolbar, right-click any toolbar to bring up its pop-up menu and then select the Debug option.

There may be times when you want to completely restart the execution of a program. This occurs often when a variable or code has been modified at runtime and the program needs to be restarted to take advantage of this modification. To restart the execution of a program, select the Debug | Restart menu option, press Shift+f5, or select the Restart button on the Debug toolbar.

Stepping Through Code at Runtime

More commonly when execution stops at a breakpoint, you will need to examine the behavior of a block of code or a method at runtime. Because this behavior cannot be truly observed by examining variables at a breakpoint, you'll need to step through the code one statement at a time to observe what is actually going on "under the hood." The Visual J++ debugger allows you to do this. Execution will continue just as it normally would have (except at a much slower pace!). By using the debugger to step through code, you can see exactly how the code is being run by the computer. The following sections explain the different ways you can step through code using the Visual J++ debugger.

Step Over

The Step Over process forces the debugger to execute all method calls and operations in a single line of code and then to step to the next line of code in the current source file. Take the following two lines of code as an example:


USSenate.AddPolitician(BobDole);

USSenate.AddPolitician(StromThurmond);

If the first line of code is current when Step Over is selected, program execution immediately transfers to the second line of code without stepping into the AddPolitician() method.

You can use the Step Over option by doing any of the following:

There are times when you will need to examine a method within a line of code in more detail. Visual J++ also allows you to step into a method.

Step Into

The Step Into process forces the debugger to step into any method calls that are contained at the current line of code. For instance, imagine that the current line of code is the following:


USSenate.AddPolitician(BobDole);

Selecting the Step Into debugging option will cause the source file containing the AddPolitician() method to be loaded. The AddPolitician() method is the current method in the Visual J++ editor, and the program execution symbol (denoted by a yellow arrow in the left margin of the code) is located at the first line of code in the AddPolitician() method.

You can select the Step Into option by doing any of the following:

Stepping into each method within each line of code allows you to thoroughly view everything that is going on as the program executes. When you're satisfied with the operation of the method you stepped into, you can step out immediately using the Step Out Of option.

Step Out Of

When you have stepped into a method using the Step Into option, you do not have to step through every single line of code within that method before continuing with the program's normal execution. The Step Out Of option allows you to return to the original line of code that contains the method that was stepped into.

You can select the Step Out Of option by doing any of the following:

In addition to simply stepping through single lines of code or stepping into a method for one line of code, you can run and step over blocks of code quickly using the Run to Cursor option.

Run to Cursor

The Run to Cursor option allows you to position the cursor on a line of code somewhere beneath the currently executing line of code. When the Run to Cursor option is selected, all lines of code between the current line and the cursor will be executed immediately. Program execution will halt again when the line of code containing the cursor is encountered.

You can select the Run to Cursor option by doing any of the following:

This concludes the discussion of breakpoints and stepping through code at runtime. Although this capability is extremely important to help understand how a program is actually operating while running, developers gain the most value from being able to examine program variables while running the program. Visual J++ provides numerous capabilities that allow you to view variables or code at runtime.

Viewing and Modifying Values at Runtime

The Visual J++ debugger duplicates the functionality found in other Microsoft tools (notably Visual Basic and Visual C++) by providing the capability to view program values at runtime. Using tools such as the Watch window and the Variables window, you can actually see values change on the screen as lines of code are stepped through. This section introduces a variety of tools for examining variable values at runtime, starting with the Watch window.

The Watch Window

By now, you are probably familiar with three commonly used dockable windows in Visual J++: the Project Workspace, InfoViewer Topic, and Output windows. These windows are said to be dockable because they can be dragged to different corners of the screen and docked there. The Watch window allows you to specify variables and expressions to be watched during a program's execution. The Watch window displays variables in a spreadsheet window made up of multiple rows and two columns. In each row is a variable to be watched and its associated value(s). The first column is used to store the variable's name. The second column contains that variable's current value. Figure 12.5 illustrates the use of the Watch window while debugging.

Figure 12.5 : The Watch window.

Note that variables that are Java objects contain a small + sign to the left of their name. Clicking on the + will expand the properties of the class and show the values of each of that class's variables. In the example shown in Figure 12.5, the object Politics.Politician contains several variables (including another object, Name). To add a new variable to the Watch list, simply click in the first open row beneath the currently listed rows and type a variable's name (see Figure 12.6).

Figure 12.6 : Adding a new variable to the Watch list.

After that variable has been added, you can watch its current value in the Watch window as you step through lines of code within the program.

Removing Variables from the Watch List

The operation required to remove a variable from the Watch window list is very easy. Simply select the row in the Watch spreadsheet containing the variable to be removed and press the Delete key. This will remove the variable from the Watch list.

Modifying Variables Within the Watch List

Just as you can add and remove variables in the Watch window list, you can modify the actual values of these variables. This is useful when you know that a bad value has been returned from a method but you would like program execution to continue just to see what happens. Before continuing, simply modify the value to a "correct" value and continue stepping through the program with the new value.

To modify a variable's value within the Watch window, double-click the value to be modified and type in the new value. This works well for numeric and boolean values, but you are somewhat restricted when modifying String values. This is because a String is actually a class in Java (not a basic data type), and therefore is internally made up of an array of individual characters. Therefore, you must modify each character one at a time before the String itself is modified. In a more generic sense, to modify any object in the Watch window, you must modify that object's individual data members one at a time. The Watch window also contains a set of tabs (Watch1, Watch2, Watch3, and Watch4) that can be used to add related groups of Watch items to a list.

The Variables Window

Like the Watch window, the Variables window is a dockable window available only at debug time. The purpose of the Variables window is to display all variables that are in use in the program's current context (see Figure 12.7).

Figure 12.7 : The Variables window.

In general, this includes parameters passed to methods, local variables declared within a method, and class variables that are used within the current method. Although the Variables window may sound similar to the Watch window, it differs in several ways. First, new variables cannot be added to the Variables window, and current variables cannot be deleted from it. In addition, whereas the Watch window allows you to watch expressions as well as variables, the Variables window deals only with variables. However, the Variables window will allow you to modify the values of variables within the window using the same methodology used in the Watch window (double-click or tab, then alter the variable).

Whereas the Watch window contains multiple tabs at the bottom of the window for logically grouping sets of variables or expressions together, the Variables window contains a set of tabs that are used for a different reason. These Variables window tabs (Auto, Local, and this) are used to display different levels of information about the application variables.

The Auto tab page displays all the variables used in the current statement and the next statement as well. The Local tab page displays all the variables used in the current method. The this tab page displays variable information about the object referenced by the this keyword.

NOTE
In the case of a debugging session in the main() method, the this tab page contains the error Error: symbol "this" not found. This is because the main() method is a public, static, void method that is not associated with any object.

When you right-click a variable name and select Properties, a Properties dialog box appears containing the type, name, and value of the current variable. This is an easy way to determine the type of a variable at runtime.

You can also move to a method's source code by selecting the appropriate method within the Variable window's Context box (refer to Figure 12.7). The Context box shows the available methods within a program. Note that changing the selected method in the Context box does not physically change the program's stack or register values; it simply allows you to view the source code of a specific method on the screen and updates the Variables list based on the selected method.

Using DataTips

Tips are small boxes that pop up over an item on a toolbar, within an outline, or within an editor. In the small box is textual information that may be of interest to the user. When debugging, DataTips are available to the programmer. DataTips are enabled by holding the mouse over a variable for a half-second or so. The DataTips box that pops up contains the current value of that variable. Figure 12.8 shows a variable being examined during debugging using DataTips. Using DataTips, you can determine that the value of the retValue variable is false at the current time.

Figure 12.8 : Using DataTips while debugging.

Using QuickWatch

You can use Visual J++'s QuickWatch feature to view the value of a single variable without using the Variables window or the Watch window. When stepping through source code using the debugger, right-click a variable to bring up the pop-up menu. Selecting QuickWatch will bring up the dialog box shown in Figure 12.9. The QuickWatch dialog box can also be displayed by clicking the QuickWatch button in the Debug toolbar (the eyeglasses icon).

Figure 12.9 : The QuickWatch dialog box.

Clicking the Recalculate button will refresh the variable's value in the QuickWatch dialog box. To modify the value of the variable, double-click on that variable's value and change it to the proper new value. Selecting the Add Watch button will add this variable to the Watch window.

By examining variables using the Watch and Variables windows, you can see the value of multiple program variables while the program is running. In addition, the value of these variables is continually updated as you step through the program using the debugger's step functionality.

Using Drag-and-Drop Between Windows

To copy variables between windows (such as the Watch and Variables windows), click a variable to select and, while holding the left mouse button down, drag that variable into the desired window. After lifting the left mouse button, that variable will be copied between the windows and its value updated in both windows as it changes. Because it is not possible to add values to the Variables window, drag-and-drop is most commonly used to drag values from the Variables window into the Watch window for permanent display.

Viewing the Program Call Stack

The call stack in a program is a stack containing currently active method calls. To view the call stack of an executing Java program within Visual J++, use the Call Stack window.

The Call Stack Window

The Call Stack window can be displayed during program execution using a variety of methods. To display the Call Stack window while debugging a program, do one of the following:

The Call Stack shows, in most recently active order, the list of currently active method calls (see Figure 12.10).

Figure 12.10 : The Call Stack window.

The Call Stack window is helpful to determine which methods are calling a method within a program. This is useful in a language such as Java that supports polymorphism. In situations in which a superclass's methods have been overridden within a newly created class, the new class's methods can be called by other methods within the superclass. Because this can be confusing to the unsuspecting developer, the Call Stack window allows you to determine how or why a method was called (and by whom).

To modify the display of the methods in the Call Stack window, two options can be toggled using the right mouse button pop-up menu:

Double-clicking on a method name within the Call Stack window will cause the source code of the method to be displayed. You can also do this by selecting the Go To Code pop-up menu option in the Call Stack window.

You can use standard debugging commands in the Call Stack window as well. Because each method within the window is being currently executed, each method has an associated currently executing line of code. Clicking the Insert/Remove Breakpoint, Enable Breakpoint, or Run To Cursor pop-up menu options will produce the expected results at that method's currently executing line of code.

Debugger Use of the Output Window

As you may have noticed, the debugger continually outputs messages to the Output window's Debug page as new objects are created (see Figure 12.11).

Figure 12.11 : Debugger output in the Output window.

The purpose of this output is to give you some idea as to what objects have been loaded in memory. This output is particularly useful when debugging multithreaded applications because it is used by the debugger to show when specific threads have started or stopped.

Setting Global Debug Options

Many of the options available in the various debug windows can be set globally for all future debugging sessions. This is done by accessing the Options dialog box from the Tools | Options main menu option. Selecting the Debug tab in this dialog box will display the dialog box shown in Figure 12.12.

Figure 12.12 : The Options dialog box (Debug tab).

Setting any of the options in this dialog box automatically sets the option to the desired value during the next debugging session.

Debugging the Java Class Libraries

As you delve deeper into Java programming, times will arise when you will be interested in seeing what is going on beneath the surface. Many objects (such as strings, buttons, frames, and threads) are actually declared within the java packages included as part of the Java Base API. If no source code is available for the classes included with these packages, the debugger will be unable to step into methods within these classes. (By default, the standard Visual J++ installation does not install the source code.)

If you have the Visual J++ CD-ROM handy, simply run the Setup program and select the Custom Installation option. Selecting Install Java Class Library Source Code will install the source code for all the standard Java classes to your system. When this has been done, the debugger can step into each of the class's source codes where needed. If you do not have the Visual J++ CD-ROM available, it is possible to extract the source code from the CLASSES.ZIP file using either a ZIP decompression program (such as WINZIP32) or the JAVASRC tool supplied with Visual J++. The JAVASRC.EXE program should be located in the same directory as your CLASSES.ZIP file (in the \WINDOWS\JAVA\CLASSES directory).

If you're using WINZIP, extract all files ending in .java from the CLASSES.ZIP file. Otherwise, re-create the directory structure and the source files included with CLASSES.ZIP by running the following command from the MS-DOS prompt:


javasrc classes.zip

When this is done, the source code for all the classes included in the sun, java, and com packages can be examined and stepped into while debugging.

Summary

Visual J++ carries on the tradition of Microsoft's family of visual tools (Visual C++ and Visual Basic) by providing a graphical, integrated debugging system for the Java developer. This debugger supports the use of breakpoints, the capability to step through code, and the use of windows to display variable and call stack information.

Variables can be examined at runtime using the Variables and Watch windows. The Watch window gives you the benefit of examining expressions as well as the capability to modify variables on-the-fly while a program is executing. The Call Stack window provides a graphical display of the currently active methods calls within a program. Different options within the Call Stack window allow you to view the data types of parameters passed to a method as well as the actual value of the parameters passed to a method. Several quick tools are available, including DataTips and the QuickWatch window. These tools allow you to quickly examine the contents of variables at runtime by simply moving or clicking the mouse once over a variable.

After becoming acquainted with the Visual J++ developing environment, new Java developers should familiarize themselves with the Visual J++ debugger. Time spent learning these tools will be well worth it the first time you are forced to track down a particularly difficult bug.

This concludes the discussions of the Visual J++ development environment, compiler, and debugger. The remainder of Part II, "The Visual J++ Development Tools," examines the standard Java packages (java.awt, java.applet, java.io, and so on) and introduces GUI programming using the Java Abstract Windowing Toolkit. At the completion of this discussion, you will have been thoroughly introduced to all of the basics of Java programming using Visual J++.