使用JDI监听Java程序运行

Java虚拟机提供了一套用于调试(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去监视程序的运行的。

 

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

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的定义:

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

 

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

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

 

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

 

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

 

Listening for transport dt_socket at address: 8800
hello
hello
hello

 

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

一、取得连接器

		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;
		}

 

二、连接到远程虚拟器

		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);

 

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

		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);

 

四、注册监听

		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()的实现

	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();
		}
	}

 

最后看输出:

[JDI: EventSet: SUSPEND_EVENT_THREAD]
[JDI: Event: [email protected]:23 in thread Thread-0]
[JDI: Event: [email protected]:23 in thread Thread-0]
printHello was Entered!
Reach Method printHello of test.Test
[JDI: EventSet: SUSPEND_EVENT_THREAD]
printHello was Entered!
[JDI: Event: [email protected]:23 in thread Thread-0]
[JDI: Event: [email protected]:23 in thread Thread-0]
Reach Method printHello of test.Test

 

 

你可能感兴趣的:(java)