Many VMs other than the Java HotSpot VM implement JVM TI. The reference implementation of the back-end has been ported to other platforms. And there are JVM TI clients other than the back-end, most notably agents for applications that allow debugging of both native and Java programming language code, and thus need native level control and information. We are aware of no clean-room implementations of the back-end, although this is possible - and a lot of work.
JVM TI is problematic to implement in some VMs. JDWP is implemented directly in such VMs. On the client side, an application written in a language other than the Java programming language may not be an optimal candidate for using JDI. Some have chosen to be clients of JDWP.
The JDI could be implemented by a system with a static view of an application. It could be implemented by a system with a mechanism utterly different than JDWP/front-end for collecting information or controlling a VM.
Across each interface there are two classes of activity: requests and events. Requests originate on the debugger side and include queries for information, setting of state changes in the remote VM/application, and setting of debugging state. Events originate on the debuggee side and denote changes of state in the remote VM/application.
Let's walk through an example. A user clicks on a local variable
in a stack view in an IDE, requesting its value. The IDE uses the
JDI to get the value, in particular it calls the
getValue
method, for example:
theStackFrame.getValue(theLocalVariable)Where
theStackFrame
is a com.sun.jdi.StackFrame
and theLocalVariable
is a com.sun.jdi.LocalVariable
.
The front-end then sends this query over a communications channel (let's say a socket) to the back-end running in the debuggee process. It sends it by formatting it into a byte stream in accordance with the JDWP. In particular, it sends a GetValues command (byte value: 1) in the StackFrame command set (byte value: 16), followed by the thread ID, frame ID, etc.
The back-end deciphers the byte-stream and sends the query off to the VM through the JVM TI. In particular, let's say the requested value is an integer, the following JVM TI function call is made:
error = jvmti->GetLocalInt(frame, slot, &intValue);The back-end sends back across the socket, a response packet, which will include the value of
intValue
, and which will be
formatted according to JDWP. The front-end deciphers the response
packet and returns the value as the value of the
getValue
method call. The IDE then displays the value.
Requests to change debugging state are processed in a similar manner. For example, a request to set a breakpoint goes through the same steps -- although, of course, the JDI methods called, the JDWP commands sent, and the JVM TI functions called are different. Additionally, the front-end and back-end do more than shove data back and forth, they track and schedule activity and convert, filter, and cache information, so a breakpoint request will be processed quite differently than a get value query - but the communication sequence will be the same.
What happens when the application being debugged finally hits this breakpoint? This is where events come into play. The virtual machine sends an event across the JVM TI interface. In particular, it calls the event handling function passing the breakpoint:
The back-end has set the event handling function to be:
static void Breakpoint(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location) { ...This back-end function starts a chain of activity which filters the event to see if it is interesting, queues it, and sends it across the socket in the JDWP format defined for breakpoint events. The front-end decodes and processes the event, eventually generating a JDI event. In particular, the JDI event is exposing it as a
com.sun.tools.jdi.event.BreakpointEvent
. The IDE
then gets the event by removing it from the event queue:
theEventQueue.remove()where
theEventQueue
is a
com.sun.jdi.event.EventQueue
. The IDE will probably
update its displays by making many query calls across the JDI.
The reference implementation of the back-end can usually be moved to a new platform with little (a few lines) or no change to the source and then recompiled. To use a new VM on the same platform, the binary of the back-end should generally work -- although, it's not Java programming language code so you never know. Note that licensing issues are not covered by this document.
The front-end implementation is written in the Java programming language and will run on any platform or VM. However, the connector code has functionality that may need to be extended for some systems. For example, the reference implementation of the front-end includes a launcher which assumes virtual machines are launched using Java SE conventions. A user of the JDI can configure any launcher syntax he or she wants, but generally a debugger application would prefer to leave this to the JDI implementation. If a different type of communication channel is desired (serial, for example) this too would need to be added using the Service Provider Interface introduced in JDK 5.0.