[debugger] Adapt pause for breakpoint instrumentation checks

* Change method breakpoint mode to suspend thread in Pause command
* Move evaluation-skip logic from processLocatableEvent subcommand

Need for IDEA-370744

GitOrigin-RevId: 7acedb820d2957afd76a9ca8395f66893bf4317a
This commit is contained in:
Alexey.Merkulov
2026-02-16 19:06:25 +00:00
committed by intellij-monorepo-bot
parent 174490c8ed
commit ff1952e7e0
2 changed files with 57 additions and 40 deletions
@@ -680,6 +680,8 @@ public class DebugProcessEvents extends DebugProcessImpl {
//LOG.assertTrue(thread.isSuspended());
preprocessEvent(suspendContext, thread);
if (fastCheckToSkipBreakpointInEvaluation(suspendContext, event)) return;
//we use schedule to allow processing other events during processing this one
//this is especially necessary if a method is breakpoint condition
suspendContext.getManagerThread().schedule(new SuspendContextCommandImpl(suspendContext) {
@@ -689,47 +691,30 @@ public class DebugProcessEvents extends DebugProcessImpl {
final SuspendManagerImpl suspendManager = (SuspendManagerImpl)getSuspendManager();
final LocatableEventRequestor requestor = (LocatableEventRequestor)RequestManagerImpl.findRequestor(event.request());
ThreadReferenceProxyImpl threadProxy = suspendContext.getThread();
boolean isEvaluationOnCurrentThread = threadProxy != null && threadProxy.isEvaluating();
if (!DebuggerSession.enableBreakpointsDuringEvaluation() &&
!(requestor instanceof InstrumentationTracker.InstrumentationMethodBreakpoint) &&
!(requestor instanceof InstrumentedTechnicalBreakpoint)) {
if (isEvaluationOnCurrentThread || myThreadBlockedMonitor.isInResumeAllMode()) {
notifySkippedBreakpointInEvaluation(event, suspendContext);
// is inside evaluation, so ignore any breakpoints
logSuspendContext(suspendContext,
() -> "Resume because of evaluation: isEvaluationOnCurrentThread = " + isEvaluationOnCurrentThread +
", myThreadBlockedMonitor.isInResumeAllMode() = " + myThreadBlockedMonitor.isInResumeAllMode());
suspendManager.voteResume(suspendContext);
return;
if (myIsUnderBreakpointCheckFn != null && shouldCheckForSkipBreakpoint(event)) {
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, null);
try {
Value value = invokeMethod(
evaluationContext,
(ClassType)myIsUnderBreakpointCheckFn.declaringType(),
myIsUnderBreakpointCheckFn,
Collections.emptyList()
);
if (value instanceof BooleanValue booleanValue) {
if (booleanValue.value()) {
notifySkippedBreakpointInEvaluation(event, suspendContext);
suspendManager.voteResume(suspendContext);
return;
}
}
else {
throw new RuntimeException("Expected BooleanValue, got: " + value);
}
}
if (myIsUnderBreakpointCheckFn != null) {
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, null);
try {
Value value = invokeMethod(
evaluationContext,
(ClassType)myIsUnderBreakpointCheckFn.declaringType(),
myIsUnderBreakpointCheckFn,
Collections.emptyList()
);
if (value instanceof BooleanValue booleanValue) {
if (booleanValue.value()) {
notifySkippedBreakpointInEvaluation(event, suspendContext);
suspendManager.voteResume(suspendContext);
return;
}
}
else {
throw new RuntimeException("Expected BooleanValue, got: " + value);
}
}
catch (Throwable e) {
//TODO: switch off instrumentation breakpoint logic
logError("Error evaluating isUnderBreakpointCheckFn", e);
}
catch (Throwable e) {
//TODO: switch off instrumentation breakpoint logic
logError("Error evaluating isUnderBreakpointCheckFn", e);
}
}
@@ -869,6 +854,38 @@ public class DebugProcessEvents extends DebugProcessImpl {
});
}
private static boolean shouldCheckForSkipBreakpoint(LocatableEvent event) {
final LocatableEventRequestor requestor = (LocatableEventRequestor)RequestManagerImpl.findRequestor(event.request());
return !DebuggerSession.enableBreakpointsDuringEvaluation() &&
!(requestor instanceof InstrumentationTracker.InstrumentationMethodBreakpoint) &&
!(requestor instanceof InstrumentedTechnicalBreakpoint);
}
private boolean fastCheckToSkipBreakpointInEvaluation(SuspendContextImpl suspendContext, LocatableEvent event) {
if (shouldCheckForSkipBreakpoint(event)) {
ThreadReferenceProxyImpl threadProxy = suspendContext.getThread();
boolean isEvaluationOnCurrentThread = threadProxy != null && threadProxy.isEvaluating();
if (isEvaluationOnCurrentThread || myThreadBlockedMonitor.isInResumeAllMode()) {
final LocatableEventRequestor requestor = (LocatableEventRequestor)RequestManagerImpl.findRequestor(event.request());
// Do not report anything for low-level technical events
if (requestor == null || !requestor.shouldIgnoreThreadFiltering()) {
notifySkippedBreakpointInEvaluation(event, suspendContext);
}
// is inside evaluation, so ignore any breakpoints
logSuspendContext(suspendContext,
() -> "Resume because of evaluation: isEvaluationOnCurrentThread = " + isEvaluationOnCurrentThread +
", myThreadBlockedMonitor.isInResumeAllMode() = " + myThreadBlockedMonitor.isInResumeAllMode());
getSuspendManager().voteResume(suspendContext);
return true;
}
}
return false;
}
static boolean specialSuspendProcessingForAlwaysSwitch(@NotNull SuspendContextImpl suspendContext,
@NotNull SuspendManagerImpl suspendManager,
@@ -2454,7 +2454,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
}
};
var request = getRequestsManager().createMethodEntryRequest(requestor);
request.setSuspendPolicy(EventRequest.SUSPEND_ALL);
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
DebuggerUtilsAsync.setEnabled(request, true);
long timeout = Registry.intValue("debugger.evaluate.on.pause.timeout.ms", 500);