引入:
上文主要讲解了JDI的连接模块来建立Debugger到Target VM之间的连接,这里主要讲解事件请求和处理模块。它们都在com.sun.jdi.event和com.sun.jdi.request包中。
分析:
Part 1:查看JDI中定义的事件类型
JDI中事件的接口叫Event .
public abstract interface Event extends Mirror { public abstract EventRequest request(); }
它定义了18种具体的事件类型,如下:
ClassPrepareEvent | 装载某个指定的类所引发的事件 |
ClassUnloadEvent | 卸载某个指定的类所引发的事件 |
BreakingpointEvent | 设置断点所引发的事件 |
ExceptionEvent | 目标虚拟机运行中抛出指定异常所引发的事件 |
MethodEntryEvent | 进入某个指定方法体时引发的事件 |
MethodExitEvent | 某个指定方法执行完成后引发的事件 |
MonitorContendedEnteredEvent | 线程已经进入某个指定 Monitor 资源所引发的事件 |
MonitorContendedEnterEvent | 线程将要进入某个指定 Monitor 资源所引发的事件 |
MonitorWaitedEvent | 线程完成对某个指定 Monitor 资源等待所引发的事件 |
MonitorWaitEvent | 线程开始等待对某个指定 Monitor 资源所引发的事件 |
StepEvent | 目标应用程序执行下一条指令或者代码行所引发的事件 |
AccessWatchpointEvent | 查看类的某个指定 Field 所引发的事件 |
ModificationWatchpointEvent | 修改类的某个指定 Field 值所引发的事件 |
ThreadDeathEvent | 某个指定线程运行完成所引发的事件 |
ThreadStartEvent | 某个指定线程开始运行所引发的事件 |
VMDeathEvent | 目标虚拟机停止运行所以的事件 |
VMDisconnectEvent | 目标虚拟机与调试器断开链接所引发的事件 |
VMStartEvent | 目标虚拟机初始化时所引发的事件 |
Part 2:事件集的概念
事件集是事件发送的最小单位,并且事件集一旦被创建,则不可以被修改。
public abstract interface EventSet extends Mirror, Set<Event> { public abstract int suspendPolicy(); public abstract EventIterator eventIterator(); public abstract void resume(); }
它也提供了迭代器来访问事件集内的事件。
Part 3:事件队列
事件队列(EventQueue)的拥有者是目标虚拟机,EventQueue 将这些事件集以“先进先出”策略依次地发送到调试器端。EventQueue 负责管理来自目标虚拟机的事件,一个被调试的目标虚拟机上有且仅有一个 EventQueue实例。特别地,随着一次事件集的发送,目标虚拟机上可能会有一部分的线程因此而被挂起。如果一直不恢复这些线程,有可能会导致目标虚拟机挂机。
Part 4: JDI事件请求
正如Part1 所说,Event接口定义了request方法,该方法会返回由调试器Debugger发出的针对该事件的事件请求(EventRequest)。事件请求是由调试器向目标虚拟机发出的,目的是请求目标虚拟机在发生指定的事件后通知调试器。只有当调试器发出的请求与目标虚拟机上发生的事件匹配时,这些事件才会被分发到各个事件集,进而等待发送至调试器端。
(注意:这里可能有点绕口,一句话概括就是 EventRequest总是由Debugger发向Target VM ,而当请求与目标虚拟机上发生事件匹配,则事件会被归到EventSet中,EventSet会被Target VM的EventQueue所管理,并且按照FIFO原则发送到Debugger)
当然了,Debugger发送给Target VM的所有事件请求,不一定Target VM 都感兴趣。因此JDI提供了事件的过滤机制,来删选出最终真正要发送给Target VM的事件。
EventRequest中提供了一组addXXXFilter()方法来给某个事件请求添加一个或者多个过滤器:
public void addCountFilter(int count) throws InvalidRequestStateException { checkDisabled(); if (this.fCountFilters == null) { this.fCountFilters = new ArrayList(); } this.fCountFilters.add(new Integer(count)); } public void addThreadFilter(ThreadReference threadFilter) throws ObjectCollectedException, VMMismatchException, InvalidRequestStateException { checkVM(threadFilter); checkDisabled(); if (threadFilter.isCollected()) throw new ObjectCollectedException(); if (this.fThreadFilters == null) { this.fThreadFilters = new ArrayList(); } this.fThreadFilters.add(threadFilter); } public void addClassFilter(ReferenceType filter) throws VMMismatchException, InvalidRequestStateException { checkVM(filter); checkDisabled(); if (this.fClassFilterRefs == null) { this.fClassFilterRefs = new ArrayList(); } this.fClassFilterRefs.add(filter); } public void addClassFilter(String filter) throws InvalidRequestStateException { checkDisabled(); if (this.fClassFilters == null) { this.fClassFilters = new ArrayList(); } this.fClassFilters.add(filter); } public void addClassExclusionFilter(String filter) throws InvalidRequestStateException { checkDisabled(); if (this.fClassExclusionFilters == null) { this.fClassExclusionFilters = new ArrayList(); } this.fClassExclusionFilters.add(filter); } public void addLocationFilter(LocationImpl location) throws VMMismatchException { checkDisabled(); checkVM(location); if (this.fLocationFilters == null) { this.fLocationFilters = new ArrayList(); } this.fLocationFilters.add(location); } public void addExceptionFilter(ReferenceTypeImpl refType, boolean notifyCaught, boolean notifyUncaught) throws VMMismatchException { checkDisabled(); if (refType != null) { checkVM(refType); } if (this.fExceptionFilters == null) { this.fExceptionFilters = new ArrayList(); } ExceptionFilter filter = new ExceptionFilter(); filter.fException = refType; filter.fNotifyCaught = notifyCaught; filter.fNotifyUncaught = notifyUncaught; this.fExceptionFilters.add(filter); } public void addFieldFilter(FieldImpl field) throws VMMismatchException { checkDisabled(); checkVM(field); if (this.fFieldFilters == null) { this.fFieldFilters = new ArrayList(); } this.fFieldFilters.add(field); } public void addStepFilter(ThreadReferenceImpl thread, int size, int depth) throws VMMismatchException { checkDisabled(); checkVM(thread); if (this.fThreadStepFilters == null) { this.fThreadStepFilters = new ArrayList(); } ThreadStepFilter filter = new ThreadStepFilter(); filter.fThread = thread; filter.fThreadStepSize = size; filter.fThreadStepDepth = depth; this.fThreadStepFilters.add(filter); } public void addInstanceFilter(ObjectReference instance) { checkDisabled(); checkVM(instance); if (this.fInstanceFilters == null) { this.fInstanceFilters = new ArrayList(); } this.fInstanceFilters.add(instance); } public void addSourceNameFilter(String pattern) { checkDisabled(); if (this.fSourceNameFilters == null) { this.fSourceNameFilters = new ArrayList(); } this.fSourceNameFilters.add(pattern); }
Part 5: Target VM对于事件请求(EventRequest)的管理
在JDI中,事件请求的管理是通过EventRequestManager来完成的。它有许多createXXXRequest方法来创建不同类型的事件请求,也有许多deleteXXXRequest方法来删除不同类型的事件请求,还有xxxRequests方法来列出各种类型的事件请求。
package com.sun.jdi.request; import com.sun.jdi.Field; import com.sun.jdi.Location; import com.sun.jdi.Mirror; import com.sun.jdi.ReferenceType; import com.sun.jdi.ThreadReference; import java.util.List; public abstract interface EventRequestManager extends Mirror { public abstract ClassPrepareRequest createClassPrepareRequest(); public abstract ClassUnloadRequest createClassUnloadRequest(); public abstract ThreadStartRequest createThreadStartRequest(); public abstract ThreadDeathRequest createThreadDeathRequest(); public abstract ExceptionRequest createExceptionRequest(ReferenceType paramReferenceType, boolean paramBoolean1, boolean paramBoolean2); public abstract MethodEntryRequest createMethodEntryRequest(); public abstract MethodExitRequest createMethodExitRequest(); public abstract MonitorContendedEnterRequest createMonitorContendedEnterRequest(); public abstract MonitorContendedEnteredRequest createMonitorContendedEnteredRequest(); public abstract MonitorWaitRequest createMonitorWaitRequest(); public abstract MonitorWaitedRequest createMonitorWaitedRequest(); public abstract StepRequest createStepRequest(ThreadReference paramThreadReference, int paramInt1, int paramInt2); public abstract BreakpointRequest createBreakpointRequest(Location paramLocation); public abstract AccessWatchpointRequest createAccessWatchpointRequest(Field paramField); public abstract ModificationWatchpointRequest createModificationWatchpointRequest(Field paramField); public abstract VMDeathRequest createVMDeathRequest(); public abstract void deleteEventRequest(EventRequest paramEventRequest); public abstract void deleteEventRequests(List<? extends EventRequest> paramList); public abstract void deleteAllBreakpoints(); public abstract List<StepRequest> stepRequests(); public abstract List<ClassPrepareRequest> classPrepareRequests(); public abstract List<ClassUnloadRequest> classUnloadRequests(); public abstract List<ThreadStartRequest> threadStartRequests(); public abstract List<ThreadDeathRequest> threadDeathRequests(); public abstract List<ExceptionRequest> exceptionRequests(); public abstract List<BreakpointRequest> breakpointRequests(); public abstract List<AccessWatchpointRequest> accessWatchpointRequests(); public abstract List<ModificationWatchpointRequest> modificationWatchpointRequests(); public abstract List<MethodEntryRequest> methodEntryRequests(); public abstract List<MethodExitRequest> methodExitRequests(); public abstract List<MonitorContendedEnterRequest> monitorContendedEnterRequests(); public abstract List<MonitorContendedEnteredRequest> monitorContendedEnteredRequests(); public abstract List<MonitorWaitRequest> monitorWaitRequests(); public abstract List<MonitorWaitedRequest> monitorWaitedRequests(); public abstract List<VMDeathRequest> vmDeathRequests(); }
有一点需要注意的是,这里由EventRequestManager创建的createXXXRequest的事件都是非激活的,因此这些事件请求当发送给Target VM不会起任何作用,除非调用EventRequest的setEnable(true)使得该事件进入激活状态。
Part 6: JDI中Debugger与Target VM之间的事件交互。
Step 1:Debugger调用Target VM的 eventQueue() 和 eventRequestManager() 分别获取唯一的 EventQueue 实例和 EventRequestManager 实例.
Step 2: Debugger通过 EventRequestManager 的 createXXXRequest() 创建需要的事件请求,并添加过滤器和设置挂起策略.
Step 3: Debugger通过EventQueue 获取来自Target VM的事件实例.
这个事件实例包含了许多信息,比如ThreadReference ,StackFrame,LocalVariable, Location,Method等,这些事件信息就包含了当前Target VM的一些现有状态信息和数据,Debugger就用这些数据交给Eclipse的相应的Debugger UI插件来显示结果。
总结:
在此文章中,我们涉及到了多个概念,这里把其中的类模型概括下: