11 Jdwp Introduction a Step by Step Guide That Leaves No Trace for a Thousand Miles

11 JDWP Introduction- A Step-by-step Guide that Leaves No Trace for a Thousand Miles #

The Java Platform Debugging Architecture (JPDA) consists of three relatively independent levels. These three levels, from low to high, are the Java Virtual Machine Tool Interface (JVMTI), the Java Debug Wire Protocol (JDWP), and the Java Debug Interface (JDI).

Module Level Programming Language Function
JVMTI Low-level C Get and control the current virtual machine status
JDWP Intermediate level C Define the data format for interaction between JVMTI and JDI
JDI High-level Java Provide Java API to remotely control the debugged virtual machine

For more details, please refer to or search for: Overview of JPDA Architecture.

Server-side JVM Configuration #

This article mainly explains how to enable JDWP in JVM for remote debugging. Let’s assume that the main startup class is com.xxx.Test.

On a Windows machine:

java -Xdebug -Xrunjdwp:transport=dt_shmem,address=debug,server=y,suspend=y com.xxx.Test

On Solaris or Linux operating systems:

java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=y com.xxx.Test

In fact, the -Xdebug option has no practical purpose. According to the official documentation, it is kept for historical compatibility to avoid errors.

Additionally, the suspend=y parameter in this configuration will cause the Java process to suspend at startup, and it will continue executing the program when a debugger is connected.

If it is changed to suspend=n, the Java process will start executing directly, but we can connect to it anytime with a debugger.

For example, if we start a web server process, when the value is ‘y’, the JVM of the server will not start the web server after initialization. It will wait until we connect to this Java process with tools like IDEA, Eclipse, JDB, etc., and then continue starting the web server. However, if it is ’n’, the web server will run normally regardless of whether a debugger is connected or not.

By using these startup parameters, the Test class will run in debug mode and will wait for the debugger to connect to the JVM’s debug address, which is ‘Debug’ on Windows and port ‘8888’ on Oracle Solaris or Linux operating systems.

If you carefully observe, you will notice that when starting a program in Debug mode in IDEA, similar startup options are automatically set.

JDB #

After enabling JDWP, various clients can be used for debugging/remote debugging. For example, JDB can be used to debug the local JVM:

jdb -attach 'debug'
jdb -attach 8888

Once JDB is initialized and connected to Test, Java code level debugging can be performed.

However, JDB debugging is very cumbersome. For example, there are several commonly used commands:

  1. Set a breakpoint:

    stop at classname:linenumber

  2. Clear a breakpoint:

    clear at classname:linenumber

  3. Display local variables:

    localx

  4. Display the value of variable a:

    print a

  5. Display the current thread stack:

    wherei

  6. Step to the next line of code:

    next

  7. Continue executing the code until the next breakpoint is encountered:

    cont

It can be seen that using JDB for debugging is very cumbersome, so we usually debug code in development tools like IDEA or Eclipse.

Remote Debugging with IntelliJ IDEA #

Below are the steps to use remote debugging in IntelliJ IDEA. The configuration is similar to regular Debug configuration. Follow these steps:

  1. Go to Edit Configuration:

749ef972-a71a-475a-a395-ab8e78db5fdf.png

  1. Add a Remote configuration (not the one under Tomcat):

f6a45f68-6c1c-4c55-90ae-eae35c2dafc3.png

  1. Configure the port number, for example, 8888:

82bb5db4-9dc4-443d-9bb9-00864167c52f.png

  1. Click the “Apply” button to save.

  2. Click the “Debug” button to start remote debugging. After connecting, it works just like debugging a local program. Remember to add breakpoints or conditional breakpoints.

Note: When remote debugging, ensure that the code running in the server JVM is identical to the local code, otherwise, unexpected issues may occur.

Astute readers may have noticed that IntelliJ IDEA provides the remote JVM startup parameters, and it is recommended to use the agentlib method:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888

Remote debugging is not only useful during the development process but also in production environments. Sometimes, when the actual production results are inconsistent with the expected results, we are unable to determine what went wrong during program execution. In such cases, we can use remote debugging to connect to the production environment and trace which step went wrong.

Why can JVM allow different development tools and debuggers to connect for debugging? This is because JVM provides an open protocol for exchanging debugging information. Each vendor can implement their own debugging graphical tools based on this protocol, making it convenient for Java developers. Now let’s briefly explain this protocol.

JDWP Protocol Specification #

JDWP, short for Java Debug Wire Protocol, is a protocol used to define the communication between a debugger and the target JVM. In Chinese, JDWP is translated as “Java 调试连接协议”.

JDWP is an optional component and may not be available in some JDK implementations.

JDWP supports two debugging scenarios:

  • Other processes on the same computer
  • Remote computers

Unlike many protocol specifications, JDWP only defines the specific format and layout and does not specify the protocol used to transmit the data.

JDWP implementations can use a simple API to accept different transport mechanisms, and various combinations of transport are not necessarily supported.

JDWP is designed to be simple, easy to implement, and flexible enough for future upgrades.

Currently, JDWP does not specify any transport mechanisms. If changes occur in the future, they will be specified in separate documents.

JDWP is a layer in JPDA (Java Platform Debugger Architecture). The JPDA architecture also includes higher-level Java debugging interfaces (JDI, Java Debug Interface). JDWP aims to facilitate the effective use of JDI, and many of its features are tailored for this purpose.

For Debugger tools developed in Java, using JDI directly is more convenient than using JDWP.

For more information about JPDA, please refer to the Java Platform Debugger Architecture documentation.

JDWP Handshake Process #

After the connection is established, before sending any other data packets, both sides of the connection need to perform a handshake:

The handshake process includes the following steps:

  • The debugger sends 14 bytes to the target JVM, which are the ASCII characters “JDWP-Handshake”.
  • The VM replies with the same 14 bytes: JDWP-Handshake.

JDWP Packets #

JDWP is a stateless protocol that uses packets to transmit data. It consists of two basic packet types: Command Packets and Reply Packets.

Both the debugger and the target VM can send Command Packets. The debugger uses Command Packets to request relevant information from the target VM or to control the execution of a program. The target VM can notify the debugger of certain events, such as breakpoints or exceptions, using Command Packets as well.

Reply Packets are used solely to respond to Command Packets and indicate whether the command was successful or not. Reply Packets can also carry the requested data from the command, such as the value of a field or variable. Currently, events sent from the target VM do not require a reply from the debugger.

JDWP is asynchronous, meaning that multiple Command Packets can be sent before receiving a response.

The header size of both Command Packets and Reply Packets is the same. This makes the implementation and abstraction of the transmission easier. The layout of each packet is as follows.

Command Packet

  • Header
    • Length (4 bytes)
    • ID (4 bytes)
    • Flags (1 byte)
    • Command Set (1 byte)
    • Command (1 byte)
  • Data (variable length)

Reply Packet

  • Header
    • Length (4 bytes)
    • ID (4 bytes)
    • Flags (1 byte)
    • Error Code (2 bytes)
  • Data (variable length)

As can be seen, the first three fields in the headers of these two packet types have the same format.

All fields and data sent through JDWP should use big-endian byte order. The definition of big-endian byte order can be found in the “Java Virtual Machine Specification”.

Packet Field Description #

Common Header Fields #

The following header fields are common to both command packets and reply packets.

length

The length field represents the number of bytes in the entire packet (including the header). Since the size of the packet header is 11 bytes, the value of this field is set to 11 for packets without data.

id

The id field is used to uniquely identify each pair of packets (command/reply). The id value of the reply packet must be the same as the corresponding command packet ID, so that asynchronous commands and replies can be matched. The id field of all unfinished command packets sent from the same source must be unique. (There is no problem if the command packets from the debugger and the JVM have the same ID.) In addition, there are no requirements for the allocation of IDs. For most implementations, using an incrementing counter is sufficient. The id can have 2^32 packet values, which is enough to handle various debugging scenarios.

flags

The flags field is used to modify the queuing and processing of commands and also to mark packets originating from the JVM. Currently, only one flag bit 0x80 is defined, which indicates that the packet is a reply packet. Future versions of the protocol may define additional flags.

Header of Command Packet #

In addition to the common header fields described above, command packets also have the following request header.

command set

This field is mainly used to group commands in a meaningful way. The command sets defined by Sun are grouped through interfaces supported in JDI. For example, all commands supported by the VirtualMachine interface are in the VirtualMachine command set. The command set space is roughly divided into the following categories:

  • 0-63: Command sets sent to the target VM
  • 64-127: Command sets sent to the debugger
  • 128-256: Commands and extensions defined by JVM providers themselves.

command

This field is used to identify specific commands in the command set. This field, along with the command set field, is used to indicate how the command packet should be handled. In other words, they tell the receiver what to do. Specific commands will be described later in this document.

Header of Reply Packet #

In addition to the common header fields described above, reply packets also have the following request header.

error code

This field is used to indicate whether the corresponding command packet was processed successfully or not. A value of 0 indicates success, while a non-zero value indicates an error. The returned error code is specified by the specific command set/command, but it is usually mapped to the JVM TI standard error code.

Data #

The Data section of each command is different. There are also differences between the corresponding command packets and reply packets. For example, a request command packet may want to retrieve the value of a certain field, which can be filled in the Data with object ID and field ID. The Data field of the reply packet will store the value of that field.

Common Data Types in JDWP #

Usually, the format of the Data field in a command or response packet is specified by the specific command. Each field in the Data is encoded in big-endian format, which is the standard format for Java. The data types of each Data field are described below.

Most data types in JDWP packets are as follows.

Name Size
byte 1 byte
boolean 1 byte
int 4 bytes
long 8 bytes
objectID Determined by the specific JVM, up to 8 bytes
tagged-objectID size of objectID + 1 byte
threadID Same as objectID
threadGroupID Same as objectID
stringID Same as objectID
classLoaderID Same as objectID
classObjectID Same as objectID
arrayID Same as objectID
referenceTypeID Same as objectID
classID Same as referenceTypeID
interfaceID Same as referenceTypeID
arrayTypeID Same as referenceTypeID
methodID Determined by the specific JVM, up to 8 bytes
fieldID Determined by the specific JVM, up to 8 bytes
frameID Determined by the specific JVM, up to 8 bytes
location Determined by the specific JVM
string Variable length
value Variable length
untagged-value Variable length
arrayregion Variable length

In different JVMs, the sizes of Object IDs, Reference Type IDs, Field IDs, Method IDs, and Frame IDs may vary.

Generally, their sizes correspond to the sizes of native identifiers used for these items in JNI and JVMDI calls. The maximum size among these types is 8 bytes. Of course, a debugger can use the idSizes command to determine the size of each type.

If the JVM receives a command packet that contains non-implemented or non-recognized commands/command sets, it will return a response packet with the error code NOT_IMPLEMENTED. For specific error constants, please refer to the Error Constants page.

Reference Documents #