Skip to content

Commit 31dd8ee

Browse files
authored
feat: support suspend all setting (#619)
* feat: support suspend all setting * fix: handle stoppedEvent for suspend policy change * fix: not allow change policy during a live debug session * fix: format issue * fix: let step command execute for current thread * fix: remove unused import * fix: apply suspend policy to exception breakpoint * fix: improve naming
1 parent 9623b8f commit 31dd8ee

15 files changed

+117
-42
lines changed

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,28 @@ public class Breakpoint implements IBreakpoint {
4242
private String condition = null;
4343
private String logMessage = null;
4444
private HashMap<Object, Object> propertyMap = new HashMap<>();
45+
private final boolean suspendAllThreads;
4546

4647
private boolean async = false;
4748

48-
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) {
49-
this(vm, eventHub, className, lineNumber, 0, null);
49+
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, boolean suspendAllThreads) {
50+
this(vm, eventHub, className, lineNumber, 0, null, suspendAllThreads);
5051
}
5152

52-
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount) {
53-
this(vm, eventHub, className, lineNumber, hitCount, null);
53+
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, boolean suspendAllThreads) {
54+
this(vm, eventHub, className, lineNumber, hitCount, null, suspendAllThreads);
5455
}
5556

56-
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) {
57-
this(vm, eventHub, className, lineNumber, hitCount, condition, null);
57+
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount,
58+
String condition, boolean suspendAllThreads) {
59+
this(vm, eventHub, className, lineNumber, hitCount, condition, null, suspendAllThreads);
5860
}
5961

60-
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition, String logMessage) {
62+
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount,
63+
String condition, String logMessage, boolean suspendAllThreads) {
6164
this.vm = vm;
6265
this.eventHub = eventHub;
66+
this.suspendAllThreads = suspendAllThreads;
6367
String contextClass = className;
6468
String methodName = null;
6569
String methodSignature = null;
@@ -79,13 +83,15 @@ public class Breakpoint implements IBreakpoint {
7983
this.logMessage = logMessage;
8084
}
8185

82-
Breakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) {
86+
Breakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount,
87+
String condition, String logMessage, boolean suspendAllThreads) {
8388
this.vm = vm;
8489
this.eventHub = eventHub;
8590
this.sourceLocation = sourceLocation;
8691
this.hitCount = hitCount;
8792
this.condition = condition;
8893
this.logMessage = logMessage;
94+
this.suspendAllThreads = suspendAllThreads;
8995
}
9096

9197
// IDebugResource
@@ -203,6 +209,19 @@ public void setAsync(boolean async) {
203209
this.async = async;
204210
}
205211

212+
@Override
213+
public void setSuspendPolicy(String policy) {
214+
}
215+
216+
@Override
217+
public String getSuspendPolicy() {
218+
return suspendAllThreads ? "SUSPEND_ALL" : "SUSPEND_EVENT_THREAD";
219+
}
220+
221+
protected boolean suspendAllThreads() {
222+
return suspendAllThreads;
223+
}
224+
206225
@Override
207226
public CompletableFuture<IBreakpoint> install() {
208227
// It's possible that different class loaders create new class with the same name.
@@ -412,7 +431,11 @@ private CompletableFuture<List<BreakpointRequest>> createBreakpointRequests(List
412431

413432
newLocations.forEach(location -> {
414433
BreakpointRequest request = vm.eventRequestManager().createBreakpointRequest(location);
415-
request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD);
434+
if ("SUSPEND_ALL".equals(getSuspendPolicy())) {
435+
request.setSuspendPolicy(BreakpointRequest.SUSPEND_ALL);
436+
} else {
437+
request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD);
438+
}
416439
if (hitCount > 0) {
417440
request.addCountFilter(hitCount);
418441
}

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ public class DebugSession implements IDebugSession {
3636
private EventHub eventHub = new EventHub();
3737
private List<EventRequest> eventRequests = new ArrayList<>();
3838
private List<Disposable> subscriptions = new ArrayList<>();
39+
private final boolean suspendAllThreads;
3940

4041
public DebugSession(VirtualMachine virtualMachine) {
4142
vm = virtualMachine;
43+
// Capture suspend policy at session start - this persists for the session lifetime
44+
this.suspendAllThreads = DebugSettings.getCurrent().suspendAllThreads;
4245
}
4346

4447
@Override
@@ -128,17 +131,17 @@ public void terminate() {
128131

129132
@Override
130133
public IBreakpoint createBreakpoint(JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) {
131-
return new EvaluatableBreakpoint(vm, this.getEventHub(), sourceLocation, hitCount, condition, logMessage);
134+
return new EvaluatableBreakpoint(vm, this.getEventHub(), sourceLocation, hitCount, condition, logMessage, suspendAllThreads);
132135
}
133136

134137
@Override
135138
public IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage) {
136-
return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage);
139+
return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage, suspendAllThreads);
137140
}
138141

139142
@Override
140143
public IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount) {
141-
return new Watchpoint(vm, this.getEventHub(), className, fieldName, accessType, condition, hitCount);
144+
return new Watchpoint(vm, this.getEventHub(), className, fieldName, accessType, condition, hitCount, suspendAllThreads);
142145
}
143146

144147
@Override
@@ -185,7 +188,7 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught
185188

186189
if (exceptionTypes == null || exceptionTypes.length == 0) {
187190
ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught);
188-
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
191+
request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD);
189192
if (classFilters != null) {
190193
for (String classFilter : classFilters) {
191194
request.addClassFilter(classFilter);
@@ -260,17 +263,22 @@ public VirtualMachine getVM() {
260263
return vm;
261264
}
262265

266+
@Override
267+
public boolean shouldSuspendAllThreads() {
268+
return suspendAllThreads;
269+
}
270+
263271
@Override
264272
public IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition,
265273
int hitCount) {
266-
return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount);
274+
return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount, suspendAllThreads);
267275
}
268276

269277
private void createExceptionBreakpoint(ReferenceType refType, boolean notifyCaught, boolean notifyUncaught,
270278
String[] classFilters, String[] classExclusionFilters) {
271279
EventRequestManager manager = vm.eventRequestManager();
272280
ExceptionRequest request = manager.createExceptionRequest(refType, notifyCaught, notifyUncaught);
273-
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
281+
request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD);
274282
if (classFilters != null) {
275283
for (String classFilter : classFilters) {
276284
request.addClassFilter(classFilter);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public final class DebugSettings {
4545
public int jdwpRequestTimeout = 3000;
4646
public AsyncMode asyncJDWP = AsyncMode.OFF;
4747
public Switch debugSupportOnDecompiledSource = Switch.OFF;
48+
public boolean suspendAllThreads = false;
4849

4950
public static DebugSettings getCurrent() {
5051
return current;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ private static StepRequest createStepRequest(ThreadReference thread, int stepSiz
394394
request.addClassExclusionFilter(exclusionFilter);
395395
}
396396
}
397+
// Note: suspend policy will be set by the caller (StepRequestHandler) based on session settings
397398
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
398399
request.addCountFilter(1);
399400

@@ -415,7 +416,7 @@ public static CompletableFuture<Long> stopOnEntry(IDebugSession debugSession, St
415416
EventRequestManager manager = debugSession.getVM().eventRequestManager();
416417
MethodEntryRequest request = manager.createMethodEntryRequest();
417418
request.addClassFilter(mainClass);
418-
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
419+
request.setSuspendPolicy(debugSession.shouldSuspendAllThreads() ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD);
419420

420421
debugSession.getEventHub().events().filter(debugEvent -> {
421422
return debugEvent.event instanceof MethodEntryEvent && request.equals(debugEvent.event.request());

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,28 @@ public class EvaluatableBreakpoint extends Breakpoint implements IEvaluatableBre
2929
private Object compiledLogpointExpression = null;
3030
private Map<Long, Object> compiledExpressions = new ConcurrentHashMap<>();
3131

32-
EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) {
33-
this(vm, eventHub, className, lineNumber, 0, null);
32+
EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, boolean suspendAllThreads) {
33+
this(vm, eventHub, className, lineNumber, 0, null, suspendAllThreads);
3434
}
3535

36-
EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount) {
37-
this(vm, eventHub, className, lineNumber, hitCount, null);
36+
EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, boolean suspendAllThreads) {
37+
this(vm, eventHub, className, lineNumber, hitCount, null, suspendAllThreads);
3838
}
3939

4040
EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount,
41-
String condition) {
42-
this(vm, eventHub, className, lineNumber, hitCount, condition, null);
41+
String condition, boolean suspendAllThreads) {
42+
this(vm, eventHub, className, lineNumber, hitCount, condition, null, suspendAllThreads);
4343
}
4444

4545
EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount,
46-
String condition, String logMessage) {
47-
super(vm, eventHub, className, lineNumber, hitCount, condition, logMessage);
46+
String condition, String logMessage, boolean suspendAllThreads) {
47+
super(vm, eventHub, className, lineNumber, hitCount, condition, logMessage, suspendAllThreads);
4848
this.eventHub = eventHub;
4949
}
5050

5151
EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount,
52-
String condition, String logMessage) {
53-
super(vm, eventHub, sourceLocation, hitCount, condition, logMessage);
52+
String condition, String logMessage, boolean suspendAllThreads) {
53+
super(vm, eventHub, sourceLocation, hitCount, condition, logMessage, suspendAllThreads);
5454
this.eventHub = eventHub;
5555
}
5656

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,11 @@ default void setAsync(boolean async) {
5555
default boolean async() {
5656
return false;
5757
}
58+
59+
default void setSuspendPolicy(String policy) {
60+
}
61+
62+
default String getSuspendPolicy() {
63+
return null;
64+
}
5865
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,10 @@ void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, Strin
5252
IEventHub getEventHub();
5353

5454
VirtualMachine getVM();
55+
56+
/**
57+
* Returns whether breakpoints should suspend all threads or just the event thread.
58+
* This value is captured at session start and persists for the session lifetime.
59+
*/
60+
boolean shouldSuspendAllThreads();
5561
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoi
4444
private String condition;
4545
private int hitCount;
4646
private boolean async = false;
47+
private final boolean suspendAllThreads;
4748

4849
private HashMap<Object, Object> propertyMap = new HashMap<>();
4950
private Object compiledConditionalExpression = null;
@@ -53,7 +54,7 @@ public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoi
5354
private List<Disposable> subscriptions = new ArrayList<>();
5455

5556
public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, String functionName,
56-
String condition, int hitCount) {
57+
String condition, int hitCount, boolean suspendAllThreads) {
5758
Objects.requireNonNull(vm);
5859
Objects.requireNonNull(eventHub);
5960
Objects.requireNonNull(className);
@@ -64,6 +65,7 @@ public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className,
6465
this.functionName = functionName;
6566
this.condition = condition;
6667
this.hitCount = hitCount;
68+
this.suspendAllThreads = suspendAllThreads;
6769
}
6870

6971
@Override
@@ -262,7 +264,7 @@ private Optional<MethodEntryRequest> createMethodEntryRequest0(ReferenceType typ
262264
MethodEntryRequest request = vm.eventRequestManager().createMethodEntryRequest();
263265

264266
request.addClassFilter(type);
265-
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
267+
request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD);
266268
if (hitCount > 0) {
267269
request.addCountFilter(hitCount);
268270
}

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,22 @@ public class Watchpoint implements IWatchpoint, IEvaluatableBreakpoint {
4646
private HashMap<Object, Object> propertyMap = new HashMap<>();
4747
private Object compiledConditionalExpression = null;
4848
private Map<Long, Object> compiledExpressions = new ConcurrentHashMap<>();
49+
private final boolean suspendAllThreads;
4950

5051
// IDebugResource
5152
private List<EventRequest> requests = new ArrayList<>();
5253
private List<Disposable> subscriptions = new ArrayList<>();
5354

54-
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName) {
55-
this(vm, eventHub, className, fieldName, "write");
55+
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, boolean suspendAllThreads) {
56+
this(vm, eventHub, className, fieldName, "write", suspendAllThreads);
5657
}
5758

58-
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType) {
59-
this(vm, eventHub, className, fieldName, accessType, null, 0);
59+
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType, boolean suspendAllThreads) {
60+
this(vm, eventHub, className, fieldName, accessType, null, 0, suspendAllThreads);
6061
}
6162

62-
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType, String condition, int hitCount) {
63+
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType,
64+
String condition, int hitCount, boolean suspendAllThreads) {
6365
Objects.requireNonNull(vm);
6466
Objects.requireNonNull(eventHub);
6567
Objects.requireNonNull(className);
@@ -71,6 +73,7 @@ public class Watchpoint implements IWatchpoint, IEvaluatableBreakpoint {
7173
this.accessType = accessType;
7274
this.condition = condition;
7375
this.hitCount = hitCount;
76+
this.suspendAllThreads = suspendAllThreads;
7477
}
7578

7679
@Override
@@ -212,7 +215,7 @@ private List<WatchpointRequest> createWatchpointRequests(ReferenceType type) {
212215
}
213216

214217
watchpointRequests.forEach(request -> {
215-
request.setSuspendPolicy(WatchpointRequest.SUSPEND_EVENT_THREAD);
218+
request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD);
216219
if (hitCount > 0) {
217220
request.addCountFilter(hitCount);
218221
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.sun.jdi.event.ThreadStartEvent;
4141
import com.sun.jdi.event.VMDeathEvent;
4242
import com.sun.jdi.event.VMDisconnectEvent;
43+
import com.sun.jdi.request.EventRequest;
4344
import com.sun.jdi.event.VMStartEvent;
4445

4546
public class ConfigurationDoneRequestHandler implements IDebugRequestHandler {
@@ -119,7 +120,9 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
119120
((ExceptionEvent) event).catchLocation() == null);
120121
context.getExceptionManager().setException(thread.uniqueID(), jdiException);
121122
context.getThreadCache().addEventThread(thread, "exception");
122-
context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID()));
123+
boolean allThreadsStopped = event.request() != null
124+
&& event.request().suspendPolicy() == EventRequest.SUSPEND_ALL;
125+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID(), allThreadsStopped));
123126
debugEvent.shouldResume = false;
124127
} else {
125128
isImportantEvent = false;

0 commit comments

Comments
 (0)