Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Debugging a Java Application with dbx: Industrial-Strength Debugging for Your Java Code

 
By Ann Rice  

Sun ONE[tm] Studio, Compiler Collection dbx is an interactive, source-level, command-line debugging tool. You can use it to run a program in a controlled manner and to inspect the state of a stopped program. dbx gives you complete control of the dynamic execution of a program.

Using dbx With Java Code

You can use dbx to debug an application written in C, C++, or Fortran. You can also, with some limitations, debug an application that is a mixture of Java[tm] code and C JNI (Java[tm] Native Interface) code or C++ JNI code, including code developed with the Native Connector tool (NCT).

Capabilities of dbx With Java Code

You can debug the following types of Java applications with dbx:

  • A file with a file name that ends in .class
  • A file with a file name that ends in .jar
  • A Java application that is started using a wrapper
  • A running Java application that was started in debug mode to which you attach dbx
  • A C application or C++ application that embeds a Java application using the JNI_CreateJavaVM interface

Most dbx commands operate similarly on native code and Java code.

Limitations of dbx With Java Code

dbx has the following limitations when debugging Java code:

  • dbx cannot tell you the state of a Java application from a core file as it can with native code.
  • dbx cannot tell you the state of a Java application if the application is hung for some reason and dbx is not able to make procedure calls.
  • The fix and continue, runtime checking, and performance data collection features of dbx do not apply to Java applications.

Debugging a Class File

You can debug a file that uses the .class file name extension.

(dbx) debug myclass.class

If the class that defines the application is defined in a package, you need to include the package path just as when running the application under the JVM software.

(dbx) debug java.pkg.Toy.class

You can also use a full path name for the class file. dbx automatically determines the package portion of the class path by looking in the .class file and adds the remaining portion of the full path name to the class path.

(dbx) debug /home/user/java/pkg/Toy.class

Debugging a JAR File

A Java application can be bundled in a JAR (Java Archive) file. You can debug a JAR file using dbx.

(dbx) debug myjar.jar

When you start debugging a file that has a file name ending in .jar, dbx uses the Main_Class attribute specified in the manifest of this JAR file to determine the main class. (The main class is the class within the JAR file that is your application's entry point. If you use a full path name or relative path name to specify the JAR file, dbx uses the directory name and prefixes it to the class path in the Main-Class attribute.

If you debug a JAR file that does not have the Main-Class attribute, you can use the JAR URL syntax jar:<url>!/{entry} that is specified in the class JarURLConnection of the Java[tm] 2 Platform, Standard Edition to specify the name of the main class.

(dbx) debug jar:myjar.jar!/myclass.class
(dbx) debug jar:/a/b/c/d/e.jar!/x/y/z.class
(dbx) debug jar:file:/a/b/c/d.jar!/myclass.class

For each of these examples dbx would do the following:

  • Treat the class path specified after the ! character as the main class (for example, /myclass.class or /x/y/z.class)
  • Add the name of the JAR file ./myjar.jar, /a/b/c/d/e.jar, or /a/b/c/d.jar to the class path
  • Begin debugging the main class

If you have specified a custom startup of the JVM software using the jvm_invocation environment variable (see Customizing Startup of the JVM Software, the file name of the JAR file is not automatically added to the class path In this case, you must add the file name of the JAR file to the class path when you start debugging.

Debugging a Java Application That Has a Wrapper

A Java application usually has a wrapper to set environment variables. If your Java application has a wrapper, you need to tell dbx that a wrapper script is being used by setting the jvm_invocation environment variable (see Customizing Startup of the JVM Software).

Attaching dbx to a Running Java Application

You can attach dbx to a running Java application if you specified the options shown in the following example when you started the application. After starting the application, you would use the dbx command with the process ID of the running process to start debugging.

$ java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent myclass.class
$ dbx - 2345

For the JVM software to locate libdbx_agent.so, you need to add installation_directory/SUNWspro/lib to LD_LIBRARY_PATH before running the Java application, where install_directory is the location where dbx was installed. If you are using the 64-bit version of the JVM software, you need to add installation_directory/SUNWspro/lib/v9 to LD_LIBRARY_PATH.

When you attach dbx to the running application, dbx starts debugging the application in Java mode.

If your Java application requires 64-bit object libraries, include the -d64 option when you start the application. Then when you attach dbx to the application, dbx will use the 64-bit JVM software on which the application is running.

$ java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent -d64 myclass.class
$ dbx - 2345

Debugging a C Application or C++ Application That Embeds a Java Application

You can debug a C application or C++ application that embeds a Java application using the JNI_CreateJavaVM interface. The C application or C++ application must start the Java application by specifying the following options to the JVM software:

-Xdebug -Xnoagent -Xrundbx_agent

For the JVM software to locate libdbx_agent.so, you need to install_directory /current/lib to LD_LIBRARY_PATH before running the Java application, where install_directory is the location where dbx was installed. If you are using the 64-bit version of the JVM software, you need to add install_directory /current/lib/v9 to LD_LIBRARY_PATH.

Passing Arguments to the JVM Software

When you use the run command in Java mode, the arguments you give are passed to the application and not to the JVM software. To pass arguments to the JVM software, see Customizing Startup of the JVM Software.

Specifying the Location of Your Java Source Files

Sometimes your Java source files are not in the same directory as the .class or .jar files. You can use the $JAVASRCPATH environment variable to specify the directories in which dbx should look for Java source files. For example JAVASRCPATH=.:/mydir/mysrc:/mydir/mylibsrc:/mydir/myutils causes dbx to look in the listed directories for source files that correspond to the class files being debugged.

Specifying the Location of Your C Source Files or C++ Source Files

dbx might not be able to find your C source files or C++ source files in the following circumstances:

  • If your source files are not in the same location as they were when you compiled them
  • If you compiled your source files on a different system than the one on which you are running dbx and the compile directory does not have the same path name

In such cases, use the pathmap command to map one path name to another so that dbx can find your files.

Specifying a Path for Class Files That Use Custom Class Loaders

An application can have custom class loaders that load class files from locations that might not be part of the regular class path. In such situations dbx cannot locate the class files. The CLASSPATHX environment variable lets you specify to dbx a path for the class files that are loaded by their custom class loaders. For example, CLASSPATHX=.:/myloader/myclass:/mydir/mycustom causes dbx to look in the listed directories when it is trying to locate a class file.

Setting Breakpoints on Code That Has Not Yet Been Loaded by the JVM Software

To set a stop breakpoint on a Java method in a class file that has not been loaded by the JVM software, use the full name of the class with a stop in command, or the class name with a stop inmethod command.

(dbx) stop in Java.Pkg.Toy.myclass.class.mymethod
(dbx) stop inmethod myclass.class.mymethod

To set a stop breakpoint on a C function or C++ function in a shared library that has not been loaded by the JVM software, preload the symbol table of the shared library before setting the breakpoint.

(dbx) loadobject -load fullpathto/mylibrary.so
(dbx> stop in myfunc

You can also load the symbol tables of all dynamically loaded shared objects by running your application once before beginning to debug it with dbx.

Customizing Startup of the JVM Software

You might need to customize startup of the JVM software from dbx to do the following:

You can customize startup of the JVM software using the jvm_invocation environment variable. By default, when the jvm_invocation environment variable is not defined, dbx starts the JVM software as follows:

java -Xdebug -Xnoagent -Xrundbx_agent:syncpid

When the jvm_invocation environment variable is defined, dbx uses the value of the variable to start the JVM software.

You must include the -Xdebug option in the definition of the jvm_invocation environment variable. dbx expands -Xdebug into the internal options -Xdebug -Xnoagent -Xrundbxagent::sync.

If you do not include the -Xdebug option in the definition, dbx issues an error message.


Specifying a Path Name for the JVM Software

To specify a path name for the JVM software, set the jvm_invocation environment variable to the appropriate path name. For example:

jvm_invocation="/myjava/java -Xdebug" 

This causes dbx to start the JVM software as follows:

/myjava/java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent:sync

Passing Run Arguments to the JVM Software

To pass run arguments to the JVM software, set the jvm_invocation environment variable to start the JVM software with those arguments. For example:

jvm_invocation="java -Xdebug -Xms512 -Xmx1024 -Xcheck:jni" 

This causes dbx to start the JVM software as follows:

java -Djava.compiler=NONE -Xdebug -Xnoagent -Xrundbx_agent:sync= -Xms512 -Xmx1024 -Xcheck:jni 

Specifying a Custom Wrapper for Your Java Application

Java application can use a custom wrapper for startup. If your application uses a custom wrapper, you can use the jvm_invocation environment variable to specify the wrapper to be used. For example:

jvm_invocation="/export/siva-a/forte4j/bin/forte4j.sh -J-Xdebug" 

This causes dbx to start the JVM software as follows:

/export/siva-a/forte4j/bin/forte4j.sh - -J-Xdebug -J-Xnoagent -J-Xrundbxagent:sync=process_id 

Using a Custom Wrapper That Accepts Command-Line Options

The following wrapper script (xyz) sets a few environment variables and accepts command line options:

#!/bin/sh
CPATH=/mydir/myclass:/mydir/myjar.jar; export CPATH
JARGS="-verbose:gc -verbose:jni -DXYZ=/mydir/xyz"
ARGS=
 [ $# -gt 0 ] ; do
    case "$1" in
        -userdir) shift; if [ $# -gt 0 ]
; then userdir=$1; fi;;
        -J*) jopt=`expr $1 : '-J<.*>'`
; JARGS="$JARGS '$jopt'";;
        *) ARGS="$ARGS '$1'" ;;
    esac
    shift
done
java $JARGS -cp $CPATH $ARGS

This script accepts some command line options for the JVM software and the user application. For wrapper scripts of this form, you would set the jvm_invocation environment variable and start dbx as follows:

% jvm_invocation="xyz -J-Xdebug -Jany other java options"
% dbx myclass.class -Dide=visual

Using a Custom Wrapper That Does Not Accept Command-Line Options

The following wrapper script (xyz) sets a few environment variables and starts the JVM software, but does not accept any command line options or a class name:

#!/bin/sh
CLASSPATH=/mydir/myclass:/mydir/myjar.jar; export CLASSPATH
ABC=/mydir/abc; export ABC
java <options> myclass

You could use such a script to debug a wrapper using dbx in one of two ways:

  • You could modify the script to start dbx from inside the wrapper script itself by adding the definition of the jvm_invocation variable to the script and starting dbx.
    #!/bin/sh
    CLASSPATH=/mydir/myclass:/mydir/myjar.jar; export CLASSPATH
    ABC=/mydir/abc; export ABC
    jvm_invocation="java -Xdebug "; export jvm_invocation
    dbx myclass.class
    

    Then you would start the debugging session by running the script.

  • You could modify the script slightly to accept some command line options.
    #!/bin/sh
    CLASSPATH=/mydir/myclass:/mydir/myjar.jar; export CLASSPATH
    ABC=/mydir/abc; export ABC
    JAVA_OPTIONS="$1 "
    java $JAVA_OPTIONS $2
    

    Then you would set the jvm_invocation environment variable and start dbx.

    % jvm_invocation="xyz -Xdebug"; export jvm_invocation
    % dbx myclass.class
    

Specifying 64-bit JVM Software

If you want dbx to start 64-bit JVM software to debug an application that requires 64-bit object libraries, include the -d64 option when you set the jvm_invocation environment variable.


dbxModes for Debugging Java Code

When debugging a Java application, dbx is in one of three modes:

  • Java mode
  • JNI mode
  • Native mode

The current mode (java, jni, native) is stored in the environment variable jdbx_mode.

When dbx is Java mode, you can inspect the state of your Java application, including JNI code, and control execution of the code. you interact with dbx using Java syntax and dbx uses Java syntax to present information to you. This mode is used for debugging pure Java code, or the Java code in an application that is a mixture of Java code and C JNI code or C++ JNI code.

In JNI mode, dbx commands use native syntax and affect native code, but the output of commands shows Java-related status as well as native status, so JNI mode is a "mixed" mode. This mode is used for debugging the native parts of an application that is a mixture of Java code and C JNI code or C++ JNI code.

When dbx is in native mode, you can inspect the state of your C or C++ JNI code. In native mode, dbx commands affect only a native program, and all Java-related features are disabled. This mode is used for debugging non-Java related programs.

As you execute your Java application, dbx switches automatically between Java mode and JNI mode as appropriate. For example, when it encounters a Java breakpoint, dbx switches into Java mode, and when you step from Java code into JNI code, it switches into JNI mode.

dbx does not switch automatically into native mode. You can switch explicitly from Java or JNI Mode to native mode with the joff command, and from native mode to Java mode with the jon command.

Using dbx Commands in Java Mode

When you are using dbx to debug a mixture of Java and native code:

  • Some commands accept the same arguments and operate the same way in Java mode or JNI mode as in native mode.
  • Some commands have arguments that are valid only in Java mode or JNI mode, as well as arguments that are valid only in native mode.
  • Some commands that are valid only in Java mode or JNI mode.
  • Some commands work only in native mode.

For lists of the specific commands that fall in each of the above categories, see the "Debugging a Java Application With dbx" chapter of the Debugging a Program With dbx manual. For the Java mode and native mode syntaxes of a specific command, see Appendix C of the manual.

Much of the information about a Java application is normally available only after the JVM software has started, and is unavailable after the Java application has finished executing. However, when you debug a Java application with dbx, dbx gleans some of the information it needs from class files and JAR files that are part of the system class path and user class path before it starts the JVM software. This allows dbx to do better error checking on breakpoints before you run the application.

Some Java classes and their attributes might not be accessible through the class path. dbx can inspect and step through these classes, and the expression parser can access them, once they are loaded. However, the information it gathers is temporary and is no longer available after the JVM software terminates.

Some information that dbx needs to debug your Java application is not recorded anywhere so dbx skims Java source files to derive this information as it is debugging your code.



Related Information


About the Author

Ann Rice is a staff writer in the Sun ONE Studio Technical Documentation group, and is responsible for dbx and the dbx Debugger GUI. Ann began her career as a programmer, developing Fortran and COBOL applications for 12 years before making a transition to technical publications in 1979. She has documented network technology and development tools, as well as serving as a technical publications manager for several Silicon Valley companies, including 3Com Corporation and Sybase, Inc. Ann is a past president of the Silicon Valley chapter of the Society for Technical Communication.