参考:http://java.sun.com/javase/6/docs/jdk/api/jpda/jdi/index.html
JDI(Java Debug Interface)为Java调试器的开发提供了标准的接口,我们可以通过此构建标准的Java调试器。如JPDA架构图所示,通过Front-end、Transport(交互机制)和Back-end的交互,JDI对外使用Java语言提供了一个JVM TI的功能子集的高级接口(并非所有JVM TI功能都可以通过JDI访问到)。需要注意的是,JDI实现上不会太多考虑性能上的问题,因此一般不会使用JDI来构建性能剖析相关工具。
1.主要的API
- com.sun.jdi.connect.AttachingConnector:连接器,通过该连接器可以连接到一个运行中的JVM上
- com.sun.jdi.VirtualMachine:远程/本地运行JVM的映像,类似于JVM TI中的jvmEnv对象
- com.sun.jdi.request.EventRequestManager:事件请求管理器,通过该管理器进行事件注册
- com.sun.jdi.ThreadReference:线程引用,可以通过此类获得线程相关的信息,譬如线程状态、线程栈等
2.范例
我们通过一个范例来了解一下JDI的使用,本范例首先获取到当前所有线程的信息,然后注册方法进入和退出事件,同时打印进入和退出信息。(如下程序需要tools.jar包)
1)在被调试的JVM上启动调试Back end:在被调试的JVM启动参数中增加如下参数
具体参数的配置可参见《Connection and Invocation Details 》
2)连接到被调试的JVM上
由于Back-end的transport使用的是dt_socket的方式,首先需要获得一个dt_socket的AttachConnector
VirtualMachineManager vmManager = Bootstrap.virtualMachineManager();
List connectors = vmManager.attachingConnectors();
AttachingConnector socketAttachingConnector = null;
for (int i = 0; i < connectors.size(); i++)
{
Connector connector = (Connector) connectors.get(i);
Transport transport = connector.transport();
if (”dt_socket”.equals(transport.name()))
{
socketAttachingConnector = (AttachingConnector) connector;
break;
}
}
连接到被调试的JVM上
Map arguments = socketAttachingConnector.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(“8000”);
VirtualMachine jvm = socketAttachingConnector.attach(arguments);
3)打印当前线程信息
List<ThreadReference> threadReferences = jvm.allThreads();
for (ThreadReference tr : threadReferences)
{
System.out.print("Thread[" + tr.name() + "] : ");
//线程状态
switch (tr.status())
{
case ThreadReference.THREAD_STATUS_MONITOR:
System.out.println(" Waiting");
break;
case ThreadReference.THREAD_STATUS_NOT_STARTED:
System.out.println(" Not Start");
break;
case ThreadReference.THREAD_STATUS_RUNNING:
System.out.println(" Running");
break;
case ThreadReference.THREAD_STATUS_SLEEPING:
System.out.println(" Sleepping");
break;
case ThreadReference.THREAD_STATUS_UNKNOWN:
System.out.println(" Unknow");
break;
case ThreadReference.THREAD_STATUS_WAIT:
System.out.println(" Wait");
break;
case ThreadReference.THREAD_STATUS_ZOMBIE:
System.out.println(" Finish");
break;
}
boolean suspend = tr.isSuspended();
//注意,只有suspend的线程才能获得其线程栈,因此需要将其线suspend一下
if (!suspend)
{
tr.suspend();
}
List<StackFrame> frames = tr.frames();
for (StackFrame frame : frames)
{
System.out.println("-----"
+ frame.location().method().toString() + ":"
+ frame.location().lineNumber());
}
System.out.println("count:" + tr.entryCount());
if (!suspend)
{
tr.resume();
}
frames = null;
}
4)注册方法进入和退出事件,在实现调试器的时候,我们可以通过注册一个BreakPoint事件,在事件发生时挂住执行线程,然后检查事件发生对象的信息来实现最常见的断点调试功能,或者Step事件来完成单步执行功能
EventRequestManager eventRequestManager = jvm.eventRequestManager();
MethodEntryRequest methodEntryRequest = eventRequestManager.createMethodEntryRequest();
methodEntryRequest.addClassExclusionFilter("java.*");//设置过滤器,对过滤器中的Class不捕获其实践
methodEntryRequest.addClassExclusionFilter("sun.*");
methodEntryRequest.addClassExclusionFilter("javax.*");
methodEntryRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);//这个属性一定要设,
默认事件发生时会suspend住执行线程
methodEntryRequest.enable();
MethodExitRequest methodExitRequest = eventRequestManager.createMethodExitRequest();
methodExitRequest.addClassExclusionFilter("java.*");
methodExitRequest.addClassExclusionFilter("sun.*");
methodExitRequest.addClassExclusionFilter("javax.*");
methodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
methodExitRequest.enable();
5)处理事件
EventQueue eventQueue = jvm.eventQueue();
EventSet eventSet;
while (true)
{
eventSet = eventQueue.remove();
EventIterator eventIterator = eventSet.eventIterator();
while (eventIterator.hasNext())
{
Event event = (Event) eventIterator.next();
execute(event);
}
}
打印处理信息
private void execute(Event event) throws Exception
{
if (event instanceof MethodEntryEvent)
{
System.out.println("Mehtod Entry:" + ((MethodEntryEvent) event).method());
}
else if (event instanceof MethodExitEvent)
{
System.out.println("Mehtod Exi:" + ((MethodExitEvent) event).method());
}
}