Skip to content

Commit 61a0393

Browse files
Add method that allows IDEA debugger to retrieve enhanced stack trace in a JSON format (#2933)
1 parent 5636e43 commit 61a0393

File tree

2 files changed

+78
-6
lines changed

2 files changed

+78
-6
lines changed

kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt

+29-6
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ internal object DebugProbesImpl {
183183
*/
184184
@OptIn(ExperimentalStdlibApi::class)
185185
public fun dumpCoroutinesInfoAsJsonAndReferences(): Array<Any> {
186-
fun Any.toStringWithQuotes() = "\"$this\""
187186
val coroutinesInfo = dumpCoroutinesInfo()
188187
val size = coroutinesInfo.size
189188
val lastObservedThreads = ArrayList<Thread?>(size)
@@ -196,11 +195,11 @@ internal object DebugProbesImpl {
196195
coroutinesInfoAsJson.add(
197196
"""
198197
{
199-
"name": $name,
200-
"id": ${context[CoroutineId.Key]?.id},
201-
"dispatcher": $dispatcher,
202-
"sequenceNumber": ${info.sequenceNumber},
203-
"state": "${info.state}"
198+
"name": $name,
199+
"id": ${context[CoroutineId.Key]?.id},
200+
"dispatcher": $dispatcher,
201+
"sequenceNumber": ${info.sequenceNumber},
202+
"state": "${info.state}"
204203
}
205204
""".trimIndent()
206205
)
@@ -216,6 +215,30 @@ internal object DebugProbesImpl {
216215
)
217216
}
218217

218+
/*
219+
* Internal (JVM-public) method used by IDEA debugger as of 1.6.0-RC.
220+
*/
221+
public fun enhanceStackTraceWithThreadDumpAsJson(info: DebugCoroutineInfo): String {
222+
val stackTraceElements = enhanceStackTraceWithThreadDump(info, info.lastObservedStackTrace)
223+
val stackTraceElementsInfoAsJson = mutableListOf<String>()
224+
for (element in stackTraceElements) {
225+
stackTraceElementsInfoAsJson.add(
226+
"""
227+
{
228+
"declaringClass": "${element.className}",
229+
"methodName": "${element.methodName}",
230+
"fileName": ${element.fileName?.toStringWithQuotes()},
231+
"lineNumber": ${element.lineNumber}
232+
}
233+
""".trimIndent()
234+
)
235+
}
236+
237+
return "[${stackTraceElementsInfoAsJson.joinToString()}]"
238+
}
239+
240+
private fun Any.toStringWithQuotes() = "\"$this\""
241+
219242
/*
220243
* Internal (JVM-public) method used by IDEA debugger as of 1.4-M3.
221244
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
5+
package kotlinx.coroutines.debug
6+
7+
import com.google.gson.*
8+
import kotlinx.coroutines.*
9+
import kotlinx.coroutines.debug.internal.*
10+
import org.junit.Test
11+
import kotlin.test.*
12+
13+
class EnhanceStackTraceWithTreadDumpAsJsonTest : DebugTestBase() {
14+
private data class StackTraceElementInfoFromJson(
15+
val declaringClass: String,
16+
val methodName: String,
17+
val fileName: String?,
18+
val lineNumber: Int
19+
)
20+
21+
@Test
22+
fun testEnhancedStackTraceFormatWithDeferred() = runTest {
23+
val deferred = async {
24+
suspendingMethod()
25+
assertTrue(true)
26+
}
27+
yield()
28+
29+
val coroutineInfo = DebugProbesImpl.dumpCoroutinesInfo()
30+
assertEquals(coroutineInfo.size, 2)
31+
val info = coroutineInfo[1]
32+
val enhancedStackTraceAsJsonString = DebugProbesImpl.enhanceStackTraceWithThreadDumpAsJson(info)
33+
val enhancedStackTraceFromJson = Gson().fromJson(enhancedStackTraceAsJsonString, Array<StackTraceElementInfoFromJson>::class.java)
34+
val enhancedStackTrace = DebugProbesImpl.enhanceStackTraceWithThreadDump(info, info.lastObservedStackTrace)
35+
assertEquals(enhancedStackTrace.size, enhancedStackTraceFromJson.size)
36+
for ((frame, frameFromJson) in enhancedStackTrace.zip(enhancedStackTraceFromJson)) {
37+
assertEquals(frame.className, frameFromJson.declaringClass)
38+
assertEquals(frame.methodName, frameFromJson.methodName)
39+
assertEquals(frame.fileName, frameFromJson.fileName)
40+
assertEquals(frame.lineNumber, frameFromJson.lineNumber)
41+
}
42+
43+
deferred.cancelAndJoin()
44+
}
45+
46+
private suspend fun suspendingMethod() {
47+
delay(Long.MAX_VALUE)
48+
}
49+
}

0 commit comments

Comments
 (0)