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 representing 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 beginning 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. |