Over a million developers have joined DZone.

Breakpoint processing in JDI

· Java Zone

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

I've been working on a JDI (Java Debug Interface) project lately and have been posting helpful tips as I go along. It has been a few years since I've worked with this API, but although I know there have been a few enhancements, the API is quite consistent with what I remember. In this post I'm going to discuss how to use the API to inspect variable values at a breakpoint.

As in a previous post, I'll be restricting myself to breakpoint requests (as opposed to method-entry, method-exit, exception requests, etc.), mainly because I know they can be processed rather quickly in near-real-time. Some other types of exceptions significantly slow down the target application, even if you process them as quickly as practical and immediately resume all threads. In a typical, interactive debugger, a human is in the loop, inspecting variables and making decisions, and a speed difference between JDI event types would not be noticeable. But in the application I'm developing, I want to pause execution just long enough to collect data and then continue execution immediately. So I'm going to use only breakpoint requests for my project.

Finding Values at a Breakpoint

Given an instance vm of a VirtualMachine, you might configure your event-consuming code as follows:

EventQueue evtQueue = vm.eventQueue();
 EventSet evtSet = evtQueue.remove();
 EventIterator evtIter = evtSet.eventIterator();
 while (evtIter.hasNext())
     Event evt = evtIter.next();
     EventRequest evtReq = evt.request();
     if (evtReq instanceof BreakpointRequest)
       BreakpointRequest bpReq = (BreakpointRequest)evtReq;
       BreakpointEvent bpEvt = (BreakpointEvent)evt;
       ThreadReference threadRef = bpEvt.thread();
       StackFrame stackFrame = threadRef.frame(0);
At this point, you can find variables in two places:
  • The stack (hence the retrieval of the StackFrame); for example, local variables in a method;
  • The current ObjectReference, for static and instance variables.
If, at a breakpoint, you are searching for the value of a local variable, let's call it varName, you could find it with the following snippet of code:
  List visVars = stackFrame.visibleVariables();
for (LocalVariable visibleVar: visVars)
  if (visibleVar.name().equals(varName))
    Value val = stackFrame.getValue(visibleVar);
As I've mentioned before, the JDI API isn't big on classes with constructors; most of what you see is interface definitions, with impls supplied behind the scenes. So, you would not instantiate a Field in JDI and search for it; instead, you drill down into the API and compare some aspect of the object you want with the data you have at hand. For example, you list all the LocalVariables on the stack and then compare their .name()s with the name of your target variable.

If you don't find your variable on the stack, then you can check the object's fields (I prefer to check the stack first). To get object field values, you would first get the ObjectReference and then query it:
ObjectReference objRef = stackFrame.thisObject();
Value targetVal = objRef.getValue(field...
Halt. An ObjectReference will get a value for you, but it requires a Field object, for which there is no constructor. This is just the issue I was discussing above. Just where do you get a Field object? You query the available Fields of the object's ReferenceType first, then pass those Field references to the object to get their values for this instance:
ObjectReference objRef = stackFrame.thisObject();
ReferenceType refType = objRef.referenceType();
List<Field> objFields = refType.allFields();
for (int i=0; i<objFields.size(); i++)
  Field nextField = objFields.get(i);
  if (nextField.name().equals(varName))
    Value targetVal = objRef.getValue(nextField);

So there are your values at the breakpoint, obtained either from the stack or the instance fields of the ObjectReference.

Working with JDI Values

The Value that you get from JDI requires a little effort to be of practical use. For example, if the Value is an instance of a StringReference, you'll need to cast it to a StringReference, then call .value() to get a java.lang.String value. If it's an instance of an IntegerValue, then you'll have to cast the Value to an IntegerValue and call .value(), which in this case will return an int! I find this process to be a little tedious (note: .toString() doesn't return the value!). In my case, I'm going to be generating either log output or key-value pairs for each breakpoint event, so my approach is just to write a convenience method that hides the details and provides the value in String form.

An interesting case is when the Value is itself an ObjectReference. Just as you can expand variables down to their fields in a typical debugger, you can also choose to drill down into objects at a breakpoint and retrieve their fields, to an arbitrary level of depth. The same process can be used for other JDI types, such as StringReference.

These fields might not be quite as familiar as the methods you are more accustomed to using. For example, if you are logging the size of Strings that are being generated in your target program, you can't call .length() on the StringReference, but you can get its instance fields and retrieve count, which is the name of the field holding the String's length.

Practical Considerations

While it is possible to drill down to an arbitrary depth in the stack or in the instance's fields, keep in mind that you have halted your application while this is occurring. It's best to keep processing during this period to a minimum. While you could spawn a thread to finish processing the breakpoint (for example, creating and sending a JMS message), you will need to retrieve all the variables from the stack and instance while the target application is halted. So that drill-down should be quick.

That caveat aside, you can then specify variables in some custom format, such as dot-notation, and then drill down into the fields/variables recursively until you find the value you want. For example, you could have a variable specified as fileName.count, in which case you would locate the fileName StringReference, then get the instance fields of fileName and find count, which is the length of the file name.

For fields like String's count, which might not be an obvious choice to someone configuring a breakpoint specification, some type of browser would be useful. Browsing the stack isn't an option until you've already set breakpoints and reached a breakpoint, as the stack frame isn't in scope until the breakpoint is reached. But for static and instance fields, you only need a ReferenceType to list the available Fields, to some configurable depth, before you even start debugging the application. Since these Fields are available in JDI as soon as the class is loaded, you don't have to halt program execution just to get the static and instance field names for the class.


From http://wayne-adams.blogspot.com/2011/11/breakpoint-processing-in-jdi.html

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.


The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}