If a coroutine suspends at, say, callstack A, and then is resumed with an exception created at callstack B, the exception’s stack trace doesn’t include callstack A.
This kind of defeats the purpose of coroutines, and make them feel awkward compared to threads. Many times, it makes stack traces useless for debugging.
I’m working around this by wrapping some critical points where coroutines may get suspended and then resumed with an exception like this:
fun stackChainer(): (Throwable) -> Throwable {
val original = Throwable()
return { t ->
RuntimeException("exception caught while handling async operation result: $t", t).apply {
stackTrace = original.stackTrace
}
}
}
internal suspend fun <T> keepStack(block: suspend () -> T): T = stackChainer().let { chain ->
try {
block()
} catch (t: Throwable) {
throw chain(t)
}
}
But I’d like something akin to this to happen automatically and pervasively, in an opt-in way.
I’ve seen the DebugProbes API, which seems to keep the necessary data about coroutines to accomplish this, but then doesn’t.
Is there a way to do this I’m missing? Should it be considered?