使用JDI监听Java程序运行
转载于:http://liugang594.iteye.com/blog/1397811#bc2311991Java虚拟机提供了一套用于调试(JVMDI)和监视(JVMPI)的接口,Java5之后统一为JVMTI:http://docs.oracle.com/javase/1.5.0/docs/guide/jvmti/ 。 其中JVMDI分为三个部分:JVMDI
转载于:http://liugang594.iteye.com/blog/1397811#bc2311991
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: 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
更多推荐
所有评论(0)