(CoroutineDebugger) negative frames removed from coroutine stack
This commit is contained in:
+23
-9
@@ -10,18 +10,21 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
||||
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
||||
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
||||
import com.intellij.util.containers.addIfNotNull
|
||||
import com.sun.jdi.ObjectReference
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
|
||||
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
|
||||
import org.jetbrains.kotlin.idea.debugger.safeLocation
|
||||
import org.jetbrains.kotlin.idea.debugger.safeMethod
|
||||
import java.lang.Integer.min
|
||||
|
||||
|
||||
class CoroutineFrameBuilder {
|
||||
|
||||
companion object {
|
||||
val log by logger
|
||||
const val PRE_FETCH_FRAME_COUNT = 5
|
||||
|
||||
fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? =
|
||||
when {
|
||||
@@ -39,12 +42,12 @@ class CoroutineFrameBuilder {
|
||||
for (runningStackFrameProxy in realFrames) {
|
||||
val preflightStackFrame = coroutineExitFrame(runningStackFrameProxy, suspendContext)
|
||||
if (preflightStackFrame != null) {
|
||||
coroutineStackFrameList.add(buildRealStackFrameItem(preflightStackFrame.stackFrameProxy,))
|
||||
coroutineStackFrameList.addIfNotNull(buildRealStackFrameItem(preflightStackFrame.stackFrameProxy))
|
||||
val doubleFrameList = build(preflightStackFrame, suspendContext)
|
||||
coroutineStackFrameList.addAll(doubleFrameList.stackTrace)
|
||||
return DoubleFrameList(coroutineStackFrameList, doubleFrameList.creationStackTrace)
|
||||
} else {
|
||||
coroutineStackFrameList.add(buildRealStackFrameItem(runningStackFrameProxy,))
|
||||
coroutineStackFrameList.addIfNotNull(buildRealStackFrameItem(runningStackFrameProxy))
|
||||
}
|
||||
}
|
||||
return DoubleFrameList(coroutineStackFrameList, emptyList())
|
||||
@@ -59,7 +62,8 @@ class CoroutineFrameBuilder {
|
||||
stackFrames.addAll(preflightFrame.restoredStackTrace())
|
||||
|
||||
// rest of the stack
|
||||
stackFrames.addAll(preflightFrame.threadPreCoroutineFrames.drop(1).mapIndexedNotNull { index, stackFrameProxyImpl ->
|
||||
val framesLeft = preflightFrame.threadPreCoroutineFrames.drop(1)
|
||||
stackFrames.addAll(framesLeft.mapIndexedNotNull { index, stackFrameProxyImpl ->
|
||||
suspendContext.invokeInManagerThread { buildRealStackFrameItem(stackFrameProxyImpl) }
|
||||
})
|
||||
|
||||
@@ -73,9 +77,12 @@ class CoroutineFrameBuilder {
|
||||
|
||||
private fun buildRealStackFrameItem(
|
||||
frame: StackFrameProxyImpl
|
||||
): RunningCoroutineStackFrameItem {
|
||||
): RunningCoroutineStackFrameItem? {
|
||||
val location = frame.location()
|
||||
return RunningCoroutineStackFrameItem(frame, location)
|
||||
if (!location.safeCoroutineExitPointLineNumber())
|
||||
return RunningCoroutineStackFrameItem(frame, location)
|
||||
else
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,10 +120,7 @@ class CoroutineFrameBuilder {
|
||||
if (mode.isSuspendMethodParameter()) {
|
||||
if (theFollowingFrames.isNotEmpty()) {
|
||||
// have to check next frame if that's invokeSuspend:-1 before proceed, otherwise skip
|
||||
val theFollowingFrame = theFollowingFrames.first()
|
||||
val theFollowingMode = theFollowingFrame.location().isPreFlight()
|
||||
if (theFollowingMode != SuspendExitMode.SUSPEND_METHOD)
|
||||
return null // break, that's not the exit, perhaps the following one?
|
||||
val invokeSuspendFrame = lookForTheFollowingFrame(theFollowingFrames) ?: return null
|
||||
} else
|
||||
return null
|
||||
}
|
||||
@@ -135,6 +139,16 @@ class CoroutineFrameBuilder {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun lookForTheFollowingFrame(theFollowingFrames: List<StackFrameProxyImpl>): StackFrameProxyImpl? {
|
||||
for (i in 0 until min(PRE_FETCH_FRAME_COUNT, theFollowingFrames.size)) { // pre-scan PRE_FETCH_FRAME_COUNT frames
|
||||
val nextFrame = theFollowingFrames.get(i)
|
||||
if (nextFrame.location().isPreFlight() == SuspendExitMode.SUSPEND_METHOD) {
|
||||
return nextFrame
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun preflight(
|
||||
frame: StackFrameProxyImpl,
|
||||
framesLeft: List<StackFrameProxyImpl>,
|
||||
|
||||
+8
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.debugger.coroutine.util
|
||||
|
||||
import com.intellij.debugger.engine.SuspendContextImpl
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -26,6 +27,9 @@ const val CREATION_STACK_TRACE_SEPARATOR = "\b\b\b" // the "\b\b\b" is used as c
|
||||
fun Method.isInvokeSuspend(): Boolean =
|
||||
name() == "invokeSuspend" && signature() == "(Ljava/lang/Object;)Ljava/lang/Object;"
|
||||
|
||||
fun Method.isInvoke(): Boolean =
|
||||
name() == "invoke" && signature().contains("Ljava/lang/Object;)Ljava/lang/Object;")
|
||||
|
||||
fun Method.isContinuation() =
|
||||
isInvokeSuspend() && declaringType().isContinuation() /* Perhaps need to check for "Lkotlin/coroutines/Continuation;)" in signature() ? */
|
||||
|
||||
@@ -44,11 +48,14 @@ fun Location.isPreFlight(): SuspendExitMode {
|
||||
return SuspendExitMode.SUSPEND_LAMBDA
|
||||
else if (method.hasContinuationParameter())
|
||||
return SuspendExitMode.SUSPEND_METHOD_PARAMETER
|
||||
else if (method.isInvokeSuspend() && safeLineNumber() == -1)
|
||||
else if ((method.isInvokeSuspend() || method.isInvoke()) && safeCoroutineExitPointLineNumber())
|
||||
return SuspendExitMode.SUSPEND_METHOD
|
||||
return SuspendExitMode.NONE
|
||||
}
|
||||
|
||||
fun Location.safeCoroutineExitPointLineNumber() =
|
||||
wrapIllegalArgumentException { DebuggerUtilsEx.getLineNumber(this, false) } ?: -2 == -1
|
||||
|
||||
fun ReferenceType.isContinuation() =
|
||||
isBaseContinuationImpl() || isSubtype("kotlin.coroutines.Continuation")
|
||||
|
||||
|
||||
@@ -7,22 +7,19 @@ Thread stack trace:
|
||||
($completion, b, bParam)
|
||||
a:21, SuspendFunKt (coroutine1)
|
||||
($completion, $continuation, $result, a, aParam)
|
||||
test1:15, SuspendFunKt (coroutine1)
|
||||
($completion, $continuation, $result, i, test1)
|
||||
invoke:9, SuspendFunKt$main$result$1 (coroutine1)
|
||||
CoroutineNameIdState(name=coroutine, id=-1, state=UNKNOWN, dispatcher=null)
|
||||
test1:15, coroutine1.SuspendFunKt
|
||||
(i, test1)
|
||||
invoke:9, coroutine1.SuspendFunKt$main$result$1
|
||||
(continuation, p1, this)
|
||||
invoke:-1, SuspendFunKt$main$result$1 (coroutine1)
|
||||
(this)
|
||||
invokeSuspend:121, IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3 (kotlin.coroutines.intrinsics)
|
||||
invokeSuspend:121, kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3
|
||||
($i$a$-createCoroutineFromSuspendFunction-IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$2, it, result, this)
|
||||
resumeWith:33, BaseContinuationImpl (kotlin.coroutines.jvm.internal)
|
||||
resumeWith:33, kotlin.coroutines.jvm.internal.BaseContinuationImpl
|
||||
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$with, completion, current, param, result, this)
|
||||
startCoroutine:128, ContinuationKt (kotlin.coroutines)
|
||||
startCoroutine:128, kotlin.coroutines.ContinuationKt
|
||||
($this$startCoroutine, completion, receiver)
|
||||
main:9, SuspendFunKt (coroutine1)
|
||||
main:9, coroutine1.SuspendFunKt
|
||||
(cnt)
|
||||
main:-1, SuspendFunKt (coroutine1)
|
||||
()
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
+1
-3
@@ -12,7 +12,7 @@ CoroutineNameIdState(name=coroutine, id=-1, state=UNKNOWN, dispatcher=null)
|
||||
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$with, completion, current, param, result, this)
|
||||
hasNext:140, kotlin.sequences.SequenceBuilderIterator
|
||||
(step, this)
|
||||
hasNext:374, kotlin.sequences.TakeSequence$iterator$1
|
||||
hasNext:406, kotlin.sequences.TakeSequence$iterator$1
|
||||
(this)
|
||||
toCollection:722, kotlin.sequences.SequencesKt___SequencesKt
|
||||
($this$toCollection, destination)
|
||||
@@ -22,8 +22,6 @@ CoroutineNameIdState(name=coroutine, id=-1, state=UNKNOWN, dispatcher=null)
|
||||
($this$toList)
|
||||
main:5, continuation.SuspendLambdaKt
|
||||
(a)
|
||||
main:-1, continuation.SuspendLambdaKt
|
||||
()
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
-2
@@ -30,8 +30,6 @@ CoroutineNameIdState(name=coroutine, id=1, state=RUNNING, dispatcher=BlockingEve
|
||||
()
|
||||
main:9, continuation.CoroutineSuspendFunKt
|
||||
(main)
|
||||
main:-1, continuation.CoroutineSuspendFunKt
|
||||
()
|
||||
Creation stack frame
|
||||
createCoroutineUnintercepted:116, kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt
|
||||
()
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
LineBreakpoint created at coroutineSuspendFun135.kt:29
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
coroutineSuspendFun135.kt:29
|
||||
Thread stack trace:
|
||||
c:29, CoroutineSuspendFun135Kt (continuation)
|
||||
($completion, c, paramB)
|
||||
b:23, CoroutineSuspendFun135Kt (continuation)
|
||||
($completion, $continuation, $result, b, paramA)
|
||||
CoroutineNameIdState(name=coroutine, id=1, state=RUNNING, dispatcher=BlockingEventLoop@305fd85d)
|
||||
a:16, continuation.CoroutineSuspendFun135Kt
|
||||
(a)
|
||||
invokeSuspend:10, continuation.CoroutineSuspendFun135Kt$main$1
|
||||
($this$runBlocking)
|
||||
resumeWith:33, kotlin.coroutines.jvm.internal.BaseContinuationImpl
|
||||
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$with, completion, current, param, result, this)
|
||||
run:56, kotlinx.coroutines.DispatchedTask
|
||||
($i$a$-withCoroutineContext-DispatchedTask$run$1, $i$f$withCoroutineContext, context, continuation, countOrElement$iv, delegate, exception, fatalException, job, oldValue$iv, state, taskContext, this)
|
||||
processNextEvent:274, kotlinx.coroutines.EventLoopImplBase
|
||||
(delayed, task, this)
|
||||
joinBlocking:79, kotlinx.coroutines.BlockingCoroutine
|
||||
(this)
|
||||
runBlocking:54, kotlinx.coroutines.BuildersKt__BuildersKt
|
||||
(block, context, contextInterceptor, coroutine, currentThread, eventLoop, newContext)
|
||||
runBlocking:1, kotlinx.coroutines.BuildersKt
|
||||
()
|
||||
runBlocking$default:36, kotlinx.coroutines.BuildersKt__BuildersKt
|
||||
()
|
||||
runBlocking$default:1, kotlinx.coroutines.BuildersKt
|
||||
()
|
||||
main:9, continuation.CoroutineSuspendFun135Kt
|
||||
(main)
|
||||
Creation stack frame
|
||||
createCoroutineUnintercepted:116, kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt
|
||||
()
|
||||
startCoroutineCancellable:26, kotlinx.coroutines.intrinsics.CancellableKt
|
||||
()
|
||||
runBlocking$default:1, kotlinx.coroutines.BuildersKt
|
||||
()
|
||||
main:9, continuation.CoroutineSuspendFun135Kt
|
||||
()
|
||||
main:-1, continuation.CoroutineSuspendFun135Kt
|
||||
()
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
+1
-1
@@ -118,7 +118,7 @@ private inline fun <T> wrapEvaluateException(block: () -> T): T? {
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> wrapIllegalArgumentException(block: () -> T): T? {
|
||||
public inline fun <T> wrapIllegalArgumentException(block: () -> T): T? {
|
||||
return try {
|
||||
block()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
|
||||
Reference in New Issue
Block a user