目前我正在尝试通过 JDI 提取一些执行数据。因此,我首先使用命令手动启动 java vm
java -agentlib:jdwp=transport=dt_socket,server=y,address=8000 DebugDummy
我的 DebugDummy.java:
public class DebugDummy {
public class MyInnerClass {
private int a;
public MyInnerClass(int a) {
this.a = a;
this.doSomething();
}
public void doSomething() {
System.out.println(this.a);
}
}
public DebugDummy() {
MyInnerClass myInnerClass = new MyInnerClass(5);
myInnerClass.doSomething();
}
public static void main(String[] args) {
DebugDummy dd = new DebugDummy();
}
}
然后与JDI连接,等待main-method入口和每行通过代码执行的step。
public class VMStart {
public static void main(String[] args) {
//Check for argument count
if(args.length != 3){
System.err.println("Not enough parameter!");
System.exit(0);
}
String cwd = "", mainClass = "", vmPort = "";
cwd = args[0];
mainClass = args[1];
vmPort = args[2];
System.out.println("CWD: " + cwd);
System.out.println("MainClass: " + mainClass);
System.out.println("VM Port: " + vmPort);
//Init vm arguments and settings
VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
AttachingConnector ac = vmm.attachingConnectors().get(0);
//Setting port
Map<String, Connector.Argument> env = ac.defaultArguments();
Connector.Argument port = env.get("port");
port.setValue(vmPort);
//Setting hostname
Connector.Argument hostname = env.get("hostname");
hostname.setValue("localhost");
//Attach vm to remote vm
VirtualMachine vm = null;
try {
vm = ac.attach(env);
} catch (IOException | IllegalConnectorArgumentsException e) {
//Doesn't work, stop here...
System.err.println("Can't connect to vm!");
e.printStackTrace();
System.exit(0);
}
//Create EventQueue and EventRequestManager for further event handling
EventQueue eventQueue = vm.eventQueue();
EventRequestManager mgr = vm.eventRequestManager();
//Set the vm to sleep for further operations
vm.suspend();
//Searching for our main thread reference
ThreadReference mainThread = null;
List<ThreadReference> threads = vm.allThreads();
for (ThreadReference thread : threads) {
if ("main".equals(thread.name())) {
mainThread = thread;
}
}
//Create and register MethodEntryRequest, so we can pause execution at first line of main later on
MethodEntryRequest methodEntryRequest = mgr.createMethodEntryRequest();
methodEntryRequest.addClassFilter(mainClass);
methodEntryRequest.addThreadFilter(mainThread);
methodEntryRequest.enable();
//Resume the execution of the remote vm
vm.resume();
//Resume the execution of the main thread in remote vm
mainThread.resume();
//Waiting for our needed MethodEntryEvent so execution started at first line of main method
Event event = null;
while (true) {
EventSet eventSet = null;
try {
eventSet = eventQueue.remove();
} catch (InterruptedException e) {
System.err.println("Something went wrong while waiting for MethodEntryEvent");
e.printStackTrace();
System.exit(0);
}
event = eventSet.eventIterator().next();
if (event instanceof MethodEntryEvent) {
break;
}
}
//Indicates whether there is still code execution in our remote vm
boolean codeIsExecuting = true;
//Step loop until there is no code execution
while(codeIsExecuting){
//Filter for steeping not into java api methods
final String[] noBreakpointRequests = {"java.*", "javax.*", "sun.*", "com.sun.*"};
//Creating our StepRequest for each line of code
StepRequest stepRequest = mgr.createStepRequest(mainThread, StepRequest.STEP_LINE, StepRequest.STEP_INTO);
for(String classInFilter : noBreakpointRequests){ //Apply filter
stepRequest.addClassExclusionFilter(classInFilter);
}
stepRequest.addCountFilter(1);
try{
stepRequest.enable();
}catch(IllegalThreadStateException e){
//program reached end of code, so there is no code execution anymore
codeIsExecuting = false;
System.out.println("Code execution ended...");
break;
}
//Extract data from current vm execution state
//TODO
System.out.println("TODO - extract data from current vm execution state");
//Test
StackFrame stackFrame = null;
try {
stackFrame = mainThread.frame(0);
System.out.println(stackFrame.location());
} catch (IncompatibleThreadStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Resume vm for code execution
vm.resume();
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//StepRequest is done, remove it from event queue to prevent lock
mgr.deleteEventRequest(stepRequest);
}
//Debugging has ended, we free our remote vm
try{
vm.dispose();
}catch(VMDisconnectedException e){
}
//Print results
//TODO
System.out.println("TODO - print results...");
}
}
不幸的是,如果我删除 Thread.sleep(),我会在一行中收到 IncompatibleThreadStateException
stackFrame = mainThread.frame(0);
例外:
com.sun.jdi.IncompatibleThreadStateException at com.sun.tools.jdi.ThreadReferenceImpl.privateFrames(ThreadReferenceImpl.java:436)
at com.sun.tools.jdi.ThreadReferenceImpl.frame(ThreadReferenceImpl.java:355)
at VMStart.main(VMStart.java:143)
怎么了?我是否必须等待某个事件才能更进一步?