Java Technology Home Page
A-Z Index

Java Developer Connection(SM)
Online Training

Downloads, APIs, Documentation
Java Developer Connection
Tutorials, Tech Articles, Training
Online Support
Community Discussion
News & Events from Everywhere
Products from Everywhere
How Java Technology is Used Worldwide
 
Training Index

Writing Advanced Applications
Chapter 7 Continued: Running Tests and Analyzing

[<<BACK] [CONTENTS] [NEXT>>]

If you are still having problems even after you have ruled out installation and environment problems and included debugging code, it is time to use tools to test and analyze your program.


Getting Behind the Seat with jdb

Although there are some very good integrated development environment (IDE) tools on the market, the JavaTM debugger tool, jdb and its successors have an important role to play in testing and debugging programs. Some advantages of jdb over IDE tools are it is free, it is platform independent (some IDE tools are not), and it runs as a separate process to the program it is debugging. The benefit to jdb running as a separate process is you can attach a debug session to a running program.

The downsides to using jdb are it has only a command-line interface, and it relies on the same code you are trying to debug. This means if there is a bug in the Java VM, jdb could break attempting to diagnose that same bug!

The new JBUG architecture was created to solve these problems in jdb. JBUG, amongst other things, provides a debugger helper API in the Java VM called the Java VM Debug Interface (JVMDI). This helper communicates with the debugging front end using the Java Debug Wire Protocol (JDWP). The debugging front end uses the remote Java Debug Interface (JDI) to send and receive commands over the Java Debug Wire Protocol. JBug is available for Java 2 platforms, and has a jdb style front end that you will learn more about later.

Simple jdb Test Drive

Back to the classic jdb tool. Here are some simple steps to analyze a program using jdb. This first example debugs a program from application startup. The Remote Debugging example shows how to connect to a running program.

Start the Session

To begin the debug session, compile the SimpleJdbTest.java program with full debugging information using javac and the -g debug flag as shown. In this example, the SimpleJdbTest.java program is an application, but it could just as well be an applet. The procedures for debugging applications with jdb are the same for debugging applets once the debug session has started.

javac -g SimpleJdbTest.java
Next, start the jdb tool with the program class name as a parameter:
jdb SimpleJdbTest
Initializing jdb...
0xad:class(SimpleJdbTest)

To debug an applet in appletviewer use the -debug parameter as in this example:

$ appletviewer -debug MyApplet.html
Initializing jdb...
0xee2f9808:class(sun.applet.AppletViewer)
> 

Setting a Breakpoint and Listing Methods

At this point, the SimpleJdbTest class has only been loaded; the class constructor has not been called. To make jdb stop when the program is first instantiated, put a stop, or breakpoint, at the constructor using the stop in command. When the breakpoints has been set, instruct jdb to run your program using the run command as follows:
stop in SimpleJdbTest.<init>
Breakpoint set in SimpleJdbTest.<init>
run
run SimpleJdbTest
running ...
main[1]
Breakpoint hit: SimpleJdbTest.<init> 
                  (SimpleJdbTest:10)

The jdb tool stops at the first line in the constructor. To list the methods that were called to get to this breakpoint, enter the where command:

main[1] where
[1] SimpleJdbTest.<init> (SimpleJdbTest:10)
[2] SimpleJdbTest.main (SimpleJdbTest:29)

The numbered method in the list is the last stack frame that the Java VM has reached. In this case the last stack frame is the SimpleJdbTest constructor that was called from SimpleJdbTest main.

Whenever a new method is called, it is placed on this stack list. The Hotspot technology achieves some of its speed gains by eliminating a new stack frame when a new method is called. To gain a general appreciation of where the code has stopped, enter the list command.

main[1] list
6 		Panel p;
7 		Button b;
8 		int counter=0;
9
10 		SimpleJdbTest() {
11 			setSize(100,200);
12 			setup();
13 		}
14 		void setup (){

Locating the Source

If the source to the class file stopped in is not available on the current path, you can tell jdb to find the source with the use command by giving it the source directory as a parameter. In the following example the source is in a subdirectory or folder called book.

main[1] list
Unable to find SimpleJdbTest.java
main[1] use book
main[1] list
6 		Panel p;
7 		Button b[];
8 		int counter=0;
9 
10 	=> 	SimpleJdbTest() {

Looking at a Method

To see what happens in the setup method for SimpleJdbText, use the step command to step through the 4 lines to get to it.

main[1] step
main[1]
Breakpoint hit: java.awt.Frame.<init> (Frame:222)

But wait a minute! This is now the Frame class constructor! If you keep stepping you follow the Frame Constructor and not the SimpleJdbText class. Because SimpleJdbTest extends the Frame class, the parent constructor, which in this case is Frame, is called on your behalf.

The step up Command

You could continue stepping and eventually you will return to the SimpleJdbTest constructor, but to return immediately, you can use the step up command to go back to the SimpleJdbTest constructor.

main[1] step up
main[1]
Breakpoint hit: SimpleJdbTest.<init> 
                  (SimpleJdbTest:8)

The next Command

You can also use the next command to get to the setup method. In this next example, the jdb tool has approximated that the source line is outside the constructor when it processed the last step up command. To return to the constructor, use another step command, and to get to the setup method, use a next command. To debug the setup method, you can step through the setup method.

main[1] step
Breakpoint hit: SimpleJdbTest.<init> 
                  (SimpleJdbTest:11)
main[1] list
7 		Button b[]=new Button[2];
8		int counter=0;
9
10 		SimpleJdbTest() {
11 			setSize(100,200);<
12 			setup();
13 		}
14 		void setup (){
15 			p=new Panel();
16		}
main[1] next
Breakpoint hit: SimpleJdbTest.<init> 
                  (SimpleJdbTest:12)
main[1] step
Breakpoint hit: SimpleJdbTest.setup (SimpleJdbTest:15)

The stop in Command

Another way to get to the setup method is to use the stop in SimpleJdbTest.setup command. You can list the source again to check where you are:

main[1] list
11 			setSize(100,200);
12 			setup();
13 		}
14 		void setup (){
15	=>		p=new Panel();
16 			  b[0]= new Button("press");
17 			  p.add(b[0]);
18 			  add(p);
19

The print Command

The first thing the setup method does is create a Panel p. If you try to display the value of p with the print p command, you will find that the value is null.

main[1] print p
p = null

This occurred because the line has not been executed and so field p has not been assigned a value. You need to step over that assignment operation with the next command and then use the print p command again.


main[1] next

Breakpoint hit: SimpleJdbTest.setup (SimpleJdbTest:16)
main[1] print p
p = java.awt.Panel[panel0,0,0,0x0,invalid,
		     layout=java.awt.FlowLayout]

Setting Breakpoints on Overloaded Methods

Although stepping through small classes is fast, as a general rule on larger applications, it is often a lot faster to set breakpoints. This is partly because jdb has a very simple command set and no shortcuts, so each command has to be pasted or typed in full.

To set a breakpoint in the Button class, use stop in java.awt.Button.<init>


main[1] stop in java.awt.Button.<init>
java.awt.Button.<init> is overloaded,
          use one of the following:
void <init>
void <init>java.lang.String)

The message explains why jdb cannot stop in this method without more information, but the message is slightly misleading as you do not need to specify the return type for overloaded methods, you just need to be explicit about exactly which one of the overloaded methods you want to stop in. To stop in the Button constructor that creates this Button, use stop in java.awt.Button.<init>java.lang.String).

The cont Command

To continue the jdb session, use the cont command. The next time the program creates a Button with a String as the constructor, jdb stops so you can examine the output.


main[1] cont
main[1]
Breakpoint hit: java.awt.Button.<init> 
                          (Button:130)

If the Button class had not been recompiled with debug information as described earlier, you would not see the internal fields from the print command.

Clearing Breakpoints

To clear this breakpoint and not stop every time a Button is created use the clear command. This example uses the clear command with no arguments to display the list of current breakpoints, and the clear command with the java.awt.Button:130. argument to clear the java.awt.Button:130. breakpoint.

main[1] clear
Current breakpoints set:
SimpleJdbTest:10
java.awt.Button:130
main[1] clear java.awt.Button:130
Breakpoint cleared at java.awt.Button: 130

Displaying Object Details

To display details about an object, use the print command to call the object's toString method, or use the dump command to display the object's fields and values.

This example puts a breakpoint at line 17 and uses the print and dump commands to print and dump the first Button object in the array of Button objects. The dump command output has been abbreviated.

main[1] stop at SimpleJdbTest:17
Breakpoint set at SimpleJdbTest:17
main[1] cont
main[1]
Breakpoint hit: SimpleJdbTest.setup (SimpleJdbTest:17)

main[1] print b[0]
b[0] = java.awt.Button[button1,0,0,0x0,invalid,
					label=press]
main[1] dump b[0]
b[0] = (java.awt.Button)0x163 {
private int componentSerializedDataVersion = 2
boolean isPacked = false
private java.beans.PropertyChangeSupport 
			changeSupport = null
long eventMask = 4096
transient java.awt.event.InputMethodListener 
			inputMethodListener = null
....
java.lang.String actionCommand = null
java.lang.String label = press
}

Ending the Session

That finishes the simple jdb examples. To terminate the jdb session, use the quit command:

0xee2f9820:class(SimpleJdbTest)
> quit

Remote Debugging

The jdb tool is an external process debugger, which means it debugs the program by sending messages to and from a helper inside the Java VM. This makes it is easy to debug a running program, and helps you debug a program that interacts with the end user. A remote debug session from the command-line does not interfere with the normal operation of the application.

Starting the Session

Before the Java 2 release, the only thing required to enable remoted debugging was to start the program with the -debug flag as the first argument, and if the application uses native libraries, make the library name end in _g. For example, you would need to copy nativelib.dll to nativelib_g.dll to debug with that library.

In Java 2, things are a little more complicated. You need to tell the Java VM where the tools.jar file is by using the CLASSPATH variable. The tools.jar file is normally found in the lib directory of the Java platform installation.

You also need to disable the Just In Time (JIT) compiler if one exists. The JIT compiler is disabled by setting the java.compiler property to NONE or to an empty string. Finally, as the -classpath option overrides any previously set user classpath, you also need to add the CLASSPATH needed by your application.

Putting all of this together, here is the command line needed to start a program in remote debug mode. Put this all on one line and include all the classes you need on the command line.

Windows:

$ java -debug -classpath C:\java\lib\tools.jar;. 
-Djava.compiler=NONE SimpleJdbTest
Agent password=4gk5hm

Unix:

$ java -debug -classpath /usr/java/lib/tools.jar:. 
-Djava.compiler=NONE SimpleJdbTest
Agent password=5ufhic

The output is the agent password (in this case, 4gk5hm) if the program was successfully started. The agent password is supplied when starting jdb so jdb can find the corresponding application started in debug mode on that machine.

To start jdb in remote debug mode, supply a host name, which can be either the machine where the remote program was started or localhost if you are debugging on the same machine as the remote program, and the agent password.

jdb -host localhost -password 4gk5hm

Listing Threads

Once inside the jdb session, you can list the currently active threads with the threads command, and use the thread <threadnumber> command, for example, thread 7 to select the thread to analyze. Once the thread is selected, use the where command to see which methods have been called for this thread.

$ jdb -host arsenal -password 5ufhic
Initializing jdb...
> threads
Group system:
1. (java.lang.Thread)0x9        Signal dispatcher  
			       cond. waiting
2. (java.lang.ref.Reference     0xb Reference Handler
      $ReferenceHandler)       cond. waiting
3. (java.lang.ref.              Finalizer
      Finalizer                cond. waiting
      $FinalizerThread)0xd            
						
4. (java.lang.Thread)0xe       Debugger agent     
		              running    
5. (sun.tools.agent.           Breakpoint handler
      Handler)0x10            cond. waiting
6. (sun.tools.agent.           Step handler
      StepHandler)0x12        cond. waiting
Group main:
7. (java.awt.                  AWT-EventQueue-0 
       EventDispatchThread)   cond. waiting 
       0x19 					         
8. (sun.awt.                    PostEventQueue-0 
      PostEventQueue)0x1b      cond. waiting          
9. (java.lang.Thread)0x1c       AWT-Motif        
			       running                
10. (java.lang.Thread)0x1d      TimerQueue       
			       cond. waiting          
11. (sun.awt.                   Screen Updater
       ScreenUpdater)0x1f      cond. waiting          
12. (java.lang.Thread)0x20      Thread-0         
			       cond. waiting          
> thread 7
AWT-EventQueue-0[1] where
  [1] java.lang.Object.wait (native method)
  [2] java.lang.Object.wait (Object:424)
  [3] java.awt.EventQueue.getNextEvent 
                            (EventQueue:179)
  [4] java.awt.EventDispatchThread.run 
	         (EventDispatchThread:67)

Listing Source

To list the source, the thread needs to be suspended using the suspend command. To let this thread continue use the resume command. The example uses resume 7.


AWT-EventQueue-0[1] suspend 7
AWT-EventQueue-0[1] list
Current method is native
AWT-EventQueue-0[1] where
  [1] java.lang.Object.wait (native method)
  [2] java.lang.Object.wait (Object:424)
  [3] java.awt.EventQueue.getNextEvent 
                            (EventQueue:179)
  [4] java.awt.EventDispatchThread.run 
		 (EventDispatchThread:67)
AWT-EventQueue-0[1] resume 7

Ending the Session

When you finish debugging this program remotely, clear any remaining breakpoints before quitting the debug session. To get a list of remaining breakpoints use the clear command, and to remove them enter clear class:linenumber as follows:

main[1] clear
Current breakpoints set:
SimpleJdbTest:10

main[1] clear SimpleJdbTest:10
main[1] quit

Using Auto-Pilot

One little known trick with jdb is the jdb startup file. jdb automatically looks for a file called jdb.ini in the user.home directory. If you have multiple projects, it is a good idea to set a different user.home property for each project when you start jdb. To start jdb with a jdb.ini file in the current directory, type the following:

jdb -J-Duser.home=.

The jdb.ini file lets you set up jdb configuration commands, such as use, without having to enter the details each time jdb runs. The following example jdb.ini file starts a jdb session for the FacTest class. It includes the Java platform sources on the source path list and passes the parameter 6 to the program. It then runs and stops at line 13, displays the free memory, and waits for further input.

load FacTest
stop at FacTest:13
use /home/calvin/java:/home/calvin/jdk/src/
run FacTest 6
memory
Here is the output from the jdb.ini file execution:
$ jdb -J-Duser.home=/home/calvin/java
Initializing jdb...
0xad:class(FacTest)
Breakpoint set at FacTest:13
running ...
Free: 662384, total: 1048568
main[1]
Breakpoint hit: FacTest.compute (FacTest:13)
main[1]
You might wonder if jdb.ini files can be used to control an entire jdb session. Unfortunately, commands in a jdb.ini startup file are executed synchronously, and jdb does not wait until a breakpoint is reached before executing the next command. This makes printing variables awkward. You can add artificial delays with repeated help commands, but there is still no guarantee the thread will be suspended when you need it to be.

Creating a Session Log

You can use a little-known jdb feature to obtain a record of your debug session. The output is similar to what you see when you run jdb -dbgtrace.

To enable jdb logging, create a file called .agentLog in the directory where you are running jdb or java -debug. In the .agentLog file, put the file name that you want the session information to be written to on the first line. For example, an .agentLog file would have these contents:

jdblog

When you next run jdb or java -debug, you will see jdb session information as shown below. You can use this information to retrieve the breakpoint hits and the commands entered if you need to reproduce this debug session.


---- debug agent message log ----
[debug agent: adding Debugger agent to 
system thread list]
[debug agent: adding Breakpoint handler 
to system thread list]
[debug agent: adding Step handler to 
system thread list]
[debug agent: adding Finalizer to 
system thread list]
[debug agent: adding Reference Handler to 
system thread list]
[debug agent: adding Signal dispatcher to 
system thread list]
[debug agent: Awaiting new step request]
[debug agent: cmd socket: 
Socket[addr=localhost/127.0.0.1,
port=38986,localport=3 8985]]
[debug agent: connection accepted]
[debug agent: dumpClasses()]
[debug agent: no such class: HelloWorldApp.main]
[debug agent: Adding breakpoint bkpt:main(0)]
[debug agent: no last suspended to resume]
[debug agent: Getting threads for HelloWorldApp.main]

[TOP]


[ This page was updated: 13-Oct-99 ]

Products & APIs | Developer Connection | Docs & Training | Online Support
Community Discussion | Industry News | Solutions Marketplace | Case Studies
Glossary - Applets - Tutorial - Employment - Business & Licensing - Java Store - Java in the Real World
FAQ | Feedback | Map | A-Z Index
For more information on Java technology
and other software from Sun Microsystems, call:
(800) 786-7638
Outside the U.S. and Canada, dial your country's AT&T Direct Access Number first.
Sun Microsystems, Inc.
Copyright © 1995-99 Sun Microsystems, Inc.
All Rights Reserved. Legal Terms. Privacy Policy.