使用JDI监听Java程序运行

转载于: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去监视程序的运行的。

 

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

Java代码  收藏代码
  1. package test;  
  2.   
  3. public class Test {  
  4.   
  5.     public static void main(String[] args) {  
  6.         new Thread() {  
  7.             @Override  
  8.             public void run() {  
  9.                 Test test = new Test();  
  10.                 while (true) {  
  11.                     try {  
  12.                         sleep(5000);  
  13.                     } catch (InterruptedException e) {  
  14.                         e.printStackTrace();  
  15.                     }  
  16.                     test.printHello();  
  17.                 }  
  18.             }  
  19.         }.start();  
  20.     }  
  21.   
  22.     protected void printHello() {  
  23.         System.out.println("hello");  
  24.   
  25.     }  
  26.   
  27. }  

 

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

 

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

 

看看JDI的定义:

Java代码  收藏代码
  1. JDI - Java Debug Interface   
  2. Defines a high-level Java language interface which tool developers can easily use to write remote debugger applications.   

 

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

Java代码  收藏代码
  1. java -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8800 -cp . test.Test  

 

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

 

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

 

Java代码  收藏代码
  1. Listening for transport dt_socket at address: 8800  
  2. hello  
  3. hello  
  4. hello  

 

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

一、取得连接器

Java代码  收藏代码
  1. VirtualMachineManager vmm = Bootstrap.virtualMachineManager();  
  2. List<AttachingConnector> connectors = vmm.attachingConnectors();  
  3. SocketAttachingConnector sac = null;  
  4. for (AttachingConnector ac : connectors) {  
  5.     if (ac instanceof SocketAttachingConnector) {  
  6.         sac = (SocketAttachingConnector) ac;  
  7.         break;  
  8.     }  
  9. }  
  10. if (sac == null) {  
  11.     System.out.println("JDI error");  
  12.     return;  
  13. }  

 

二、连接到远程虚拟器

Java代码  收藏代码
  1. Map arguments = sac.defaultArguments();  
  2. Connector.Argument hostArg = (Connector.Argument) arguments.get(HOST);  
  3. Connector.Argument portArg = (Connector.Argument) arguments.get(PORT);  
  4.   
  5. hostArg.setValue("127.0.0.1");  
  6. portArg.setValue(String.valueOf(8800));  
  7.   
  8. vm = sac.attach(arguments);  

 

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

Java代码  收藏代码
  1. List<ReferenceType> classesByName = vm.classesByName("test.Test");  
  2. if (classesByName == null || classesByName.size() == 0) {  
  3.     System.out.println("No class found");  
  4.     return;  
  5. }  
  6. ReferenceType rt = classesByName.get(0);  
  7. List<Method> methodsByName = rt.methodsByName("printHello");  
  8. if (methodsByName == null || methodsByName.size() == 0) {  
  9.     System.out.println("No method found");  
  10.     return;  
  11. }  
  12. Method method = methodsByName.get(0);  

 

四、注册监听

Java代码  收藏代码
  1. vm.setDebugTraceMode(VirtualMachine.TRACE_EVENTS);  
  2. vm.resume();  
  3. EventRequestManager erm = vm.eventRequestManager();  
  4.   
  5. MethodEntryRequest methodEntryRequest = erm.createMethodEntryRequest();  
  6. methodEntryRequest.addClassFilter(rt);  
  7. methodEntryRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);  
  8. methodEntryRequest.enable();  
  9.   
  10. BreakpointRequest breakpointRequest = erm  
  11.         .createBreakpointRequest(method.location());  
  12. breakpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);  
  13. breakpointRequest.enable();  
  14.   
  15. eventLoop();  

 

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

 

四、eventLoop()的实现

Java代码  收藏代码
  1. private static void eventLoop() throws Exception {  
  2.     eventQueue = vm.eventQueue();  
  3.     while (true) {  
  4.         if (vmExit == true) {  
  5.             break;  
  6.         }  
  7.         eventSet = eventQueue.remove();  
  8.         EventIterator eventIterator = eventSet.eventIterator();  
  9.         while (eventIterator.hasNext()) {  
  10.             Event event = (Event) eventIterator.next();  
  11.             execute(event);  
  12.         }  
  13.     }  
  14. }  
  15.   
  16. private static void execute(Event event) throws Exception {  
  17.     if (event instanceof VMStartEvent) {  
  18.         System.out.println("VM started");  
  19.         eventSet.resume();  
  20.     } else if (event instanceof BreakpointEvent) {  
  21.         System.out  
  22.                 .println("Reach Method printHello of test.Test");  
  23.         eventSet.resume();  
  24.     } else if (event instanceof MethodEntryEvent) {  
  25.         MethodEntryEvent mee = (MethodEntryEvent) event;  
  26.         Method method = mee.method();  
  27.         System.out.println(method.name() + " was Entered!");  
  28.         eventSet.resume();  
  29.     } else if (event instanceof VMDisconnectEvent) {  
  30.         vmExit = true;  
  31.     } else {  
  32.         eventSet.resume();  
  33.     }  
  34. }  

 

最后看输出:

Java代码  收藏代码
  1. [JDI: EventSet: SUSPEND_EVENT_THREAD]  
  2. [JDI: Event: MethodEntryEvent@test.Test:23 in thread Thread-0]  
  3. [JDI: Event: BreakpointEvent@test.Test:23 in thread Thread-0]  
  4. printHello was Entered!  
  5. Reach Method printHello of test.Test  
  6. [JDI: EventSet: SUSPEND_EVENT_THREAD]  
  7. printHello was Entered!  
  8. [JDI: Event: MethodEntryEvent@test.Test:23 in thread Thread-0]  
  9. [JDI: Event: BreakpointEvent@test.Test:23 in thread Thread-0]  
  10. Reach Method printHello of test.Test  

 

你可能感兴趣的:(使用JDI监听Java程序运行)