IDEA-358435 Counterintuitive behaviour when enabling "Async Stack Traces"

GitOrigin-RevId: b548851ef3a033d1bf85b9bcb1b964c1eb0cbb48
This commit is contained in:
Egor Ushakov
2024-08-29 16:46:47 +02:00
committed by intellij-monorepo-bot
parent 907e9ffed1
commit e8108aa2c9
8 changed files with 93 additions and 26 deletions

View File

@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.idea.debugger.base.util.evaluate.DefaultExecutionCon
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.safeSkipCoroutineStackFrameProxy
import java.lang.Integer.min
class CoroutineFrameBuilder {
companion object {
@@ -133,18 +132,9 @@ class CoroutineFrameBuilder {
if (!mode.isCoroutineFound())
return null
val (theFollowingFrames, isFirstSuspendFrame) = theFollowingFrames(frame)
if (mode.isSuspendMethodParameter()) {
if (theFollowingFrames.isNotEmpty()) {
// have to check next frame if that's invokeSuspend:-1 before proceed, otherwise skip
// remove negative frames from the stacktrace
lookForTheFollowingFrame(theFollowingFrames) ?: return null
} else
return null
}
val continuation = extractContinuation(frame, mode) ?: return null
if (threadAndContextSupportsEvaluation(suspendContext, frame)) {
val (theFollowingFrames, isFirstSuspendFrame) = theFollowingFrames(frame)
val context = DefaultExecutionContext(suspendContext, frame)
val continuationHolder = ContinuationHolder.instance(context)
val coroutineInfo = continuationHolder.extractCoroutineInfoData(continuation) ?: return null
@@ -160,16 +150,6 @@ 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[i]
if (nextFrame.getSuspendExitMode() != SuspendExitMode.NONE) {
return nextFrame
}
}
return null
}
private fun getLVTContinuation(frame: StackFrameProxyImpl?) =
frame?.continuationVariableValue()
@@ -183,7 +163,13 @@ class CoroutineFrameBuilder {
val indexOfGetCoroutineSuspended = hasGetCoroutineSuspended(frames)
// @TODO if found - skip this thread stack
if (indexOfGetCoroutineSuspended < 0 && frames.size > indexOfCurrentFrame + 1) {
return Pair(frames.drop(indexOfCurrentFrame + 1), isFirstSuspendFrame(indexOfCurrentFrame, frames))
return Pair(
frames.asSequence()
.drop(indexOfCurrentFrame + 1)
.dropWhile { it.getSuspendExitMode() != SuspendExitMode.NONE }
.toList(),
isFirstSuspendFrame(indexOfCurrentFrame, frames)
)
}
} else {
log.error("Frame isn't found on the thread stack.")

View File

@@ -68,9 +68,6 @@ fun Location.isInvokeSuspend() =
fun Location.isInvokeSuspendWithNegativeLineNumber() =
isInvokeSuspend() && safeLineNumber() < 0
fun Location.isFilteredInvokeSuspend() =
isInvokeSuspend() || isInvokeSuspendWithNegativeLineNumber()
fun StackFrameProxyImpl.variableValue(variableName: String): ObjectReference? {
val continuationVariable = safeVisibleVariableByName(variableName) ?: return null
return getValue(continuationVariable) as? ObjectReference ?: return null
@@ -120,7 +117,7 @@ fun Location.sameLineAndMethod(location: Location?): Boolean =
location != null && location.safeMethod() == safeMethod() && location.safeLineNumber() == safeLineNumber()
fun Location.isFilterFromTop(location: Location?): Boolean =
isFilteredInvokeSuspend() || sameLineAndMethod(location) || location?.safeMethod() == safeMethod()
isInvokeSuspendWithNegativeLineNumber() || sameLineAndMethod(location) || location?.safeMethod() == safeMethod()
fun Location.isFilterFromBottom(location: Location?): Boolean =
sameLineAndMethod(location)

View File

@@ -35,6 +35,11 @@ public class K2IdeK1CodeContinuationStackTraceTestGenerated extends AbstractK2Id
runTest("../testData/continuation/suspendFun.kt");
}
@TestMetadata("suspendFun1.kt")
public void testSuspendFun1() throws Exception {
runTest("../testData/continuation/suspendFun1.kt");
}
@TestMetadata("suspendFunStackTraceFolded.kt")
public void testSuspendFunStackTraceFolded() throws Exception {
runTest("../testData/continuation/suspendFunStackTraceFolded.kt");

View File

@@ -35,6 +35,11 @@ public class K2IdeK2CodeContinuationStackTraceTestGenerated extends AbstractK2Id
runTest("../testData/continuation/suspendFun.kt");
}
@TestMetadata("suspendFun1.kt")
public void testSuspendFun1() throws Exception {
runTest("../testData/continuation/suspendFun1.kt");
}
@TestMetadata("suspendFunStackTraceFolded.kt")
public void testSuspendFunStackTraceFolded() throws Exception {
runTest("../testData/continuation/suspendFunStackTraceFolded.kt");

View File

@@ -35,6 +35,11 @@ public class ContinuationStackTraceTestGenerated extends AbstractContinuationSta
runTest("testData/continuation/suspendFun.kt");
}
@TestMetadata("suspendFun1.kt")
public void testSuspendFun1() throws Exception {
runTest("testData/continuation/suspendFun1.kt");
}
@TestMetadata("suspendFunStackTraceFolded.kt")
public void testSuspendFunStackTraceFolded() throws Exception {
runTest("testData/continuation/suspendFunStackTraceFolded.kt");

View File

@@ -8,6 +8,8 @@ Thread stack trace:
CoroutinePreflightFrame a:!LINE_NUMBER!, SuspendFunKt (coroutine1)
($completion, $continuation, $result, a, aParam)
CoroutineInfo: -1 coroutine UNKNOWN
CoroutineStackFrame test1:!LINE_NUMBER!, SuspendFunKt (coroutine1)
(i)
KotlinStackFrame invoke:!LINE_NUMBER!, SuspendFunKt$main$result$1 (coroutine1)
($completion, p0, this)
KotlinStackFrame invoke:!LINE_NUMBER!, SuspendFunKt$main$result$1 (coroutine1)

View File

@@ -0,0 +1,19 @@
package continuation
// ATTACH_LIBRARY: maven(org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.3.8)-javaagent
import kotlinx.coroutines.*
fun main() {
runBlocking {
SuspendFun1.f2()
println()
}
}
object SuspendFun1 {
suspend fun f2() {
//Breakpoint!
yield()
println()
}
}

View File

@@ -0,0 +1,48 @@
LineBreakpoint created at suspendFun1.kt:16
Run Java
Connected to the target VM
suspendFun1.kt:16
Thread stack trace:
CoroutinePreflightFrame f2:!LINE_NUMBER!, SuspendFun1 (continuation)
($completion, $continuation, $result)
CoroutineInfo: 1 coroutine RUNNING
CoroutineStackFrame invokeSuspend:!LINE_NUMBER!, SuspendFun1Kt$main$1 (continuation)
($completion, $continuation, $result)
InlineStackFrame lambda 'with' in 'resumeWith':!LINE_NUMBER!, BaseContinuationImpl (kotlin.coroutines.jvm.internal)
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$resumeWith_u24lambda_u240, completion, current, param, result, this)
KotlinStackFrame resumeWith$$$capture:!LINE_NUMBER!, BaseContinuationImpl (kotlin.coroutines.jvm.internal)
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$resumeWith_u24lambda_u240, completion, current, param, result, this)
KotlinStackFrame FRAME:resumeWith:-1, BaseContinuationImpl (kotlin.coroutines.jvm.internal)
(this)
Async stack trace
CapturedStackFrame <init>:!LINE_NUMBER!, DebugProbesImpl$CoroutineOwner (kotlinx.coroutines.debug.internal)
()
CapturedStackFrame createOwner:!LINE_NUMBER!, DebugProbesImpl (kotlinx.coroutines.debug.internal)
()
CapturedStackFrame probeCoroutineCreated$kotlinx_coroutines_core:!LINE_NUMBER!, DebugProbesImpl (kotlinx.coroutines.debug.internal)
()
CapturedStackFrame probeCoroutineCreated:!LINE_NUMBER!, DebugProbesKt (kotlin.coroutines.jvm.internal)
()
CapturedStackFrame createCoroutineUnintercepted:!LINE_NUMBER!, IntrinsicsKt__IntrinsicsJvmKt (kotlin.coroutines.intrinsics)
()
CapturedStackFrame startCoroutineCancellable:!LINE_NUMBER!, CancellableKt (kotlinx.coroutines.intrinsics)
()
CapturedStackFrame invoke:!LINE_NUMBER!, CoroutineStart (kotlinx.coroutines)
()
CapturedStackFrame start:!LINE_NUMBER!, AbstractCoroutine (kotlinx.coroutines)
()
CapturedStackFrame runBlocking:!LINE_NUMBER!, BuildersKt__BuildersKt (kotlinx.coroutines)
()
CapturedStackFrame runBlocking:!LINE_NUMBER!, BuildersKt (kotlinx.coroutines)
()
CapturedStackFrame runBlocking$default:!LINE_NUMBER!, BuildersKt__BuildersKt (kotlinx.coroutines)
()
CapturedStackFrame runBlocking$default:!LINE_NUMBER!, BuildersKt (kotlinx.coroutines)
()
CapturedStackFrame main:!LINE_NUMBER!, SuspendFun1Kt (continuation)
()
CapturedStackFrame FRAME:main:-1, SuspendFun1Kt (continuation)
()
Disconnected from the target VM
Process finished with exit code 0