[debugger] Do not skip breakpoints during coroutine-thread stepping

IDEA-381878 IDEA-365578

GitOrigin-RevId: 253c8edc09ebff548220f0e8504107c55ce8d196
This commit is contained in:
Alexey.Merkulov
2025-11-08 00:55:18 +00:00
committed by intellij-monorepo-bot
parent 2ad7f181ae
commit 84de3835b1
5 changed files with 40 additions and 13 deletions
@@ -638,7 +638,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
@Override
public void contextAction(@NotNull SuspendContextImpl suspendContext) {
logSuspendContext(suspendContext, () -> "start locatable event processing");
final SuspendManager suspendManager = getSuspendManager();
final SuspendManagerImpl suspendManager = (SuspendManagerImpl)getSuspendManager();
final LocatableEventRequestor requestor = (LocatableEventRequestor)RequestManagerImpl.findRequestor(event.request());
ThreadReferenceProxyImpl threadProxy = suspendContext.getThread();
@@ -671,7 +671,10 @@ public class DebugProcessEvents extends DebugProcessImpl {
suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD &&
myRunToCursorManager.shouldTryToPauseAnotherHit(suspendContext)) {
postponeSuspendRunToCursorBP = true;
} else {
} else if (mySteppingProgressTracker.isSuspendAllStepping() ||
!suspendManager.getSuspendAllContexts().isEmpty() ||
requestor instanceof SyntheticBreakpoint
) {
// notify only if the current session is not one with evaluations hidden from the user
if (!checkContextIsFromImplicitThread(suspendContext)) {
notifySkippedBreakpoints(event, SkippedBreakpointReason.STEPPING);
@@ -679,6 +682,8 @@ public class DebugProcessEvents extends DebugProcessImpl {
logSuspendContext(suspendContext, () -> "Skip breakpoint because of filter " + filter);
suspendManager.voteResume(suspendContext);
return;
} else {
suspendContext.threadFilterWasPassed = false;
}
}
}
@@ -7,18 +7,21 @@ import com.intellij.debugger.engine.jdi.ThreadReferenceProxy
import com.intellij.debugger.impl.DebuggerUtilsAsync
import com.intellij.debugger.impl.PrioritizedTask
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
import com.intellij.debugger.settings.DebuggerSettings
import com.sun.jdi.request.EventRequest
import kotlinx.coroutines.CompletableDeferred
import org.jetbrains.annotations.Nls
import java.util.concurrent.CompletableFuture
private data class TrackedSteppingData(val stepCompetedStatus: CompletableDeferred<Unit>, val threadFilter: (ThreadReferenceProxy?, SuspendContextImpl) -> Boolean)
private data class TrackedSteppingData(val stepCompetedStatus: CompletableDeferred<Unit>, val isSuspendAllStepping: Boolean, val threadFilter: (ThreadReferenceProxy?, SuspendContextImpl) -> Boolean)
internal class SteppingProgressTracker(private val debuggerProcessImpl: DebugProcessImpl) {
private val trackedStepping = mutableListOf<TrackedSteppingData>()
val isSteppingInProgress: Boolean get() = trackedStepping.isNotEmpty()
val isSuspendAllStepping: Boolean get() = trackedStepping.any { it.isSuspendAllStepping }
/** returns true iff the [suspendContext] is the end of ongoing stepping */
fun onPaused(suspendContext: SuspendContext): Boolean {
val thread = suspendContext.thread
@@ -31,8 +34,8 @@ internal class SteppingProgressTracker(private val debuggerProcessImpl: DebugPro
return completedSteps.isNotEmpty()
}
fun addStepping(stepCompetedStatus: CompletableDeferred<Unit>, threadFilter: (ThreadReferenceProxy?, SuspendContextImpl) -> Boolean) {
trackedStepping.add(TrackedSteppingData(stepCompetedStatus, threadFilter))
fun addStepping(stepCompetedStatus: CompletableDeferred<Unit>, isSuspendAllStepping: Boolean, threadFilter: (ThreadReferenceProxy?, SuspendContextImpl) -> Boolean) {
trackedStepping.add(TrackedSteppingData(stepCompetedStatus, isSuspendAllStepping, threadFilter))
}
}
@@ -41,6 +44,8 @@ private class CancelingSteppingListener : SteppingListener {
val debuggerProcessImpl: DebugProcessImpl = suspendContext.debugProcess
val filter: LightOrRealThreadInfo? = debuggerProcessImpl.requestsManager.filterThread
val isSuspendAllPolicy = suspendContext.suspendPolicyFromRequestors == DebuggerSettings.SUSPEND_ALL
val threadForStepping: ThreadReferenceProxyImpl? =
if (filter != null) suspendContext.virtualMachineProxy.getThreadReferenceProxy(filter.realThread)
else suspendContext.thread
@@ -76,8 +81,10 @@ private class CancelingSteppingListener : SteppingListener {
}
val tracker = suspendContext.debugProcess.mySteppingProgressTracker
tracker.addStepping(stepCompetedStatus) { thread, suspendContext ->
thread == null || filter != null && filter.checkSameThread(thread.threadReference, suspendContext) || thread == threadForStepping
tracker.addStepping(stepCompetedStatus, isSuspendAllPolicy) { thread, suspendContext ->
thread == null
|| (filter != null && filter.checkSameThread(thread.threadReference, suspendContext))
|| (filter == null && thread == threadForStepping)
}
}
}
@@ -62,6 +62,9 @@ abstract class SuspendContextImpl @ApiStatus.Internal constructor(
@JvmField
protected var mySteppingThreadForResumeOneSteppingCurrentMode: ThreadReferenceProxyImpl? = null
@JvmField
internal var threadFilterWasPassed = true
@get:ApiStatus.Internal
@set:ApiStatus.Internal
var lightThreadFilter: Any? = null
@@ -268,7 +271,7 @@ abstract class SuspendContextImpl @ApiStatus.Internal constructor(
@MagicConstant(flagsFromClass = EventRequest::class)
override fun getSuspendPolicy(): Int = mySuspendPolicy
val suspendPolicyFromRequestors: String?
val suspendPolicyFromRequestors: String
get() {
if (mySuspendPolicy == EventRequest.SUSPEND_ALL) return DebuggerSettings.SUSPEND_ALL
val eventSet = myEventSet
@@ -324,7 +324,6 @@ public class SuspendManagerImpl implements SuspendManager {
}
else {
LOG.debug("vote paused");
myDebugProcess.cancelSteppingBreakpoints();
if (!Registry.is("debugger.keep.step.requests")) {
ThreadReferenceProxyImpl thread = suspendContext.getEventThread();
myDebugProcess.deleteStepRequests(suspendContext.getVirtualMachineProxy().eventRequestManager(),
@@ -559,13 +559,22 @@ public final class DebuggerSession implements AbstractDebuggerSession {
return;
}
else {
currentThread = mySteppingThroughThread.get();
if (suspendContext.threadFilterWasPassed) {
currentThread = mySteppingThroughThread.get();
}
else {
return;
}
}
}
else {
setSteppingThrough(currentThread);
if (suspendContext.threadFilterWasPassed) {
setSteppingThrough(currentThread);
}
}
myDebugProcess.cancelSteppingBreakpoints();
final StackFrameContext positionContext;
SourcePosition position;
@@ -702,11 +711,15 @@ public final class DebuggerSession implements AbstractDebuggerSession {
final ThreadReferenceProxyImpl newThread = suspendContext.getEventThread();
if (newThread == null || suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL || !myDebugProcess.isSteppingInProgress()) {
return true;
if (suspendContext.threadFilterWasPassed) {
return true;
}
}
final SuspendContextImpl currentSuspendContext = getContextManager().getContext().getSuspendContext();
if (currentSuspendContext == null || currentSuspendContext.isResumed()) {
return mySteppingThroughThread.get() == null;
if (suspendContext.threadFilterWasPassed) {
return mySteppingThroughThread.get() == null;
}
}
if (enableBreakpointsDuringEvaluation()) {
final ThreadReferenceProxyImpl currentThread = currentSuspendContext.getThread();