Debugging JAVA With DDD Under UNIX

Note: This web page contains a lot of screen dumps. Thus, it will likely take a long time to print out. Also, you may see lots of blank space on the printed page because the print routine will not normally split images across pages.

Why use a debugger?

You carefully designed your code, got it to compile, and now it does not work correctly. What should you do? The first method most people use is to add a lot of print statements. This method requires recompiling your code and often takes many tries to see what you want. Using a debugger is a much more efficient method of doing this work. Debuggers allow one, among other things, to examine variables, change the values, and control the execution of the program.

Keep in mind that a debugger isn't a substitute for thinking. It is important to reason through how things may have happened and to choose the test data intelligently.

What is DDD?

DDD is a graphical interface that allows debugging of many different types of code. It supports C++, JAVA, perl, and other choices. There are several advantages to DDD. First, once you learn it for one language you can use the same techniques on other languages. Second, its graphical interface is much easier to use then most of the other Unix debuggers. This will make your life easier (and it will be more like the PC if you are used to that!).

Note that DDD is an interface to debuggers. Thus, it depends on another program to actually execute the debugging commands. In the case of JAVA, DDD uses the program jdb. The bad news is that jdb is relatively new and still has bugs and limited capabilities. As a result, failures in jdb cause DDD to report errors. For simple operations this is not too common so DDD with JAVA is still very useful. If you do encounter an error then try to be patient with the situation. Additionally, if you read the DDD documentation you will see lots of advanced and interesting features. Many of them are not yet available for JAVA due to jdb limitations. These are generally noted somewhere in the manual (and some in this documentation). All this should improve as jdb is upgraded.

DDD on the Unix machines uses X windows to display its windows. As a result, you need to have X windows running and capable of creating new windows at your console. If you are in doubt, try doing ("%" is the Unix prompt I will use - your prompt is likely to differ):

% xterm

and see if a new window pops up with a Unix prompt. If not, you need to fix this up first to use DDD.

Sample code

For this discussion, I will use a simple code that tries to copy one array to another and then change the values of the copied array. The code is available (use shift-click on the link to download from some browsers) and shown here:

public class test {
    public static void main(String args[]) {

	final int SIZE = 3; // size of arrays
	int [] A = new int[SIZE];
	int [] B = new int[SIZE];
	int i;

	System.out.println("Begin test");

	// set array A[i] = i+1
	for (i = 0; i < A.length; i++) {
	    A[i] = i + 1;
	}
	// Set the values of B to be the same as A.
	// actually causes an alias problem
	B = A;
	// make B[i] = 2 * (i + 1) which is 2 times current value
	for (i = 0; i < B.length; i++) {
	    B[i] = 2 * B[i];
	}
	// print results
	for (i = 0; i < A.length - 1; i++) {
	    System.out.print("A[" + i + "] = " + A[i] + ", ");
	}
	System.out.println("A[" + i + "] = " + A[i]);
	for (i = 0; i < B.length - 1; i++) {
	    System.out.print("B[" + i + "] = " + B[i] + ", ");
	}
	System.out.println("B[" + i + "] = " + B[i]);

	System.out.println("Ending test");
    }
}

 

If you do:

% javac test.java

and then

% java test

you will see:

Begin test

A[0] = 2, A[1] = 4, A[2] = 6

B[0] = 2, B[1] = 4, B[2] = 6

Ending test

The intention of the programmer was that array A would have A[0] = 1, A[1] = 2, A[2] = 3 and array B would have B[0] = 2, B[1] = 4, B[2] = 6. However, both arrays have the same values (the ones that were expected in array B). The programmer is confused by this output so using DDD is a good way to figure out what is going on.

Using DDD on test.java

To debug a program it is important that it be compiled with the debug option. In Unix it is customary to use the "-g" flag to do this. Thus, for this program you should do:

% javac -g test.java

Be sure that if you compiled the code before without this flag you recompile the code if you want to see what is shown here. If you forget to do this you won't be able to look at variable values and other operations in DDD.

To debug test using ddd do:

% ddd -jdb test &

The & at the end puts this task in the background. This means that the prompt (%) will return right away and not wait for the task to finish. Note that it is very important that you give the "-jdb" flag. By default, DDD will run in C++ mode. As a result, things will look very strange. After you enter the above command, a new window should now pop up and look like this:

Another small window with tips may also pop up. If it does, you can choose close. Also note that it takes a few seconds for DDD to start up. You can tell when DDD is busy doing something because the box in the lower right hand corner will blink.

The first thing I want to check out is if the values of array A are being set correctly. To do this I am going to stop the code after the values of A are set and see what they are. Thus, I put a breakpoint at line 17 (A = B;). Keep in mind that the code stops before it executes the line where the breakpoint is located. To do this I right click the mouse in the area to the left of the line I want the breakpoint at. When I do this the following appears:

and I choose the "Set Breakpoint" option from the menu. A "stop sign" logo shows up to the left of the line where the breakpoint is set. In addition, a message shows up in the bottom part of the window to indicate that a breakpoint has been set. Finally, the line where you clicked shows up in an area just below the top of window. Overall it looks like:

Now we are ready to run the code to see what happens. To do this click the "run" button on the "DDD" menu that is to the right of the code. (As with most DDD commands there are several ways to do the same thing. For example, you could choose "Program->Run".) The code now runs and stops at line 17 where the breakpoint is located. The window now looks like:

A few things to note. First, there is a bold arrow pointing to the line where the code is currently stopped at. This is the same line with the stop sign since ddd should have stopped at the breakpoint. The bottom part of the window now has more output. It has "Begin test" which is the output from the JAVA code. It also puts out the message "running ..." when the code began. Finally, it says that the code stopped at line 17 for the breakpoint. The next thing to do is to see what values A currently holds. The easiest way to see the values of a variable is to touch it with the cursor arrow. After a second the values associated with the variable will appear in a yellow box. Note that if the variable is not in scope then no value will be displayed. I point to the "A" on line 17 (I can point at it anywhere) I get a box with "{ 1, 2, 3 } { }". This indicates that the values of array A are A[0] = 1, A[1] = 2, A[3] = 3 which is what we wanted. Also note that the values show up at the very bottom of the window. If I put the cursor arrow on B I get "{ 0, 0, 0 } { }" which indicates B is all zero as expected since no values have been set to it yet.

The next thing I want to do is see if the values of B are set properly after line 17 executes. To do this I push the "Next" button on the DDD menu. "Next" causes the code to execute the current line of code. "Step" is similar but if a function executes on that line the debugger will go to the first line of the function. Notice that if you put the cursor arrow over a button and wait a second a yellow box pops up to give you a short clue of what the button does. The same information is also displayed at the bottom of the window. This is like displaying the value of a variable but now it gives you help on a button. After I hit the "Next" button the window looks like:

Notice the bold arrow has moved to line 19 and the message near the bottom of the window shows that DDD stopped at line 19. Now if I touch B I see it has the same values as A, i.e., {1, 2, 3}. Touching A verifies these values are still the same. Everything seems fine. Next I want to see what happens as the values of B are changed. To do this I hit next to go to line 20 and then hit it again to go back to line 19. It goes back to line 19 since we are in a loop. Now when I touch B I see the values come up as "{ 2, 2, 3 } { }". If I touch "i" I see the value is zero. This makes sense: the index is zero and the old value of B at index zero is doubled from 1 to 2. Now if I touch A I see its values are the same as B, that is, "{ 2, 2, 3 } { }". This is wrong. I did not want the value of A to change too. Somehow when I change B I also change A. If I continue to do next and look at values in i, A, and B, I will continue to see A change as B changes. This is the problem. When I did "B = A;" it set the objects to be the same (created an aliased copy) and did not copy the values. Thus, A and B point to the same thing. I need to change this so it copies the values. Code like this would do the trick:

System.arraycopy(A, 0, B, 0, SIZE);

Whenever you are done with DDD you can exit by using the top menu to do "File->Exit".

Now that we have tried DDD to figure this out, we can discuss some more features of DDD. If you want to remove a breakpoint, right click on it and choose "Delete Breakpoint". You can move a breakpoint around by clicking on it with the left mouse and dragging it where you want it to go. Another cool feature is that you can get a display of variables. To do this you can point at an object and right click and then choose "Display". You can also highlight a variable with the left mouse button and then use the right button to choose "Display". When you do this the display shows up near the top of the window. If this is the first display then a new area opens up inside the window. If I do this for B after one time through the loop I see (after resizing the display area):

This shows the same values as if I pointed to B. An advantage of the display is you can have multiple ones at the same time. Thus, you can easily see what is going on with several variables. Note that you can drag the displays around to place them in convenient locations (such as next to each other). Within an area in the window, you scroll if there is more to see. Also, you can go to the right edge where the box and double line is to change the size of part of the window. To resize, put the arrow cursor over the raised box. When it is in the correct position, the cursor will change to a cross pattern and a yellow hint box will pop up that says "Resize window". At this point left click on the box and move it up and down as desired for resizing. This can help give you more space when you display lots of results or have lots of messages at the bottom. Another very nice feature of the display is that values that just changed are highlighted in yellow.

Here are some other features you may use:

There are a few things to note. jdb (which DDD is using) has significant limitations at this time. You cannot do many of the fancy features of DDD in JAVA due to these limitations. Some notable ones are you cannot watch variables, cannot set conditional breakpoints, execute commands automatically at a breakpoint, or print in other than decimal. Note that DDD will sometimes report internal problems with jdb. I don't have any good advice on what to do in this case except to restart it. If you have the option to ignore the error that will sometimes work. To learn more about DDD, use the online help or see the manual (note it is 90 pages long). You will find many other options in DDD as well as some shortcuts for common operations in the documentation. A common one is double clicking with the left mouse button to get the default operation. For example, you can use this to set breakpoints. I have found this works but it is very sensitive to the exact location and the way you click. That is why I use the other method in this document (using the right mouse button and then choosing from the menu).