Skip to content

Commit 55e9ff6

Browse files
committed
~: copy slice of the stacktrace into recovered one to have knitted stacktraces
1 parent f7c3e6b commit 55e9ff6

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

core/kotlinx-coroutines-core/src/internal/Exceptions.kt

+21-1
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,31 @@ private fun <E : Throwable> recoverFromStackFrame(exception: E, continuation: Co
4848
val newException = tryCopyException(exception) ?: return exception
4949
val stacktrace = createStackTrace(continuation)
5050
if (stacktrace.isEmpty()) return exception
51+
val copied = meaningfulActualStackTrace(exception)
5152
stacktrace.add(0, artificialFrame("Current coroutine stacktrace"))
52-
newException.stackTrace = stacktrace.toTypedArray()
53+
newException.stackTrace = (copied + stacktrace).toTypedArray() // TODO optimizable
5354
return newException
5455
}
5556

57+
/*
58+
* Returns slice of the original stacktrace from the original exception.
59+
* E.g. for
60+
* at kotlinx.coroutines.PlaygroundKt.foo(PlaygroundKt.kt:14)
61+
* at kotlinx.coroutines.PlaygroundKt$foo$1.invokeSuspend(PlaygroundKt.kt)
62+
* at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
63+
* at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)
64+
* at kotlinx.coroutines.DispatchedKt.resume(Dispatched.kt:277)
65+
* at kotlinx.coroutines.DispatchedKt.dispatch(Dispatched.kt:266)
66+
*
67+
* first two elements will be returned.
68+
*/
69+
private fun <E : Throwable> meaningfulActualStackTrace(exception: E): List<StackTraceElement> {
70+
val stackTrace = exception.stackTrace
71+
val index = stackTrace.indexOfFirst { "kotlin.coroutines.jvm.internal.BaseContinuationImpl" == it.className }
72+
if (index == -1) return emptyList()
73+
return stackTrace.slice(0 until index)
74+
}
75+
5676

5777
@Suppress("NOTHING_TO_INLINE")
5878
internal actual suspend inline fun recoverAndThrow(exception: Throwable): Nothing {

core/kotlinx-coroutines-core/test/exceptions/StackTraceRecoveryTest.kt

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class StackTraceRecoveryTest : TestBase() {
3131
val deferred = createDeferred(3)
3232
val traces = listOf(
3333
"java.util.concurrent.ExecutionException\n" +
34+
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testAsync\$1\$1\$1.invokeSuspend(StackTraceRecoveryTest.kt:99)\n" +
3435
"\t(Current coroutine stacktrace)\n" +
3536
"\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" +
3637
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.oneMoreNestedMethod(StackTraceRecoveryTest.kt:49)\n" +
@@ -53,6 +54,7 @@ class StackTraceRecoveryTest : TestBase() {
5354
deferred.join()
5455
val stacktrace = listOf(
5556
"java.util.concurrent.ExecutionException\n" +
57+
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCompletedAsync\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:44)\n" +
5658
"\t(Current coroutine stacktrace)\n" +
5759
"\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" +
5860
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.oneMoreNestedMethod(StackTraceRecoveryTest.kt:81)\n" +
@@ -91,6 +93,7 @@ class StackTraceRecoveryTest : TestBase() {
9193
channelNestedMethod(
9294
channel, listOf(
9395
"java.lang.IllegalArgumentException\n" +
96+
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testReceiveFromChannel\$1\$job\$1.invokeSuspend(StackTraceRecoveryTest.kt:93)\n" +
9497
"\t(Current coroutine stacktrace)\n" +
9598
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.channelNestedMethod(StackTraceRecoveryTest.kt:110)\n" +
9699
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testReceiveFromChannel\$1.invokeSuspend(StackTraceRecoveryTest.kt:89)",
@@ -141,6 +144,7 @@ class StackTraceRecoveryTest : TestBase() {
141144

142145
outerMethod(deferred, listOf(
143146
"kotlinx.coroutines.RecoverableTestException\n" +
147+
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testWithContext\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:143)\n" +
144148
"\t(Current coroutine stacktrace)\n" +
145149
"\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" +
146150
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.innerMethod(StackTraceRecoveryTest.kt:158)\n" +
@@ -177,6 +181,7 @@ class StackTraceRecoveryTest : TestBase() {
177181

178182
outerScopedMethod(deferred, listOf(
179183
"kotlinx.coroutines.RecoverableTestException\n" +
184+
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCoroutineScope\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:143)\n" +
180185
"\t(Current coroutine stacktrace)\n" +
181186
"\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" +
182187
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.innerMethod(StackTraceRecoveryTest.kt:158)\n" +

0 commit comments

Comments
 (0)