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: Collecting Evidence

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

The first step in trying to solve any problem is to gather as much evidence and information as possible. If you can picture a crime scene, you know that everything is checked, cataloged and analyzed before any conclusions are reached. When debugging a program, you do not have weapons, hair samples, or fingerprints, but there is plenty of evidence you can gather that might contain or ultimately lead to the solution. This section explains how to gather that evidence.


Installation and Environment

The JavaTM platform is a fast-moving and changing technology. You might have more than one release installed on your system, and those releases might have been installed as part of another products installation. In an environment with mixed releases, a program can experience problems due to changes to the platform in a new version or release.

For example, if classes, libraries, or Windows registry entries from previous installations remain on your system after an upgrade, there is a chance the new software mix is causing your problems and needs to be investigated and ruled out. Opportunities for problems related to mixed software releases have increased with the use of different release tools to deliver the Java platform software.

The section on Version Issues at the end of this chapter provides a complete list of major Java platform release and version information to help you rule out software release issues. This next section highlights the most common problems you are likely to encounter.

Class Path

In the Java 2 platform, the CLASSPATH environment variable is needed to specify the application's own classes only, and not the Java platform classes as was required in earlier releases. So it is possible your CLASSPATH environment variable is pointing at Java platform classes from earlier releases and causing problems.

To examine the CLASSPATH, type the following at the command line:

Windows 95/98/NT:
echo %CLASSPATH%

Unix Systems:
echo $CLASSPATH

Java classes are loaded on a first come, first served basis from the CLASSPATH list. If the CLASSPATH variable contains a reference to a lib/classes.zip file, which in turn points to a different Java platform installation, this can cause incompatible classes to be loaded.


Note: In the Java 2 platform, the system classes are chosen before any class on the CLASSPATH list to minimize the possibility of any old broken Java classes being loaded instead of a Java 2 class of the same name.

The CLASSPATH variable can get its settings from the command line or from configuration settings such as those specified in the User Environment on Windows NT, an autoexec.bat file, or a shell startup file like .cshrc on Unix.

You can control the classes the Java1 Virtual Machine (VM) uses by compiling your program with a special command-line option that lets you supply the CLASSPATH you want. The Java 2 platform option and parameter is -Xbootclasspath classpath, and earlier releases use -classpath classpath and -sysclasspath classpath. Regardless of which release you are running, the classpath parameter specifies the system and user classpath, and zip or Java ARchive (JAR) files to be used in the compilation.

To compile and run the Myapp.java program with a system CLASSPATH supplied on the command line, use the following instructions:

Windows 95/98/NT:

In this example, the Java platform is installed in the C:\java directory. Type everything on one line:

javac -J-Xbootclasspath:c\java\lib\tools.jar;c:
\java\jre\lib\rt.jar;c:\java\jre\lib\i18n.jar;.  
  Myapp.java

You do not need the -J runtime flag to run the compiled Myapp program, just type the following on one line:

java -Xbootclasspath:c:\java\jre\lib\rt.jar;c:
\java\jre\lib\i18n.jar;.  Myapp

Unix Systems:

In this example, the Java platform is installed in the /usr/local/java directory. Type everything on one line:

javac -J-Xbootclasspath:/usr/local/java/lib/tools.jar:
/usr/local/java/jre/lib/rt.jar:
/usr/local/java/jre/lib/i18n.jar:. Myapp.java

You do not need the -J runtime flag to run the compiled Myapp program, just type the following on one line:

java -Xbootclasspath:/usr/local/java/jre/lib/rt.jar:
/usr/local/java/jre/lib/i18n.jar:. Myapp

Class Loading

Another way to analyze CLASSPATH problems is to locate where your application is loading its classes. The -verbose option to the java command shows which .zip or .jar file a class comes from when it is loaded. This way, you will be able to tell if it came from the Java platform zip file or from some other application's JAR file.

For example, an application might be using the Password class you wrote for it or it might be loading a Password class from an installed integrated development environment (IDE) tool.

You should see each jar and zip file named as in the example below:


$ java -verbose SalesReport
[Opened /usr/local/java/jdk1.2/solaris/jre/lib/rt.jar 
        in 498 ms]
[Opened /usr/local/java/jdk1.2/solaris/jre/lib/i18n.jar 
        in 60 ms]
[Loaded java.lang.NoClassDefFoundError from 
	/usr/local/java/jdk1.2/solaris/jre/lib/rt.jar]
[Loaded java.lang.Class from 
	/usr/local/java/jdk1.2/solaris/jre/lib/rt.jar]
[Loaded java.lang.Object from 
	/usr/local/java/jdk1.2/solaris/jre/lib/rt.jar]

Including Debug Code

A common way to add diagnostic code to an application is to use System.out.println statements at strategic locations in the application. This technique is fine during development, providing you remember to remove them all when you release your product. However, there are other approaches that are just as simple, do not affect the performance of your application, and do not display messages that you do not want your customers to see. The following are two techniques that overcome the problems with simple System.out.println statements.

Turning Debug Information On at Runtime

The first alternative to the classic println debug statements is to turn on debugging information at runtime. One advantage to this is you do not need to recompile any code if problems appear at the testing stage or on a customer site.

Another advantage is that sometimes software problems can be attributed to race conditions where the same segment of code behaves unpredictably due to timing between other program interactions. If you control your debug code from the command line instead of adding println debug statements, you can rule out sequence problems caused by race conditions coming from the println code. This technique also saves you adding and removing println debug statements and having to recompile your code.

This technique requires you to use a system property as a debug flag and include application code to test that system property value. To turn on debug information from the command line at run time, start the application and set the debug system property to true as follows:

java -Ddebug=true TestRuntime

The source code for the TestRuntime class needs to examine this property and set the debug boolean flag as follows:


public class TestRuntime {
    boolean debugmode; //global flag that we test

    public TestRuntime () {

       String dprop=System.getProperty("debug");

       if ((dprop !=null) && (dprop.equals("yes"))){
            debugmode=true;
       }

       if (debugmode) {
           System.err.println("debug mode!");
       }
    }
}

Creating Debug and Production Releases at Compile Time

As mentioned earlier, one problem with adding System.out.println debug statements to your code is finding and removing them before you release the product. Apart from adding unnecessary code, println debug statements can contain information you do not want your customers to see.

One way to remove System.out.println debug statements from your code is to use the following compiler optimization to remove pre-determined branches from your code at compile time and achive something similar to a debug pre-processor.

This example uses a static dmode boolean flag that when set to false results in the debug code and the debug test statement being removed. When the dmode value is set to true, the code is included in the compiled class file and is available to the application for debugging purposes.


class Debug {

  //set dmode to false to compile out debug code
  public static final boolean dmode=true;
}

public class TestCompiletime {

  if (Debug.dmode) {                       // These 
    System.err.println("Debug message");   // are 
    }                                      // removed
}

Using Diagnostic Methods

You can use diagnostic methods to request debug information from the Java VM. The following two methods from the Runtime class trace the method calls and Java VM byte codes your application uses. As both these methods produce a lot of output, it is best to trace very small amounts of code, even as little as one line at a time.

To enable trace calls so you will see the output, you have to start the Java VM with the java_g or java -Xdebug interpreter commands.

To list every method as it is invoked at runtime, add the following line before the code you wish to start tracing and add a matching traceMethodCalls line with the argument set to false to turn the tracing off. The tracing information is displayed on the standard output.

// set boolean argument to false to disable
Runtime.getRuntime().traceMethodCalls(true);
callMyCode();
Runtime.getRuntime().traceMethodCalls(false);
To see each line as bytecodes as they are executed, add the following line to your application code:
// set boolean argument to false to disable
Runtime.getRuntime().traceInstructions(true);
callMyCode();
Runtime.getRuntime().traceInstructions(false);

You can also add the following line to your application to dump your own stack trace using the dumpStack method from the Thread class. The output from a stack trace is explained in Analyzing Stack Traces, but for now you can think of a stack trace as a snapshot of the current threads running in the Java VM.

Thread.currentThread().dumpStack();

Adding Debug Information

Local variable information is not included in the core Java platform system classes. So, if you use a debug tool to list local variables for system classes where you place stop commands, you will get the following output, even when you compile with the -g flag as suggested by the output. This output is from a jdb session:
main[1] locals
No local variables: try compiling with -g

To get access to the local variable information, you have to obtain the source (src.zip or src.jar) and recompile it with a debug flag. You can get the source for most java.* classes with the binary downloads from java.sun.com.

Once you download the src.zip or src.jar file, extract only the files you need. For example, to extract the String class, type the following at the command line:

unzip /tmp/src.zip src/java/lang/String.java
or

jar -xf /tmp/src.jar src/java/lang/String.java

Recompile the extracted class or classes with the -g option. You could also add your own additional diagnostics to the source file at this point.

javac -g src/java/lang/String.java

The Java 2 javac compiler gives you more options than just the original -g option for debug code, and you can reduce the size of your classes by using -g:none, which gives you on average about a 10 percent reduction in size.

To run the application with the newly compiled debug class or classes, you need to use the bootclasspath option so these new classes are picked up first.

Type the following on one line with a space before myapp.

Win95/NT Java 2 Platform:

This example assumes the Java platform is installed in c:\java, and the source files are in c:\java\src:

jdb -Xbootclasspath:c:\java\src;c:\java\jre\lib\rt.jar;c:
\java\jre\i18n.jar;.  myapp

Unix Systems:

This example assumes the Java platform is installed in c:\java, and the source files are in c:\java\src.

jdb -Xbootclasspath:/usr/java/src;
/usr/java/jre/lib/rt.jar;
/usr/java/jre/i18n.jar;.  myapp

The next time you run the locals command you will see the internal fields of the class you wish to analyze.

[TOP]

_______
1 As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.


[ 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.