|
16 | 16 | import java.util.ArrayList; |
17 | 17 | import java.util.List; |
18 | 18 |
|
| 19 | +import org.apache.commons.lang3.StringUtils; |
| 20 | + |
19 | 21 | import com.sun.jdi.ObjectCollectedException; |
| 22 | +import com.sun.jdi.ReferenceType; |
20 | 23 | import com.sun.jdi.ThreadReference; |
| 24 | +import com.sun.jdi.VMDisconnectedException; |
21 | 25 | import com.sun.jdi.VirtualMachine; |
| 26 | +import com.sun.jdi.event.ClassPrepareEvent; |
| 27 | +import com.sun.jdi.request.ClassPrepareRequest; |
22 | 28 | import com.sun.jdi.request.EventRequest; |
23 | 29 | import com.sun.jdi.request.EventRequestManager; |
24 | 30 | import com.sun.jdi.request.ExceptionRequest; |
25 | 31 |
|
| 32 | +import io.reactivex.disposables.Disposable; |
| 33 | + |
26 | 34 | public class DebugSession implements IDebugSession { |
27 | 35 | private VirtualMachine vm; |
28 | 36 | private EventHub eventHub = new EventHub(); |
| 37 | + private List<EventRequest> eventRequests = new ArrayList<>(); |
| 38 | + private List<Disposable> subscriptions = new ArrayList<>(); |
29 | 39 |
|
30 | 40 | public DebugSession(VirtualMachine virtualMachine) { |
31 | 41 | vm = virtualMachine; |
@@ -136,9 +146,27 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught |
136 | 146 |
|
137 | 147 | @Override |
138 | 148 | public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters) { |
| 149 | + setExceptionBreakpoints(notifyCaught, notifyUncaught, null, classFilters, classExclusionFilters); |
| 150 | + } |
| 151 | + |
| 152 | + @Override |
| 153 | + public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, |
| 154 | + String[] classFilters, String[] classExclusionFilters) { |
139 | 155 | EventRequestManager manager = vm.eventRequestManager(); |
140 | | - ArrayList<ExceptionRequest> legacy = new ArrayList<>(manager.exceptionRequests()); |
141 | | - manager.deleteEventRequests(legacy); |
| 156 | + |
| 157 | + try { |
| 158 | + ArrayList<ExceptionRequest> legacy = new ArrayList<>(manager.exceptionRequests()); |
| 159 | + manager.deleteEventRequests(legacy); |
| 160 | + manager.deleteEventRequests(eventRequests); |
| 161 | + } catch (VMDisconnectedException ex) { |
| 162 | + // ignore since removing breakpoints is meaningless when JVM is terminated. |
| 163 | + } |
| 164 | + subscriptions.forEach(subscription -> { |
| 165 | + subscription.dispose(); |
| 166 | + }); |
| 167 | + subscriptions.clear(); |
| 168 | + eventRequests.clear(); |
| 169 | + |
142 | 170 | // When no exception breakpoints are requested, no need to create an empty exception request. |
143 | 171 | if (notifyCaught || notifyUncaught) { |
144 | 172 | // from: https://www.javatips.net/api/REPLmode-master/src/jm/mode/replmode/REPLRunner.java |
@@ -153,20 +181,48 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught |
153 | 181 | // a thread to be available, and queries it by calling allThreads(). |
154 | 182 | // See org.eclipse.debug.jdi.tests.AbstractJDITest for the example. |
155 | 183 |
|
156 | | - // get only the uncaught exceptions |
157 | | - ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); |
158 | | - request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); |
159 | | - if (classFilters != null) { |
160 | | - for (String classFilter : classFilters) { |
161 | | - request.addClassFilter(classFilter); |
| 184 | + if (exceptionTypes == null || exceptionTypes.length == 0) { |
| 185 | + ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); |
| 186 | + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); |
| 187 | + if (classFilters != null) { |
| 188 | + for (String classFilter : classFilters) { |
| 189 | + request.addClassFilter(classFilter); |
| 190 | + } |
162 | 191 | } |
| 192 | + if (classExclusionFilters != null) { |
| 193 | + for (String exclusionFilter : classExclusionFilters) { |
| 194 | + request.addClassExclusionFilter(exclusionFilter); |
| 195 | + } |
| 196 | + } |
| 197 | + request.enable(); |
| 198 | + return; |
163 | 199 | } |
164 | | - if (classExclusionFilters != null) { |
165 | | - for (String exclusionFilter : classExclusionFilters) { |
166 | | - request.addClassExclusionFilter(exclusionFilter); |
| 200 | + |
| 201 | + for (String exceptionType : exceptionTypes) { |
| 202 | + if (StringUtils.isBlank(exceptionType)) { |
| 203 | + continue; |
| 204 | + } |
| 205 | + |
| 206 | + // register exception breakpoint in the future loaded classes. |
| 207 | + ClassPrepareRequest classPrepareRequest = manager.createClassPrepareRequest(); |
| 208 | + classPrepareRequest.addClassFilter(exceptionType); |
| 209 | + classPrepareRequest.enable(); |
| 210 | + eventRequests.add(classPrepareRequest); |
| 211 | + |
| 212 | + Disposable subscription = eventHub.events() |
| 213 | + .filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent |
| 214 | + && eventRequests.contains(debugEvent.event.request())) |
| 215 | + .subscribe(debugEvent -> { |
| 216 | + ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; |
| 217 | + createExceptionBreakpoint(event.referenceType(), notifyCaught, notifyUncaught, classFilters, classExclusionFilters); |
| 218 | + }); |
| 219 | + subscriptions.add(subscription); |
| 220 | + |
| 221 | + // register exception breakpoint in the loaded classes. |
| 222 | + for (ReferenceType refType : vm.classesByName(exceptionType)) { |
| 223 | + createExceptionBreakpoint(refType, notifyCaught, notifyUncaught, classFilters, classExclusionFilters); |
167 | 224 | } |
168 | 225 | } |
169 | | - request.enable(); |
170 | 226 | } |
171 | 227 | } |
172 | 228 |
|
@@ -195,4 +251,22 @@ public IMethodBreakpoint createFunctionBreakpoint(String className, String funct |
195 | 251 | int hitCount) { |
196 | 252 | return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount); |
197 | 253 | } |
| 254 | + |
| 255 | + private void createExceptionBreakpoint(ReferenceType refType, boolean notifyCaught, boolean notifyUncaught, |
| 256 | + String[] classFilters, String[] classExclusionFilters) { |
| 257 | + EventRequestManager manager = vm.eventRequestManager(); |
| 258 | + ExceptionRequest request = manager.createExceptionRequest(refType, notifyCaught, notifyUncaught); |
| 259 | + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); |
| 260 | + if (classFilters != null) { |
| 261 | + for (String classFilter : classFilters) { |
| 262 | + request.addClassFilter(classFilter); |
| 263 | + } |
| 264 | + } |
| 265 | + if (classExclusionFilters != null) { |
| 266 | + for (String exclusionFilter : classExclusionFilters) { |
| 267 | + request.addClassExclusionFilter(exclusionFilter); |
| 268 | + } |
| 269 | + } |
| 270 | + request.enable(); |
| 271 | + } |
198 | 272 | } |