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