blob: ce1478ad0754c2e41e622ca511696ebf229295f6 [file] [log] [blame]
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE", "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<StackTraceElement> 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<StackTraceElement> {
var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList()
val result = ArrayList<StackTraceElement>()
while (frame != null) {
frame.getStackTraceElement()?.let { result.add(it) }
frame = frame.callerFrame
}
return result
}
private fun creationStackTrace(): List<StackTraceElement> {
val bottom = creationStackBottom ?: return emptyList()
// Skip "Coroutine creation stacktrace" frame
return sequence<StackTraceElement> { yieldFrames(bottom.callerFrame) }.toList()
}
private tailrec suspend fun SequenceScope<StackTraceElement>.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
}