Skip to content

Commit 9cc55c8

Browse files
committed
~: optimize traces slicing
1 parent ca3acc0 commit 9cc55c8

File tree

1 file changed

+40
-40
lines changed

1 file changed

+40
-40
lines changed

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

+40-40
Original file line numberDiff line numberDiff line change
@@ -65,27 +65,46 @@ private fun <E : Throwable> recoverFromStackFrame(exception: E, continuation: Co
6565
mergeRecoveredTraces(recoveredStacktrace, stacktrace)
6666
}
6767

68-
/*
69-
* Here we partially copy original exception stacktrace to make current one much prettier.
70-
* E.g. for
71-
* ```
72-
* fun foo() = async { error(...) }
73-
* suspend fun bar() = foo().await()
74-
* ```
75-
* we would like to produce following exception:
76-
* IllegalStateException
77-
* at foo
78-
* at kotlinx.coroutines.resumeWith
79-
* (Current coroutine stacktrace)
80-
* at bar
81-
* ...real stacktrace...
82-
* caused by "IllegalStateException" (original one)
83-
*/
84-
// TODO optimizable allocations and passes
85-
stacktrace.addFirst(artificialFrame("Coroutine boundary"))
86-
val copied = meaningfulActualStackTrace(cause)
87-
newException.stackTrace = (copied + stacktrace).toTypedArray()
88-
return newException
68+
// Take recovered stacktrace, merge it with existing one if necessary and return
69+
return createFinalException(cause, newException, stacktrace)
70+
}
71+
72+
/*
73+
* Here we partially copy original exception stackTrace to make current one much prettier.
74+
* E.g. for
75+
* ```
76+
* fun foo() = async { error(...) }
77+
* suspend fun bar() = foo().await()
78+
* ```
79+
* we would like to produce following exception:
80+
* IllegalStateException
81+
* at foo
82+
* at kotlin.coroutines.resumeWith
83+
* (Coroutine boundary)
84+
* at bar
85+
* ...real stackTrace...
86+
* caused by "IllegalStateException" (original one)
87+
*/
88+
private fun <E : Throwable> createFinalException(cause: E, result: E, resultStackTrace: ArrayDeque<StackTraceElement>): E {
89+
resultStackTrace.addFirst(artificialFrame("Coroutine boundary"))
90+
val causeTrace = cause.stackTrace
91+
val size = causeTrace.frameIndex("kotlin.coroutines.jvm.internal.BaseContinuationImpl")
92+
if (size == -1) {
93+
result.stackTrace = resultStackTrace.toTypedArray()
94+
return result
95+
}
96+
97+
val mergedStackTrace = arrayOfNulls<StackTraceElement>(resultStackTrace.size + size)
98+
for (i in 0 until size) {
99+
mergedStackTrace[i] = causeTrace[i]
100+
}
101+
102+
for ((index, element) in resultStackTrace.withIndex()) {
103+
mergedStackTrace[size + index] = element
104+
}
105+
106+
result.stackTrace = mergedStackTrace
107+
return result
89108
}
90109

91110
/**
@@ -117,25 +136,6 @@ private fun mergeRecoveredTraces(recoveredStacktrace: Array<StackTraceElement>,
117136
}
118137
}
119138

120-
/*
121-
* Returns slice of the original stacktrace from the original exception.
122-
* E.g. for
123-
* at kotlinx.coroutines.PlaygroundKt.foo(PlaygroundKt.kt:14)
124-
* at kotlinx.coroutines.PlaygroundKt$foo$1.invokeSuspend(PlaygroundKt.kt)
125-
* at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
126-
* at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)
127-
* at kotlinx.coroutines.DispatchedKt.resume(Dispatched.kt:277)
128-
* at kotlinx.coroutines.DispatchedKt.dispatch(Dispatched.kt:266)
129-
*
130-
* first two elements will be returned.
131-
*/
132-
private fun <E : Throwable> meaningfulActualStackTrace(exception: E): List<StackTraceElement> {
133-
val stackTrace = exception.stackTrace
134-
val index = stackTrace.frameIndex("kotlin.coroutines.jvm.internal.BaseContinuationImpl")
135-
if (index == -1) return emptyList()
136-
return stackTrace.slice(0 until index)
137-
}
138-
139139
@Suppress("NOTHING_TO_INLINE")
140140
internal actual suspend inline fun recoverAndThrow(exception: Throwable): Nothing {
141141
if (recoveryDisabled(exception)) throw exception

0 commit comments

Comments
 (0)