Java Debug Wire Protocol Transport Interface (jdwpTransport)

A transport is implemented as a series of functions in a dynamic library (sometimes called a DLL, or shared library). The library exports an onload function that is invoked by the JDWP (or other) agent at startup-time.

Like JVMTI, jdwpTransport functions are accessed through an interface pointer called the environment pointer. An environment pointer is a pointer to an environment and has type jdwpTransportEnv*. The environment pointer is returned to the JDWP (or other) agent from the onload function.

A transport may support a single environment or it may support multiple environments. In other words, a transport may be used by only one, or multiple agents at the same time. If a transport supports multiple environments then each call to the onload function will return a new environment pointer. If a transport only supports a single environment then second, and subsequent calls to the onload function will return an error.

A transport is thread-safe and jdwpTransport functions can be used by multiple concurrent threads. For example, one thread may be blocked in the jdwpTransport ReadPacket function waiting for a packet while another thread invokes WritePacket function to write a packet.

In most cases jdwpTransport functions return a jdwpTransportError value indicating return status. Some functions return additional values through pointers provided by the calling function. In cases where the return values are allocated by a jdwpTransport function then the function will use the memory allocation routine specified by the agent. The memory allocation routines are specified to the transport, at start-up, via the onload function.

In the event of an error (that is, one of the jdwpTransport functions returns a value other than JDWPTRANSPORT_ERROR_NONE) then a string representing the error can be subsequently obtained through a call to the jdwpTransport function GetLastError. Errors are recorded on a per-thread basis. The GetLastError function will return a string represetning the last error that was encountered by the current thread only.

Developing a Transport implementation

A transport library can be developed in any native language that supports C language calling conventions and C or C++ definitions.

The function, data type, and constant definitions needed for using the jdwpTransport interface are defined in the include file jdwpTransport.h. To use these definitions add the Java SE include directory to your include path:

#include "jdwpTransport.h"

Transport Start-Up

The transport library must export an onload function with the following prototype:

JNIEXPORT jint JNICALL
jdwpTransport_OnLoad(JavaVM *jvm,
                     jdwpTransportCallback *callback,
                     jint version,
                     jdwpTransportEnv** env);

This function will be called by the JDWP (or other agent) when the library is loaded.

The jvm argument is the JNI invocation interface obtained by agent by invoking JNI's GetJavaVM function.

The callback argument is a pointer to a function table of memory management routines that the transport must use to allocate the memory for return values that are allocated by the transport implementation:

typedef struct jdwpTransportCallback {
    void* (*alloc)(jint numBytes);
    void (*free)(void *buffer);
} jdwpTransportCallback;

The lifespan of the callback argument is the onload function and therefore the transport must take a copy of the function table in the jdwpTransport_OnLoad function.

The function table has two entries. The alloc function allocates an area of memory. It has a single argument to specify the number of bytes to allocate. It returns a pointer to the begining of the allocated memory, or NULL if the memory request cannot be honored. If the number of bytes requested is zero then NULL is returned. The free function deallocates an area of memory that was previously allocated using the alloc function.

The memory management functions provided by the agent are thread safe and the transport implementation is not required to synchronize calls to the these functions. The implementation of the memory management functions are guaranteed not to call any jdwpTransport function.

version is the version of the transport interface that the agent expects. This must be specified as JDWPTRANSPORT_VERSION_1_0.

env is a pointer to the environment pointer returned by the function.

The jdwpTransport_OnLoad function returns JNI_OK if the transport initializes successfully. If initialization fails then one of the following errors is returned:

JNI_ENOMEM
JNI_EVERSION
JNI_EEXIST

JNI_ENOMEM is returned if there is insufficient memory to complete initialization.

JNI_EVERSION is returned if the version in the version argument is not JDWPTRANSPORT_VERSION_1_0.

JNI_EEXIST is returned if the transport only supports a single environment, and the environment pointer was previously returned by the first call to the onload function.

Functions

The jdwpTransport functions fall into these categories:

Connection Management

The connection management functions are used to establish and close the connection to the debugger. A connection provides a reliable flow of JDWP packets to and from the debugger. Packets written to a connection are read, by the debugger, in exactly the order in which they were written. Similarly, any packets written to the connection by the debugger are read in the order in which they were written.

Connections are established either actively or passively. Establishing the connection actively means that the jdwpTransport's Attach function is called to initiate the connection the debugger. Establishing the connection passively means that the jdwpTransport's StartListening function is used to put the transport into listen mode so that it listens for a connection from a debugger. Once in listen mode the Accept function is used to accept the connection. Irrespective of how the connection is established the Close function is used to close the connection, and IsOpen is used to test if a connection is open to the debugger.

Attach

jdwpTransportError
Attach(jdwpTransportEnv* env, const char* address,
       jlong attachTimeout, jlong handshakeTimeout)

Attaches to the debugger. Attaching to the debugger involves two steps. First, a connection is established to the specified address. Once a connection is established a handshake is performed to ensure that the connection is indeed established to a debugger. Handshaking involves the exchange of ASCII string JDWP-Handshake as specified in the Java Debug Wire Protocol specification.

The address argument is a pointer to a string representing the address of the debugger. The exact format is specific to the transport (In the case of a TCP/IP based transport the address may include the hostname and port number of the debugger. In the case of a transport that supports connections through a serial port it might be the device name of the serial port).

The attachTimeout argument specifies a timeout to use when attaching. If the transport supports an attach timeout (see GetCapabilities) and if the attachTimeout is positive then it specifies the timeout, in milliseconds (more or less), to use when attaching to the debugger. If the transport does not support an attach timeout, or if attachTimeout is specified as zero then a timeout is not used when attaching.

The handshakeTimeout argument specifies a timeout to use when handshaking with the debugger. If the transport supports a handshake timeout (see GetCapabilities) and if the handshakeTimeout is positive then it specifies the timeout, in milliseconds (more or less), to use when handshaking with the debugger. The exact usage of the handshake timeout is specific to the transport - for example one implementation may use the timeout as an inter-character timeout while waiting for the JDWP-Handshake message from the debugger. Another implementation may use the timeout to indicate the total duration allowed for the handshake exchange. In general the purpose of the handshake timeout is to allow for error handling in the event that the transport connects to something other than a valid debugger. If the transport does not support a handshake timeout, or if the handshakeTimeout is specified as zero then a timeout is not used when handshaking.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT
JDWPTRANSPORT_ERROR_ILLEGAL_STATE
JDWPTRANSPORT_ERROR_IO_ERROR
JDWPTRANSPORT_ERROR_TIMEOUT

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT is returned if address is invalid or timeout is negative.

JDWPTRANSPORT_ERROR_ILLEGAL_STATE is returned if the transport is currently in listen mode (see StartListening), or there is already an open connection to the debugger (see IsOpen).

JDWPTRANSPORT_ERROR_IO_ERROR is returned if there is an error (other than an attach timeout) attaching to the debugger. Note that errors during the start-up handshake (including a handshake timeout) are considered I/O errors. I/O errors are specific to the transport. GetLastError can be used to obtain a string representation of the error.

JDWPTRANSPORT_ERROR_TIMEOUT is returned if the transport supports an attach timeout, and if the attachTimeout value is positive, and if the connection to the debugger cannot be established within that attachTimeout period.

StartListening

jdwpTransportError
StartListening(jdwpTransportEnv* env, const char* address, char** actualAddress);

Puts the transport into listen mode to listen for a connection from a debugger.

The address argument is a pointer to a string representing the local address that the transport should listen on. The exact format is specific to the transport (In the case of a TCP/IP based transport the address might a local TCP port number. In the case of a transport that supports connections through a serial port it might be the device name of a serial port). The address argument can be specified as NULL or as an empty string (first character is \0). In that case the transport listens on a default address that is specific to the transport.

If actualAddress is not NULL then it is set to the address of a string returned by the StartListening function. The returned string will contain the string representation of the address that the transport is listening on. This may, or may not, differ from the address provided in the address argument. The string is allocated using the allocation callback provided to the transport when the jdwpTransport_OnLoad function was called. The caller is responsible for freeing the returned string.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT
JDWPTRANSPORT_ERROR_ILLEGAL_STATE
JDWPTRANSPORT_ERROR_IO_ERROR

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT is returned if address is invalid.

JDWPTRANSPORT_ERROR_ILLEGAL_STATE is returned if there is already an open connection to a debugger (see IsOpen), or the transport is already in listen mode.

JDWPTRANSPORT_ERROR_IO_ERROR is returned if there is an error putting the transport into listen mode. The nature of the error is specific to the transport. GetLastError can be used to obtain a string representing the error.

StopListening

jdwpTransportError
StopListening(jdwpTransportEnv* env)

Takes the transport out of listen mode so that it's no longer listening for connections from a debugger.

If the transport is in listen mode then it will be taken out of this mode. If there is an open (see IsOpen) connection to a debugger then it is unaffected by this function. In other words, StopListening does not close a connection to the debugger. If the transport is not in listen mode then this function does nothing and no error is returned.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_IO_ERROR

JDWPTRANSPORT_ERROR_IO_ERROR is returned if there is an error taking the transport out of listen mode. The nature of the error is specific to the transport. GetLastError can be used to obtain a string representing the error.

Accept

jdwpTransportError
Accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout)

Accepts a connection from a debugger. Accepting a connection from a debugger involves two steps. Firstly, a connection is established by the debugger. Once a connection is established a handshake is performed to ensure that the connection was indeed established by a debugger. Handshaking involves the exchange of ASCII string JDWP-Handshake as specified in the Java Debug Wire Protocol specification.

The acceptTimeout argument specifies the timeout to use while waiting for the debugger to connect. If the transport supports an accept timeout (see GetCapabilities) and if the acceptTimeout is positive then it specifies the timeout, in milliseconds (more or less), to use when waiting for a connection from a debugger. If the transport does not support an accept timeout, or if timeout is specified as zero then block indefinitely waiting for a connection.

The handshakeTimeout argument specifies a timeout to use when handshaking with the debugger. If the transport supports a handshake timeout (see GetCapabilities) and if the handshakeTimeout is positive then it specifies the timeout, in milliseconds (more or less), to use when handshaking with the debugger. The exact usage of the handshake timeout is specific to the transport - for example one implementation may use the timeout as an inter-character timeout while waiting for the JDWP-Handshake message from the debugger. Another implementation may use the timeout to indicate the total duration allowed for the handshake exchange. In general the purpose of the handshake timeout is to allow for error handling in the event that something other than a debugger establishes a connection to the debuggee. If the transport does not support a handshake timeout, or if the handshakeTimeout is specified as zero then a timeout is not used when handshaking.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT
JDWPTRANSPORT_ERROR_ILLEGAL_STATE
JDWPTRANSPORT_ERROR_IO_ERROR
JDWPTRANSPORT_ERROR_TIMEOUT

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT is returned if attachTimeout or handshakeTimeout is negative.

JDWPTRANSPORT_ERROR_ILLEGAL_STATE is returned if there is already an open connection to a debugger (see IsOpen), or if the transport is not in listen mode (see StartListening).

JDWPTRANSPORT_ERROR_IO_ERROR is returned if there is an error (other than an accept timeout) while accepting a connection from a debugger. Note that errors during the start-up handshake (including a handshake timeout) are considered I/O errors. The nature of the error is specific to the transport. GetLastError can be used to obtain a string representing the error.

JDWPTRANSPORT_ERROR_TIMEOUT is returned if the transport supports an accept timeout, and if the acceptTimeout value is positive, and if the connection to the debugger cannot be established within that timeout period.

Note: A thread that is blocked in Accept waiting for a connection from a debugger can be interrupted by another thread calling StopListening. In that case the thread that called Accept will return JDWPTRANSPORT_ERROR_IO_ERROR indicating an I/O error has occurred. If a thread blocked in Accept has accepted a connection and is in the process of handshaking with the debugger then StopListening will not interrupt the connection.

IsOpen

jboolean
isOpen(jdwpTransportEnv* env)

Tells whether or not there is a connection open to a debugger.

Returns JNI_TRUE if, and only if, there is an open connection to a debugger. Otherwise it returns JNI_FALSE.

Close

jdwpTransportError
Close(jdwpTransportEnv* env)

Closes an open connection to a debugger.

If there isn't an open connection to a debugger (see IsOpen) then this function does nothing and no error is returned.

If there are threads blocked in any I/O functions (namely, ReadPacket and WritePacket), then these I/O functions will be interrupted by the close and will return an JDWPTRANSPORT_ERROR_IO_ERROR indicating an I/O error has occurred.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_IO_ERROR

JDWPTRANSPORT_ERROR_IO_ERROR is returned if there is an error closing the connection. The nature of the error is specific to the transport. GetLastError can be used to obtain a string representing the error.

I/O Functions

The I/O functions are used for reading and writing JDWP packets from and to the debugger.

ReadPacket

typedef struct {
    jint len;
    jint id;
    jbyte flags;
    jbyte cmdSet;
    jbyte cmd;
    jbyte *data;
} jdwpCmdPacket;

typedef struct {
    jint len;
    jint id;
    jbyte flags;
    jshort errorCode;
    jbyte *data;
} jdwpReplyPacket;

typedef struct jdwpPacket {
    union {
        jdwpCmdPacket cmd;
        jdwpReplyPacket reply;
    } type;
} jdwpPacket;

jdwpTransportError
ReadPacket(jdwpTransportEnv* env, jdwpPacket* packet)

Reads a JDWP packet from an open connection to a debugger.

This function does a blocking read on an open connection. It blocks indefinitely until a complete JDWP packet can be returned, or in the case of a transport based on a stream-oriented protocol, end-of-stream is encountered.

The packet argument is the address of a jdwpPacket structure that is populated by this function. The packet.type.cmd.len or packet.type.reply.len field (depending on if the packet is a command or reply packet) is populated with the length of the packet. If end of stream is encountered the length field will be set to 0 and all other fields in the packet will be undefined. If end of stream is encountered after reading some, but not all, bytes of a packet it is considered an I/O error and JDWPTRANSPORT_ERROR_IO_ERROR will be returned. In that case the length field will not be populated. When an entire packet is read then all fields in the packet are populated with values in host order. This may, or may not, differ from the big endian order require when transmitting JDWP packets.

The packet.type.cmd.data or packet.type.reply.data field (depending on if the packet is a command or reply packet) will be populated with NULL or a pointer to the packet data allocated by this function. Packet data is allocated using the allocation callback provided to the transport when the jdwpTransport_OnLoad function was called. The caller is responsible to free it. The layout of the packet data (that is the data following the header, if any) is returned to the caller in the byte ordering in which it was received.

The ReadPacket function does not do any integrity checking on the returned packet except checking that the length of the packet (as indicated by the first 4 bytes) is >= 11 bytes. If the length field is less than 11 bytes then JDWPTRANSPORT_ERROR_IO_ERROR is returned.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_IO_ERROR
JDWPTRANSPORT_ERROR_ILLEGAL_STATE
JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT

JDWPTRANSPORT_ERROR_IO_ERROR is returned if an I/O error occurs when reading, the connection is closed asynchronously by another thread calling the Close function, or a badly formed packet (length field less than 11 bytes) is received. I/O errors are specific to the transport. GetLastError can be used to obtain a string representing the error.

JDWPTRANSPORT_ERROR_ILLEGAL_STATE is returned if there isn't an open connection to a debugger (see IsOpen).

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT is returned if packet is NULL

WritePacket

jdwpTransportError
WritePacket(jdwpTransportEnv* env, const jdwpPacket* packet)

Writes a JDWP packet to an open connection.

The packet argument is a pointer to a jdwpPacket structure. All fields in the packet header must be stored in host order. The packet data field (packet.type.cmd.data or packet.type.reply.data) must be NULL, or a pointer to a location containing packet data that immediately follows the header. The packet data is must be in network order (big endian) ready for transmission.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT
JDWPTRANSPORT_ERROR_IO_ERROR
JDWPTRANSPORT_ERROR_ILLEGAL_STATE

JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT if packet is NULL, or if the packet length field (packet.type.cmd.len or packet.type.reply.len) is less than 11, or it is greater than 11 but the packet data field (packet.type.cmd.data or packet.type.reply.data) is NULL.

JDWPTRANSPORT_ERROR_IO_ERROR is returned if an I/O error occurs when writing, or the connection is closed asynchronously by another thread calling the Close function. I/O errors are specific to the transport. GetLastError can be used to obtain a string representing the error.

JDWPTRANSPORT_ERROR_ILLEGAL_STATE is returned if there isn't an open connection to a debugger (see IsOpen).

Miscellaneous Functions

GetLastError

jdwpTransportError
GetLastError(jdwpTransportEnv* env, char** msg);

Returns the string representation of the last error.

When an error occurs it is recorded on a per-thread basis. A subsequent call to GetLastError returns the string representation of the last I/O error.

The msg argument is a pointer to a null-terminated string returned by this function. The string is allocated using the allocation callback provided to the transport when the jdwpTransport_OnLoad function was called. The caller is responsible to free the returned string.

This function returns a universal error or one of the following errors:

JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE

JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE is returned if this thread has not encountered an I/O error or there isn't a string representation of the last error available.

GetCapabilities

typedef struct {
    unsigned int can_timeout_attach     :1;
    unsigned int can_timeout_accept     :1;
    unsigned int can_timeout_handshake  :1;
    unsigned int reserved3              :1;
    unsigned int reserved4              :1;
    unsigned int reserved5              :1;
    unsigned int reserved6              :1;
    unsigned int reserved7              :1;
    unsigned int reserved8              :1;
    unsigned int reserved9              :1;
    unsigned int reserved10             :1;
    unsigned int reserved11             :1;
    unsigned int reserved12             :1;
    unsigned int reserved13             :1;
    unsigned int reserved14             :1;
    unsigned int reserved15             :1;
} JDWPTransportCapabilities;

jdwpTransportError
GetCapabilities(jdwpTransportEnv* env, JDWPTransportCapabilities* capabilitiesPtr)

Returns via capabilitiesPtr the optional jdwpTransport features supported by this transport. The capabilities structure contains a number of boolean flags indicating whether the named feature is supported. The current set of flags is:

Boolean Flag Meaning
can_timeout_attach Indicates if the transport supports attaching with a timeout
can_timeout_accept Indicates if the transport supports an accept timeout
can_timeout_handshake Indicates if the transport supports a timeout when performing the initial handshake with the debugger when the connection is established

This function does not return any errors.

Universal Errors

Error Meaning
JDWPTRANSPORT_ERROR_NONE No error has occurred. This is the error code that is returned on successful completion of the function.
JDWPTRANSPORT_ERROR_OUT_OF_MEMORY The function needed to allocate memory and no more memory was available for allocation.
JDWPTRANSPORT_ERROR_INTERNAL An unexpected internal error has occurred.

Copyright © 1993, 2017, Oracle and/or its affiliates. All rights reserved.