您的位置:首页 > 编程语言 > Java开发

使用JDI监听Java程序运行

2013-01-30 11:32 218 查看
va虚拟机提供了一套用于调试(JVMDI)和监视(JVMPI)的接口,Java5之后统一为JVMTI: http://docs.oracle.com/javase/1.5.0/docs/guide/jvmti/

其中JVMDI分为三个部分:JVMDI,JDWP和JDI . http://docs.oracle.com/javase/1.4.2/docs/guide/jpda/architecture.html
这篇就是简单的介绍一下怎么使用JDI去监视程序的运行的。

首先假设有一个简单的程序:

Java代码


package test;

public class Test {

public static void main(String[] args) {
new Thread() {
@Override
public void run() {
Test test = new Test();
while (true) {
try {
sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.printHello();
}
}
}.start();
}

protected void printHello() {
System.out.println("hello");

}

}

程序中,每隔五秒种,printHello()方法就会执行一次。

如果你希望每次printHell()被执行的时候通知你一下,在不修改代码的情况下,你要怎么办?没办法吧?

看看JDI的定义:

Java代码


JDI - Java Debug Interface
Defines a high-level Java language interface which tool developers can easily use to write remote debugger applications.

所以首先,我们先以远程调试的方式启动上面的Test类:

Java代码


java -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8800 -cp . test.Test

大致就是以socket传输的方式调试Test类,调试的连接端口为8800,并且连接过程不挂起。

一量启动,就可以看到如下的输出:

Java代码


Listening for transport dt_socket at address: 8800
hello
hello
hello

这样Server被调试端就准备好了,下面就是写监听端了。这里就要用到jdk中提供的JDI接口了。要使用此接口,我们需要在类路径里包含JDK下的tools.jar等包,可以在<JDK>/lib目录下找着。

一、取得连接器

Java代码


VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
List<AttachingConnector> connectors = vmm.attachingConnectors();
SocketAttachingConnector sac = null;
for (AttachingConnector ac : connectors) {
if (ac instanceof SocketAttachingConnector) {
sac = (SocketAttachingConnector) ac;
break;
}
}
if (sac == null) {
System.out.println("JDI error");
return;
}

二、连接到远程虚拟器

Java代码


Map arguments = sac.defaultArguments();
Connector.Argument hostArg = (Connector.Argument) arguments.get(HOST);
Connector.Argument portArg = (Connector.Argument) arguments.get(PORT);

hostArg.setValue("127.0.0.1");
portArg.setValue(String.valueOf(8800));

vm = sac.attach(arguments);

三、取得要关注的类和方法

Java代码


List<ReferenceType> classesByName = vm.classesByName("test.Test");
if (classesByName == null || classesByName.size() == 0) {
System.out.println("No class found");
return;
}
ReferenceType rt = classesByName.get(0);
List<Method> methodsByName = rt.methodsByName("printHello");
if (methodsByName == null || methodsByName.size() == 0) {
System.out.println("No method found");
return;
}
Method method = methodsByName.get(0);

四、注册监听

Java代码


vm.setDebugTraceMode(VirtualMachine.TRACE_EVENTS);
vm.resume();
EventRequestManager erm = vm.eventRequestManager();

MethodEntryRequest methodEntryRequest = erm.createMethodEntryRequest();
methodEntryRequest.addClassFilter(rt);
methodEntryRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
methodEntryRequest.enable();

BreakpointRequest breakpointRequest = erm
.createBreakpointRequest(method.location());
breakpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
breakpointRequest.enable();

eventLoop();

这里监听每次方法入口的时候,以及在方法上注册一个断点,发一个通知。

四、eventLoop()的实现

Java代码


private static void eventLoop() throws Exception {
eventQueue = vm.eventQueue();
while (true) {
if (vmExit == true) {
break;
}
eventSet = eventQueue.remove();
EventIterator eventIterator = eventSet.eventIterator();
while (eventIterator.hasNext()) {
Event event = (Event) eventIterator.next();
execute(event);
}
}
}

private static void execute(Event event) throws Exception {
if (event instanceof VMStartEvent) {
System.out.println("VM started");
eventSet.resume();
} else if (event instanceof BreakpointEvent) {
System.out
.println("Reach Method printHello of test.Test");
eventSet.resume();
} else if (event instanceof MethodEntryEvent) {
MethodEntryEvent mee = (MethodEntryEvent) event;
Method method = mee.method();
System.out.println(method.name() + " was Entered!");
eventSet.resume();
} else if (event instanceof VMDisconnectEvent) {
vmExit = true;
} else {
eventSet.resume();
}
}

最后看输出:

Java代码


[JDI: EventSet: SUSPEND_EVENT_THREAD]
[JDI: Event: MethodEntryEvent@test.Test:23 in thread Thread-0]
[JDI: Event: BreakpointEvent@test.Test:23 in thread Thread-0]
printHello was Entered!
Reach Method printHello of test.Test
[JDI: EventSet: SUSPEND_EVENT_THREAD]
printHello was Entered!
[JDI: Event: MethodEntryEvent@test.Test:23 in thread Thread-0]
[JDI: Event: BreakpointEvent@test.Test:23 in thread Thread-0]
Reach Method printHello of test.Test
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: