Software Development

Debug your Java code with ease using JPDA

The Java Platform Debugger Architecture (JPDA) helps developers debug a running Java application in all situations. Peter V. Mikhalenko explains how this technology works and discusses some practical aspects of its usage.

Debugging a Java program can be really boring, especially when it's not easy to get access to the running instance. The problem becomes even more difficult when an application runs on a remote environment and does not produce any output on the console or log file. The Java Platform Debugger Architecture (JPDA) technology from Sun helps when you need to debug a running Java application in all situations.

JPDA is a collection of APIs aimed to help with debugging Java code. The JPDA set of tools is available in J2SE starting from the 1.2.2 version, and beginning from 1.3.x, it's included directly into the J2SE package.

It's important to understand that JPDA is not an application or a debugging tool but a set of well-designed and implemented interfaces and protocols. Sun's mission in this standard is to provide an infrastructure, so third-party tools and debuggers can use it in the most efficient way. There are a lot of excellent debuggers and IDEs that use JPDA, including such widely recognized tools as Borland JBuilder, Oracle JDeveloper, IntelliJ IDEA, Sun NetBeans, IBM Eclipse, and many others. However, Sun provides a reference implementation in its traditional command-line debugger jdb, rewritten in Java 1.3 for supporting JPDA. In this article, I will discuss the JPDA technology and some practical aspects of its usage.

How it works

JPDA consists of three interfaces designed for use by debuggers in development environments for desktop systems. The Java Virtual Machine Tools Interface (JVMTI) defines the services a VM must provide for debugging. (JVMTI in Java 5.0 is a replacement for the Java Virtual Machine Debug Interface, which has been deprecated). The Java Debug Wire Protocol (JDWP) defines the format of information and requests transferred between the process being debugged and the debugger front end, which implements the Java Debug Interface (JDI). The JDI defines information and requests at the user code level.

Get developer tips in your inbox
Delivered each Thursday, our free Java newsletter provides insight and hands-on tips you need to unlock the full potential of this programming language.
Automatically sign up today!

The JPDA concept splits the debugging process into two parts: the program that is being debugged ("debuggee") and the JDI, which is normally a user interface of a debugger application (or a part of Java IDE). A debuggee application runs in a back-end part, while the JDI runs in a front-end part. Between the back-end and a front-end there is a communication channel working on top of the JDWP protocol; therefore, the debuggee and the debugger can be located on the same box or on different boxes.

From a developer's point of view, a debugger application may hook into JPDA at any layer. Since the JDI is the highest level and the easiest to use, it's always recommended to use this interface. Suppose a company develops a debugger using JDI. The company can use it with the reference implementation, and it will automatically work with the VMs and the platforms Sun supports, thus most IDE vendors go that way. It can also work, for example, with the reference implementation front-end and a debuggee running another company's VM that implements JDWP (which might use or by-pass JVMTI).

Some debuggers are built on top of lower layers, such as JDWP (for example, if the front-end is not written in Java) or JVMTI (for specialized debuggers which need low-level functionality).

The back-end of the debugger is responsible for communicating requests like "tell me the value of variable X" from the debugger front-end to the debuggee VM and for communicating the response to these requests (including desired events like breakpoint reach) to the front-end. The back-end communicates with the front-end over a communications channel using the JDWP. The back-end communicates with the debuggee VM using the JVMTI.

The communications channel is the link between the front- and back-ends of the debugger. You can think of it as consisting of two mechanisms: a connector and a transport. A connector is a JDI object that is the means by which a connection is established between the front- and back-ends. There can be three types of connectors:

  • listening: The front-end listens for an incoming connection from the back-end.
  • attaching: The front-end attaches to an already running back-end.
  • launching: The front-end actually launches the Java process that will run the debuggee code and the back-end.

A transport is the underlying mechanism used to move bits between the front-end and the back-end. The transport mechanism that must be used is not specified in the JPDA specification; possible mechanisms include: sockets, serial lines, and shared memory. However, the format and semantics of the serialized bit-stream flowing over the channel is specified by the JDWP. Most IDEs and debuggers support two transport types (as does the Sun reference implementation): shared memory (if the debuggee and debugger are located on the same box) and socket (the debuggee and debugger can be located anywhere, including the same box).

Starting with J2SE 5.0, JPDA includes service provider interfaces to allow the development and deployment of connector and transport implementations. These service provider interfaces allow the debugger and other tool vendors to develop new connector implementations and provide additional transport mechanisms over and beyond the socket and shared memory transport provided by Sun.

The communication between the debuggee and debugger is connection oriented; therefore one side must act as a server, listening for a connection. The other side acts as a client and connects to the server. JPDA allows either the debugger application or the target VM to act as the server.

Use JPDA in practice

If you need to use socket transport, identify the type of transport as name dt_socket in the corresponding JVM argument. If the debugger and debuggee are located on the same machine and the box is Windows-based, you can use shared memory connector with the name dt_shmem. If you want to debug your application with a JPDA-compliant debugger, you should just run it with debug mode switched on and pass additional parameters such as transport type, names of hosts and port number, and other information. All JPDA and debugging parameters must be passed as arguments to Java VM at the start of your application.

To enable debugging you should load the JDWP agent for debugging into your application's JVM. Starting from Java 5.0, you can do it with -agentlib:jdwp option. For releases prior to 5.0, the -Xdebug and -Xrunjdwp options are used (the 5.0 implementation also supports the -Xdebug and -Xrunjdwp options, but the newer -agentlib:jdwp option is preferable as the JDWP agent in 5.0 uses the JVMTI interface to the VM rather than the older JVMDI interface). You should provide sub-options to the –agentlib:jdwp parameter (in Java 5.0) or to –Xrunjdwp: (prior to Java 5.0); the set of possible sub-options is the same.

The sub-options are specified as follows:

-agentlib:jdwp=<name1>[=<value1>],<name2>[=<value2>]...

or

-Xrunjdwp:<name1>[=<value1>],<name2>[=<value2>]...

You can use these options:

  • help: prints a brief message on how to use it and exits the VM.
  • server: ("n" or "y") If "y," listen for a debugger application to attach; otherwise, attach to the debugger application at the specified address.
  • address: transport address for the connection. If server=n, attempt to attach to a debugger application at this address. If server=y, listen for a connection at this address.
  • timeout: If server=y, this specifies the timeout, in milliseconds, to wait for the debugger to attach. If server=n, this specifies the timeout, in milliseconds, to use when attaching to the debugger.
  • suspend: If "y," JVM suspends the execution until a debugger connects to the debuggee JVM.

Here are command line examples:

-agentlib:jdwp=transport=dt_socket,server=y,address=8000

Listen for a socket connection on port 8000. Suspend this VM before the main class loads (suspend=y by default). Once the debugger application connects, it can send a JDWP command to resume the VM.

-agentlib:jdwp=transport=dt_shmem,server=y,suspend=n

Choose an available shared memory transport address and print it to stdout. Listen for a shared memory connection at that address. Allow the VM to begin executing before the debugger application attaches.

-agentlib:jdwp=transport=dt_socket,address=myhost:8000

Attach to a running debugger application via socket on host myhost at port 8000. Suspend this VM before the main class loads.

Peter V. Mikhalenko is a Sun certified professional who works for Deutsche Bank as a business consultant.

1 comments
jesonpaul
jesonpaul

best article I have read so far on JPDA .thanks man. There is couple of good java debugging resources I found on internet for searching this check this out Java debugging tutorial and tips in Eclipse How to setup java remote debugging in Eclipse

Editor's Picks