/* * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "UNUSED") package kotlinx.coroutines.debug import kotlinx.coroutines.* import kotlinx.coroutines.debug.internal.* import kotlin.coroutines.* import kotlin.coroutines.jvm.internal.* /** * Class describing coroutine info such as its context, state and stacktrace. */ @ExperimentalCoroutinesApi public class CoroutineInfo internal constructor(delegate: DebugCoroutineInfo) { /** * [Coroutine context][coroutineContext] of the coroutine */ public val context: CoroutineContext = delegate.context /** * Last observed state of the coroutine */ public val state: State = State.valueOf(delegate.state) private val creationStackBottom: CoroutineStackFrame? = delegate.creationStackBottom /** * [Job] associated with a current coroutine or null. * May be later used in [DebugProbes.printJob]. */ public val job: Job? get() = context[Job] /** * Creation stacktrace of the coroutine. * Can be empty if [DebugProbes.enableCreationStackTraces] is not set. */ public val creationStackTrace: List get() = creationStackTrace() private val lastObservedFrame: CoroutineStackFrame? = delegate.lastObservedFrame /** * Last observed stacktrace of the coroutine captured on its suspension or resumption point. * It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and * reflects stacktrace of the resumption point, not the actual current stacktrace. */ public fun lastObservedStackTrace(): List { var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList() val result = ArrayList() while (frame != null) { frame.getStackTraceElement()?.let { result.add(it) } frame = frame.callerFrame } return result } private fun creationStackTrace(): List { val bottom = creationStackBottom ?: return emptyList() // Skip "Coroutine creation stacktrace" frame return sequence { yieldFrames(bottom.callerFrame) }.toList() } private tailrec suspend fun SequenceScope.yieldFrames(frame: CoroutineStackFrame?) { if (frame == null) return frame.getStackTraceElement()?.let { yield(it) } val caller = frame.callerFrame if (caller != null) { yieldFrames(caller) } } override fun toString(): String = "CoroutineInfo(state=$state,context=$context)" } /** * Current state of the coroutine. */ public enum class State { /** * Created, but not yet started. */ CREATED, /** * Started and running. */ RUNNING, /** * Suspended. */ SUSPENDED }