(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.jdi.ThreadReferenceProxyImpl
|
||||||
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
||||||
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
||||||
|
import com.intellij.util.containers.addIfNotNull
|
||||||
import com.sun.jdi.ObjectReference
|
import com.sun.jdi.ObjectReference
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
|
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
|
||||||
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
|
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
|
||||||
import org.jetbrains.kotlin.idea.debugger.safeLocation
|
import org.jetbrains.kotlin.idea.debugger.safeLocation
|
||||||
import org.jetbrains.kotlin.idea.debugger.safeMethod
|
import org.jetbrains.kotlin.idea.debugger.safeMethod
|
||||||
|
import java.lang.Integer.min
|
||||||
|
|
||||||
|
|
||||||
class CoroutineFrameBuilder {
|
class CoroutineFrameBuilder {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val log by logger
|
val log by logger
|
||||||
|
const val PRE_FETCH_FRAME_COUNT = 5
|
||||||
|
|
||||||
fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? =
|
fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? =
|
||||||
when {
|
when {
|
||||||
@@ -39,12 +42,12 @@ class CoroutineFrameBuilder {
|
|||||||
for (runningStackFrameProxy in realFrames) {
|
for (runningStackFrameProxy in realFrames) {
|
||||||
val preflightStackFrame = coroutineExitFrame(runningStackFrameProxy, suspendContext)
|
val preflightStackFrame = coroutineExitFrame(runningStackFrameProxy, suspendContext)
|
||||||
if (preflightStackFrame != null) {
|
if (preflightStackFrame != null) {
|
||||||
coroutineStackFrameList.add(buildRealStackFrameItem(preflightStackFrame.stackFrameProxy,))
|
coroutineStackFrameList.addIfNotNull(buildRealStackFrameItem(preflightStackFrame.stackFrameProxy))
|
||||||
val doubleFrameList = build(preflightStackFrame, suspendContext)
|
val doubleFrameList = build(preflightStackFrame, suspendContext)
|
||||||
coroutineStackFrameList.addAll(doubleFrameList.stackTrace)
|
coroutineStackFrameList.addAll(doubleFrameList.stackTrace)
|
||||||
return DoubleFrameList(coroutineStackFrameList, doubleFrameList.creationStackTrace)
|
return DoubleFrameList(coroutineStackFrameList, doubleFrameList.creationStackTrace)
|
||||||
} else {
|
} else {
|
||||||
coroutineStackFrameList.add(buildRealStackFrameItem(runningStackFrameProxy,))
|
coroutineStackFrameList.addIfNotNull(buildRealStackFrameItem(runningStackFrameProxy))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return DoubleFrameList(coroutineStackFrameList, emptyList())
|
return DoubleFrameList(coroutineStackFrameList, emptyList())
|
||||||
@@ -59,7 +62,8 @@ class CoroutineFrameBuilder {
|
|||||||
stackFrames.addAll(preflightFrame.restoredStackTrace())
|
stackFrames.addAll(preflightFrame.restoredStackTrace())
|
||||||
|
|
||||||
// rest of the stack
|
// 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) }
|
suspendContext.invokeInManagerThread { buildRealStackFrameItem(stackFrameProxyImpl) }
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -73,9 +77,12 @@ class CoroutineFrameBuilder {
|
|||||||
|
|
||||||
private fun buildRealStackFrameItem(
|
private fun buildRealStackFrameItem(
|
||||||
frame: StackFrameProxyImpl
|
frame: StackFrameProxyImpl
|
||||||
): RunningCoroutineStackFrameItem {
|
): RunningCoroutineStackFrameItem? {
|
||||||
val location = frame.location()
|
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 (mode.isSuspendMethodParameter()) {
|
||||||
if (theFollowingFrames.isNotEmpty()) {
|
if (theFollowingFrames.isNotEmpty()) {
|
||||||
// have to check next frame if that's invokeSuspend:-1 before proceed, otherwise skip
|
// have to check next frame if that's invokeSuspend:-1 before proceed, otherwise skip
|
||||||
val theFollowingFrame = theFollowingFrames.first()
|
val invokeSuspendFrame = lookForTheFollowingFrame(theFollowingFrames) ?: return null
|
||||||
val theFollowingMode = theFollowingFrame.location().isPreFlight()
|
|
||||||
if (theFollowingMode != SuspendExitMode.SUSPEND_METHOD)
|
|
||||||
return null // break, that's not the exit, perhaps the following one?
|
|
||||||
} else
|
} else
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -135,6 +139,16 @@ class CoroutineFrameBuilder {
|
|||||||
return null
|
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(
|
private fun preflight(
|
||||||
frame: StackFrameProxyImpl,
|
frame: StackFrameProxyImpl,
|
||||||
framesLeft: List<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.SuspendContextImpl
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
|
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
||||||
import com.intellij.openapi.project.Project
|
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 =
|
fun Method.isInvokeSuspend(): Boolean =
|
||||||
name() == "invokeSuspend" && signature() == "(Ljava/lang/Object;)Ljava/lang/Object;"
|
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() =
|
fun Method.isContinuation() =
|
||||||
isInvokeSuspend() && declaringType().isContinuation() /* Perhaps need to check for "Lkotlin/coroutines/Continuation;)" in signature() ? */
|
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
|
return SuspendExitMode.SUSPEND_LAMBDA
|
||||||
else if (method.hasContinuationParameter())
|
else if (method.hasContinuationParameter())
|
||||||
return SuspendExitMode.SUSPEND_METHOD_PARAMETER
|
return SuspendExitMode.SUSPEND_METHOD_PARAMETER
|
||||||
else if (method.isInvokeSuspend() && safeLineNumber() == -1)
|
else if ((method.isInvokeSuspend() || method.isInvoke()) && safeCoroutineExitPointLineNumber())
|
||||||
return SuspendExitMode.SUSPEND_METHOD
|
return SuspendExitMode.SUSPEND_METHOD
|
||||||
return SuspendExitMode.NONE
|
return SuspendExitMode.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Location.safeCoroutineExitPointLineNumber() =
|
||||||
|
wrapIllegalArgumentException { DebuggerUtilsEx.getLineNumber(this, false) } ?: -2 == -1
|
||||||
|
|
||||||
fun ReferenceType.isContinuation() =
|
fun ReferenceType.isContinuation() =
|
||||||
isBaseContinuationImpl() || isSubtype("kotlin.coroutines.Continuation")
|
isBaseContinuationImpl() || isSubtype("kotlin.coroutines.Continuation")
|
||||||
|
|
||||||
|
|||||||
@@ -7,22 +7,19 @@ Thread stack trace:
|
|||||||
($completion, b, bParam)
|
($completion, b, bParam)
|
||||||
a:21, SuspendFunKt (coroutine1)
|
a:21, SuspendFunKt (coroutine1)
|
||||||
($completion, $continuation, $result, a, aParam)
|
($completion, $continuation, $result, a, aParam)
|
||||||
test1:15, SuspendFunKt (coroutine1)
|
CoroutineNameIdState(name=coroutine, id=-1, state=UNKNOWN, dispatcher=null)
|
||||||
($completion, $continuation, $result, i, test1)
|
test1:15, coroutine1.SuspendFunKt
|
||||||
invoke:9, SuspendFunKt$main$result$1 (coroutine1)
|
(i, test1)
|
||||||
|
invoke:9, coroutine1.SuspendFunKt$main$result$1
|
||||||
(continuation, p1, this)
|
(continuation, p1, this)
|
||||||
invoke:-1, SuspendFunKt$main$result$1 (coroutine1)
|
invokeSuspend:121, kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3
|
||||||
(this)
|
|
||||||
invokeSuspend:121, IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3 (kotlin.coroutines.intrinsics)
|
|
||||||
($i$a$-createCoroutineFromSuspendFunction-IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$2, it, result, this)
|
($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)
|
($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)
|
($this$startCoroutine, completion, receiver)
|
||||||
main:9, SuspendFunKt (coroutine1)
|
main:9, coroutine1.SuspendFunKt
|
||||||
(cnt)
|
(cnt)
|
||||||
main:-1, SuspendFunKt (coroutine1)
|
|
||||||
()
|
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
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)
|
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$with, completion, current, param, result, this)
|
||||||
hasNext:140, kotlin.sequences.SequenceBuilderIterator
|
hasNext:140, kotlin.sequences.SequenceBuilderIterator
|
||||||
(step, this)
|
(step, this)
|
||||||
hasNext:374, kotlin.sequences.TakeSequence$iterator$1
|
hasNext:406, kotlin.sequences.TakeSequence$iterator$1
|
||||||
(this)
|
(this)
|
||||||
toCollection:722, kotlin.sequences.SequencesKt___SequencesKt
|
toCollection:722, kotlin.sequences.SequencesKt___SequencesKt
|
||||||
($this$toCollection, destination)
|
($this$toCollection, destination)
|
||||||
@@ -22,8 +22,6 @@ CoroutineNameIdState(name=coroutine, id=-1, state=UNKNOWN, dispatcher=null)
|
|||||||
($this$toList)
|
($this$toList)
|
||||||
main:5, continuation.SuspendLambdaKt
|
main:5, continuation.SuspendLambdaKt
|
||||||
(a)
|
(a)
|
||||||
main:-1, continuation.SuspendLambdaKt
|
|
||||||
()
|
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
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:9, continuation.CoroutineSuspendFunKt
|
||||||
(main)
|
(main)
|
||||||
main:-1, continuation.CoroutineSuspendFunKt
|
|
||||||
()
|
|
||||||
Creation stack frame
|
Creation stack frame
|
||||||
createCoroutineUnintercepted:116, kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt
|
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 {
|
return try {
|
||||||
block()
|
block()
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
|
|||||||
Reference in New Issue
Block a user