xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-debug/src/CoroutineInfo.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
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