1 @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "UNUSED") 2 package kotlinx.coroutines.debug 3 4 import kotlinx.coroutines.* 5 import kotlinx.coroutines.debug.internal.* 6 import kotlin.coroutines.* 7 import kotlin.coroutines.jvm.internal.* 8 9 /** 10 * Class describing coroutine info such as its context, state and stacktrace. 11 */ 12 @ExperimentalCoroutinesApi 13 public class CoroutineInfo internal constructor(delegate: DebugCoroutineInfo) { 14 /** 15 * [Coroutine context][coroutineContext] of the coroutine 16 */ 17 public val context: CoroutineContext = delegate.context 18 19 /** 20 * Last observed state of the coroutine 21 */ 22 public val state: State = State.valueOf(delegate.state) 23 24 private val creationStackBottom: CoroutineStackFrame? = delegate.creationStackBottom 25 26 /** 27 * [Job] associated with a current coroutine or null. 28 * May be later used in [DebugProbes.printJob]. 29 */ 30 public val job: Job? get() = context[Job] 31 32 /** 33 * Creation stacktrace of the coroutine. 34 * Can be empty if [DebugProbes.enableCreationStackTraces] is not set. 35 */ 36 public val creationStackTrace: List<StackTraceElement> get() = creationStackTrace() 37 38 private val lastObservedFrame: CoroutineStackFrame? = delegate.lastObservedFrame 39 40 /** 41 * Last observed stacktrace of the coroutine captured on its suspension or resumption point. 42 * It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and 43 * reflects stacktrace of the resumption point, not the actual current stacktrace. 44 */ lastObservedStackTracenull45 public fun lastObservedStackTrace(): List<StackTraceElement> { 46 var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList() 47 val result = ArrayList<StackTraceElement>() 48 while (frame != null) { 49 frame.getStackTraceElement()?.let { result.add(it) } 50 frame = frame.callerFrame 51 } 52 return result 53 } 54 creationStackTracenull55 private fun creationStackTrace(): List<StackTraceElement> { 56 val bottom = creationStackBottom ?: return emptyList() 57 // Skip "Coroutine creation stacktrace" frame 58 return sequence<StackTraceElement> { yieldFrames(bottom.callerFrame) }.toList() 59 } 60 yieldFramesnull61 private tailrec suspend fun SequenceScope<StackTraceElement>.yieldFrames(frame: CoroutineStackFrame?) { 62 if (frame == null) return 63 frame.getStackTraceElement()?.let { yield(it) } 64 val caller = frame.callerFrame 65 if (caller != null) { 66 yieldFrames(caller) 67 } 68 } 69 toStringnull70 override fun toString(): String = "CoroutineInfo(state=$state,context=$context)" 71 } 72 73 /** 74 * Current state of the coroutine. 75 */ 76 public enum class State { 77 /** 78 * Created, but not yet started. 79 */ 80 CREATED, 81 /** 82 * Started and running. 83 */ 84 RUNNING, 85 /** 86 * Suspended. 87 */ 88 SUSPENDED 89 } 90