|
| 1 | +/* |
| 2 | + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
| 3 | + */ |
| 4 | + |
| 5 | +package kotlinx.coroutines.debug.internal |
| 6 | + |
| 7 | +import kotlin.coroutines.* |
| 8 | +import kotlin.coroutines.jvm.internal.* |
| 9 | + |
| 10 | +internal const val CREATED = "CREATED" |
| 11 | +internal const val RUNNING = "RUNNING" |
| 12 | +internal const val SUSPENDED = "SUSPENDED" |
| 13 | + |
| 14 | +internal class DebugCoroutineInfo( |
| 15 | + public val context: CoroutineContext, |
| 16 | + public val creationStackBottom: CoroutineStackFrame?, |
| 17 | + @JvmField internal val sequenceNumber: Long |
| 18 | +) { |
| 19 | + |
| 20 | + public val creationStackTrace: List<StackTraceElement> get() = creationStackTrace() |
| 21 | + |
| 22 | + /** |
| 23 | + * Last observed state of the coroutine. |
| 24 | + * Can be CREATED, RUNNING, SUSPENDED. |
| 25 | + */ |
| 26 | + public val state: String get() = _state |
| 27 | + private var _state: String = CREATED |
| 28 | + |
| 29 | + @JvmField |
| 30 | + internal var lastObservedThread: Thread? = null |
| 31 | + @JvmField |
| 32 | + internal var lastObservedFrame: CoroutineStackFrame? = null |
| 33 | + |
| 34 | + public fun copy(): DebugCoroutineInfo = DebugCoroutineInfo( |
| 35 | + context, |
| 36 | + creationStackBottom, |
| 37 | + sequenceNumber |
| 38 | + ).also { |
| 39 | + it._state = _state |
| 40 | + it.lastObservedFrame = lastObservedFrame |
| 41 | + it.lastObservedThread = lastObservedThread |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * Last observed stacktrace of the coroutine captured on its suspension or resumption point. |
| 46 | + * It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and |
| 47 | + * reflects stacktrace of the resumption point, not the actual current stacktrace. |
| 48 | + */ |
| 49 | + public fun lastObservedStackTrace(): List<StackTraceElement> { |
| 50 | + var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList() |
| 51 | + val result = ArrayList<StackTraceElement>() |
| 52 | + while (frame != null) { |
| 53 | + frame.getStackTraceElement()?.let { result.add(it) } |
| 54 | + frame = frame.callerFrame |
| 55 | + } |
| 56 | + return result |
| 57 | + } |
| 58 | + |
| 59 | + private fun creationStackTrace(): List<StackTraceElement> { |
| 60 | + val bottom = creationStackBottom ?: return emptyList() |
| 61 | + // Skip "Coroutine creation stacktrace" frame |
| 62 | + return sequence<StackTraceElement> { yieldFrames(bottom.callerFrame) }.toList() |
| 63 | + } |
| 64 | + |
| 65 | + private tailrec suspend fun SequenceScope<StackTraceElement>.yieldFrames(frame: CoroutineStackFrame?) { |
| 66 | + if (frame == null) return |
| 67 | + frame.getStackTraceElement()?.let { yield(it) } |
| 68 | + val caller = frame.callerFrame |
| 69 | + if (caller != null) { |
| 70 | + yieldFrames(caller) |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + internal fun updateState(state: String, frame: Continuation<*>) { |
| 75 | + // Propagate only duplicating transitions to running for KT-29997 |
| 76 | + if (_state == state && state == SUSPENDED && lastObservedFrame != null) return |
| 77 | + _state = state |
| 78 | + lastObservedFrame = frame as? CoroutineStackFrame |
| 79 | + lastObservedThread = if (state == RUNNING) { |
| 80 | + Thread.currentThread() |
| 81 | + } else { |
| 82 | + null |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + override fun toString(): String = "DebugCoroutineInfo(state=$state,context=$context)" |
| 87 | +} |
0 commit comments