JavaTM Platform Debugger Architecture |
Home Page |
JVMTI Modularity
The Java Virtual Machine Tool Interface (JVMTI)
describes the functionality provided by a virtual machine (VM)
in order to allow debugging of Java programming language
applications running under this VM. In JPDA, JVMTI is implemented
by a VM and the client is the JPDA back-end. In the reference
implementation of JPDA, JVMTI is implemented by the Java HotSpot VM
and the client is the reference implementation of the back-end,
supplied as a native shared library (jdwp.so, jdwp.dll, ...),
which is shipped with the JDK.
Many VMs other than the Java HotSpot VM implement JVMTI. The reference implementation of the back-end has been ported to other platforms. And there are JVMTI 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.
JVMTI 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.
Walk-through
The various ways that the interfaces can be used is discussed
above. This section will examine how the standard full JPDA
works. The examples go into detail about specific calls and
codes. It is not important to understand these -- they are
present only to make the examples more concrete.
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 JVMTI. In particular, let's say the requested value is an integer, the following JVMTI 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 JVMTI 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 JVMTI 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.
Porting
Each virtual machine implementation needs its own JVMTI
implementation -- a JVMTI implementation must dig deeply into
VM data structures and must set hooks into the VM implementation
in order to get events. Adding JVMDT to a VM without JVMTI
support is a significant undertaking. Depending on the complexity
of the VM and the amount of optional JVMTI implemented, it might
be a three to twelve month project. Porting a VM which has
JVMTI support to a new platform is primarily the work of porting
the non-JVMTI portions of the VM -- JVMTI adds a comparatively
small amount of work.
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 J2SE 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. Plans are to eventually add an SPI to the JDI to allow these addition. Currently, however, these require changes to the source.
Copyright © 2004 Sun
Microsystems, Inc. All Rights Reserved.
Please send comments to: java-debugger@sun.com |