We're building a suite of monitoring tools that will help us inspect and manage our enterprise-class app. We currently have working an instrumenting interface (via mbeans) that uses javassist to instrument bytecode; this tool runs as an agent within the app's vm using the java.lang.instrument package -- no native code involved. The next aspect of the tool suite is a more sophisticated "inspector" that will offer debugging-like facilities: method tracing, local variable inspection, thread inspection, etc.
We prototyped a version using jdi and that works fine if the agent does not run in the same vm as the app. However, setting things up so that the JDI agent does run in the app's vm does not work -- the entire vm (app, agent) hang after a call to VirtualMachine.suspend().
Intuitively, I think I knew this would happen but wanted to confirm it here. I have two questions:
1. Is it possible for a JDI-based agent to run in the same vm as a monitored app?
2. If not (1), and we write our agent in C, will the RunAgentThread() jvmti api allow us to run the inspector in the app's vm?
Yes, I read that in the spec, but I don't understand that to imply a JDI agent may run in the save VM as the debuggee. A number of clues seem to indicate a JDI agent must be "out of process", and right now I'm leaning towards a native JVMTI version.
Have a look at the Javassist product for instrumenting bytecode. It's very cool.
JDI wasn't intended to be used to debug the same process in which the JDI client is running. We haven't tested that.
However, it is an interesting question and it seems that theoretically, if you are very careful, you might be able to do
a limited amount of debugging.
I gave it a try. My test program is launched with
The program starts a thread (named by default Thread-0) and then uses the SocketAttachingConnector
to attach to 60000. After it gets back a VirtualMachine object, it does
and prints the result:
instance of java.lang.Thread(name='Thread-1', id=66),
instance of java.lang.Thread(name='JDI Target VM Interface', id=4),
instance of java.lang.Thread(name='JDI Internal Event Handler', id=7),
instance of java.lang.Thread(name='Thread-0', id=69),
instance of java.lang.Thread(name='Signal Dispatcher', id=70),
instance of java.lang.ref.Finalizer$FinalizerThread(name='Finalizer', id=71),
instance of java.lang.ref.Reference$ReferenceHandler(name='Reference Handler', id=72),
instance of java.lang.Thread(name='main', id=1)
So, this method worked. It sounds like you have basically gotten this far since you said your entire
app hangs after a call to VirutalMachine.suspend(). This hang is to be expected since you are suspending
'yourself'. You have to be very careful to not do this. EG, don't set a SUSPEND_ALL breakpoint at a location
which your 'debugger' will execute. There might be other pitfalls too that make this idea impractical.
But, if you are careful, eg, restrict actions to threads in the part of your app that you want to 'monitor', you
might be able to do something useful.
Let us know how it goes.
Yes indeed, your example illustrates exactly the path I was contemplating; i.e. using an AttachConnector to attach the monitor to "myself". One task our monitor must be able to do is to capture and display method-local variables, given a line number. Looks like this entails setting breakpoints and receiving breakpoint events. According to the JDI spec for BreakpointRequest, "When an enabled BreakpointRequest is satisfied, an event set containing an BreakpointEvent will be placed on the EventQueue and the +application is interrupted+" [emphasis added]. I read this as indicating the entire VM will be suspended until the agent processes the breakpoint event and resumes vm execution. If I read correctly, I must use a native solution. Sound right?
Apologies for cross-posting. I'm new here, but I'll be a frequent visitor and I know now where to ask questions. Thanks very much for your help.
That phrase 'the application is interrupted' is bogus - what does it mean exactly? Who knows?
The suspend policy that is set in the BreakpointRequest determines what happens in the debuggee when
the breakpoint is hit. For example,the policy could be SUSPEND_NONE which means when the breakpoint is hit,
the thread which hit the breakpoint creates a BreakpointEvent, adds it to a queue to be sent to the debugger,
and then continues executing. If the policy is SUSPEND_ALL, then all threads in the debuggee are suspended
until they are resumed by the debugger. If the policy is SUSPEND_THREAD, then only the thread that hit the
breakpoint is suspended. If you want to set breakpoints, SUSPEND_THREAD is what you would want to do.
You have to be careful about locks. For example, if the thread that hits a SUSPEND_THREAD breakpoint
holds a lock that your 'debugger' code needs to process the breakpoint event, you will deadlock.
Oh, I see! Hmm. Then maybe JDI does merit further exploration for our needs. We're not writing a full-fledged debugger, so we might be able to avoid the more "controversial" aspects of JDI that might cause an in-process agent to cry.
You've been a huge help. Thanks. I'll let you know what we find out.