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