Skip to content

Commit 9df6f5d

Browse files
Support specifying the exception types you want to break on (#481)
* support specifying the exception types you want to break on
1 parent 31cc033 commit 9df6f5d

File tree

6 files changed

+104
-18
lines changed

6 files changed

+104
-18
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* @testforstephen @jdneo @Eskibear @CsCherrYY
1+
* @testforstephen @jdneo

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,26 @@
1616
import java.util.ArrayList;
1717
import java.util.List;
1818

19+
import org.apache.commons.lang3.StringUtils;
20+
1921
import com.sun.jdi.ObjectCollectedException;
22+
import com.sun.jdi.ReferenceType;
2023
import com.sun.jdi.ThreadReference;
24+
import com.sun.jdi.VMDisconnectedException;
2125
import com.sun.jdi.VirtualMachine;
26+
import com.sun.jdi.event.ClassPrepareEvent;
27+
import com.sun.jdi.request.ClassPrepareRequest;
2228
import com.sun.jdi.request.EventRequest;
2329
import com.sun.jdi.request.EventRequestManager;
2430
import com.sun.jdi.request.ExceptionRequest;
2531

32+
import io.reactivex.disposables.Disposable;
33+
2634
public class DebugSession implements IDebugSession {
2735
private VirtualMachine vm;
2836
private EventHub eventHub = new EventHub();
37+
private List<EventRequest> eventRequests = new ArrayList<>();
38+
private List<Disposable> subscriptions = new ArrayList<>();
2939

3040
public DebugSession(VirtualMachine virtualMachine) {
3141
vm = virtualMachine;
@@ -136,9 +146,27 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught
136146

137147
@Override
138148
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) {
139155
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+
142170
// When no exception breakpoints are requested, no need to create an empty exception request.
143171
if (notifyCaught || notifyUncaught) {
144172
// 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
153181
// a thread to be available, and queries it by calling allThreads().
154182
// See org.eclipse.debug.jdi.tests.AbstractJDITest for the example.
155183

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+
}
162191
}
192+
if (classExclusionFilters != null) {
193+
for (String exclusionFilter : classExclusionFilters) {
194+
request.addClassExclusionFilter(exclusionFilter);
195+
}
196+
}
197+
request.enable();
198+
return;
163199
}
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);
167224
}
168225
}
169-
request.enable();
170226
}
171227
}
172228

@@ -195,4 +251,22 @@ public IMethodBreakpoint createFunctionBreakpoint(String className, String funct
195251
int hitCount) {
196252
return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount);
197253
}
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+
}
198272
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import com.google.gson.JsonSyntaxException;
2020
import com.google.gson.annotations.SerializedName;
2121
import com.microsoft.java.debug.core.protocol.JsonUtils;
22-
import com.microsoft.java.debug.core.protocol.Requests.ClassFilters;
22+
import com.microsoft.java.debug.core.protocol.Requests.ExceptionFilters;
2323
import com.microsoft.java.debug.core.protocol.Requests.StepFilters;
2424

2525
public final class DebugSettings {
@@ -39,7 +39,7 @@ public final class DebugSettings {
3939
public String javaHome;
4040
public HotCodeReplace hotCodeReplace = HotCodeReplace.MANUAL;
4141
public StepFilters stepFilters = new StepFilters();
42-
public ClassFilters exceptionFilters = new ClassFilters();
42+
public ExceptionFilters exceptionFilters = new ExceptionFilters();
4343
public boolean exceptionFiltersUpdated = false;
4444
public int limitOfVariablesPerJdwpRequest = 100;
4545
public int jdwpRequestTimeout = 3000;

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public interface IDebugSession {
3838

3939
void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters);
4040

41+
void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, String[] classFilters, String[] classExclusionFilters);
42+
4143
IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount);
4244

4345
Process process();

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
2727
import com.microsoft.java.debug.core.protocol.Messages.Response;
2828
import com.microsoft.java.debug.core.protocol.Requests.Arguments;
29-
import com.microsoft.java.debug.core.protocol.Requests.ClassFilters;
3029
import com.microsoft.java.debug.core.protocol.Requests.Command;
30+
import com.microsoft.java.debug.core.protocol.Requests.ExceptionFilters;
3131
import com.microsoft.java.debug.core.protocol.Requests.SetExceptionBreakpointsArguments;
3232
import com.microsoft.java.debug.core.protocol.Types;
3333
import com.sun.jdi.event.VMDeathEvent;
@@ -77,10 +77,11 @@ public synchronized CompletableFuture<Response> handle(Command command, Argument
7777
}
7878

7979
private void setExceptionBreakpoints(IDebugSession debugSession, boolean notifyCaught, boolean notifyUncaught) {
80-
ClassFilters exceptionFilters = DebugSettings.getCurrent().exceptionFilters;
80+
ExceptionFilters exceptionFilters = DebugSettings.getCurrent().exceptionFilters;
81+
String[] exceptionTypes = (exceptionFilters == null ? null : exceptionFilters.exceptionTypes);
8182
String[] classFilters = (exceptionFilters == null ? null : exceptionFilters.allowClasses);
8283
String[] classExclusionFilters = (exceptionFilters == null ? null : exceptionFilters.skipClasses);
83-
debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, classFilters, classExclusionFilters);
84+
debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters);
8485
}
8586

8687
@Override

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ public static class ClassFilters {
6767
public String[] skipClasses = new String[0];
6868
}
6969

70+
public static class ExceptionFilters extends ClassFilters {
71+
/**
72+
* Specifies that exceptions which are instances of refType will be reported.
73+
* Note: this will include instances of sub-types. If null, all instances
74+
* will be reported.
75+
*/
76+
public String[] exceptionTypes = new String[0];
77+
}
78+
7079
public static class StepFilters extends ClassFilters {
7180
/**
7281
* Deprecated - please use {@link ClassFilters#skipClasses } instead.

0 commit comments

Comments
 (0)