mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[debugger] Remove the stepping request in the current thread if a coroutine suspends
This commit fixes stepping in runBlocking, IDEA-369686. And also it introduces the regression in the stepOverWithContext test. But the next commit will fix it. GitOrigin-RevId: 76750510f23d37501dee0d8ff073adcbbf792f67
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f48c719a28
commit
7e113676fa
@@ -551,7 +551,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
RequestHint hint = getRequestHint(event);
|
||||
Object commandToken = getCommandToken(event);
|
||||
|
||||
deleteStepRequests(suspendContext.getVirtualMachineProxy().eventRequestManager(), event.thread());
|
||||
removeStepRequests(suspendContext, thread);
|
||||
|
||||
boolean shouldResume = false;
|
||||
|
||||
@@ -611,6 +611,10 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeStepRequests(@NotNull SuspendContextImpl suspendContext, @Nullable ThreadReference thread) {
|
||||
suspendContext.getDebugProcess().deleteStepRequests(suspendContext.getVirtualMachineProxy().eventRequestManager(), thread);
|
||||
}
|
||||
|
||||
// Preload event info in "parallel" commands, to avoid sync jdwp requests after
|
||||
static void preloadEventInfo(ThreadReference thread, @Nullable Location location) {
|
||||
if (Registry.is("debugger.preload.event.info") && DebuggerUtilsAsync.isAsyncEnabled()) {
|
||||
|
||||
+80
-4
@@ -3,14 +3,17 @@
|
||||
package org.jetbrains.kotlin.idea.debugger.core.stepping
|
||||
|
||||
import com.intellij.debugger.DebuggerManagerEx
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.StepIntoMethodBreakpoint
|
||||
import com.intellij.debugger.engine.SuspendContextImpl
|
||||
import com.intellij.debugger.engine.*
|
||||
import com.intellij.debugger.engine.events.SuspendContextCommandImpl
|
||||
import com.intellij.debugger.settings.DebuggerSettings
|
||||
import com.intellij.debugger.ui.breakpoints.SteppingBreakpoint
|
||||
import com.intellij.debugger.ui.breakpoints.SyntheticLineBreakpoint
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.sun.jdi.Location
|
||||
import com.sun.jdi.ThreadReference
|
||||
import com.sun.jdi.event.LocatableEvent
|
||||
import org.jetbrains.kotlin.idea.debugger.base.util.safeMethod
|
||||
import org.jetbrains.kotlin.idea.debugger.core.StackFrameInterceptor
|
||||
@@ -49,7 +52,11 @@ object CoroutineBreakpointFacility {
|
||||
|
||||
private fun installCoroutineResumedBreakpoint(context: SuspendContextImpl, resumedLocation: Location, nextLocationAfterResume: Location?): Boolean {
|
||||
val debugProcess = context.debugProcess
|
||||
debugProcess.cancelRunToCursorBreakpoint()
|
||||
debugProcess.cancelSteppingBreakpoints()
|
||||
val clearSteppingBreakpoint = installBreakpointToRemoveSteppingInCurrentThread(context)
|
||||
if (clearSteppingBreakpoint == null) {
|
||||
thisLogger().warn("No clear stepping breakpoint installed for context $context")
|
||||
}
|
||||
val project = debugProcess.project
|
||||
|
||||
val useCoroutineIdFiltering = Registry.`is`("debugger.filter.breakpoints.by.coroutine.id")
|
||||
@@ -75,6 +82,13 @@ object CoroutineBreakpointFacility {
|
||||
if (!result) return false
|
||||
|
||||
val suspendContextImpl = action.suspendContext ?: return true
|
||||
clearSteppingBreakpoint?.let {
|
||||
if (!it.steppingRemoved) {
|
||||
thisLogger().debug("Clear old stepping from resume breakpoint")
|
||||
it.removeRequestAndStepping(suspendContextImpl)
|
||||
}
|
||||
}
|
||||
|
||||
return scheduleStepOverCommandForSuspendSwitch(suspendContextImpl, nextLocationAfterResume)
|
||||
}
|
||||
|
||||
@@ -98,6 +112,68 @@ object CoroutineBreakpointFacility {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun installBreakpointToRemoveSteppingInCurrentThread(context: SuspendContextImpl): ClearSteppingBreakpoint? {
|
||||
val classLoader = context.frameProxy?.classLoader ?: return null
|
||||
val originalThread = context.thread?.threadReference ?: return null
|
||||
|
||||
val debugProbesImpl =
|
||||
context.debugProcess.findLoadedClass(context, "kotlinx.coroutines.debug.internal.DebugProbesImpl", classLoader) ?: return null
|
||||
|
||||
val methods = debugProbesImpl.methods() ?: return null
|
||||
|
||||
val probeSuspendedMethod = methods.singleOrNull { it.name().contains("probeCoroutineSuspended") } ?: return null
|
||||
|
||||
val locationForBP = probeSuspendedMethod.locationOfCodeIndex(0)
|
||||
|
||||
val breakpoint = ClearSteppingBreakpoint(context.debugProcess.project, originalThread)
|
||||
|
||||
breakpoint.suspendPolicy = DebuggerSettings.SUSPEND_THREAD
|
||||
|
||||
val requestsManager = context.debugProcess.requestsManager
|
||||
val request = requestsManager.createBreakpointRequest(breakpoint, locationForBP)
|
||||
request.addThreadFilter(originalThread)
|
||||
requestsManager.enableRequest(request)
|
||||
context.debugProcess.setSteppingBreakpoint(breakpoint)
|
||||
return breakpoint
|
||||
}
|
||||
}
|
||||
|
||||
private class ClearSteppingBreakpoint(project: Project, private val originalThread: ThreadReference) : SyntheticLineBreakpoint(project), SteppingBreakpoint {
|
||||
var steppingRemoved = false
|
||||
private set
|
||||
|
||||
override fun shouldIgnoreThreadFiltering() = true
|
||||
|
||||
override fun track() = false
|
||||
|
||||
override fun processLocatableEvent(action: SuspendContextCommandImpl, event: LocatableEvent?): Boolean {
|
||||
val suspendContext = action.suspendContext
|
||||
val currentThread = suspendContext?.thread?.threadReference
|
||||
if (originalThread == currentThread) {
|
||||
removeRequestAndStepping(suspendContext)
|
||||
} else {
|
||||
// This should not happen because of the filter on the thread for this request
|
||||
thisLogger().error("Skip remove stepping breakpoint for thread ${originalThread.name()}")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun removeRequestAndStepping(suspendContext: SuspendContextImpl) {
|
||||
if (steppingRemoved) {
|
||||
return
|
||||
}
|
||||
steppingRemoved = true
|
||||
thisLogger().debug { "Remove stepping requests $suspendContext" }
|
||||
DebugProcessEvents.removeStepRequests(suspendContext, originalThread)
|
||||
suspendContext.debugProcess.requestsManager.deleteRequest(this)
|
||||
}
|
||||
|
||||
override fun isRestoreBreakpoints() = false
|
||||
|
||||
override fun setRequestHint(hint: RequestHint) {
|
||||
error("Should not be called")
|
||||
}
|
||||
}
|
||||
|
||||
fun SuspendContextImpl.getLocationCompat(): Location? {
|
||||
|
||||
+1
-1
@@ -2,9 +2,9 @@ LineBreakpoint created at soSuspendableCallInEndOfFun.kt:20
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
soSuspendableCallInEndOfFun.kt:20
|
||||
soSuspendableCallInEndOfFun.kt:10
|
||||
soSuspendableCallInEndOfFun.kt:11
|
||||
soSuspendableCallInEndOfFun.kt:8
|
||||
soSuspendableCallInEndOfFun.kt:13
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
Reference in New Issue
Block a user