.kotlinx.kotlinx-coroutines-debug.1.10.1.source-code.DebugProbes.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-coroutines-debug Show documentation
Show all versions of kotlinx-coroutines-debug Show documentation
Coroutines support libraries for Kotlin
The newest version!
@file:Suppress("UNUSED", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package kotlinx.coroutines.debug
import kotlinx.coroutines.*
import kotlinx.coroutines.debug.internal.*
import java.io.*
import java.lang.management.*
import kotlin.coroutines.*
/**
* Kotlin debug probes support.
*
* Debug probes is a dynamic attach mechanism which installs multiple hooks into coroutines machinery.
* It slows down all coroutine-related code, but in return provides diagnostic information, including
* asynchronous stacktraces, coroutine dumps (similar to [ThreadMXBean.dumpAllThreads] and `jstack`) via [DebugProbes.dumpCoroutines],
* and programmatic introspection of all alive coroutines.
* All introspecting methods throw [IllegalStateException] if debug probes were not installed.
*
* ### Consistency guarantees
*
* All snapshotting operations (e.g. [dumpCoroutines]) are *weakly-consistent*, meaning that they happen
* concurrently with coroutines progressing their own state. These operations are guaranteed to observe
* each coroutine's state exactly once, but the state is not guaranteed to be the most recent before the operation.
* In practice, it means that for snapshotting operations in progress, for each concurrent coroutine either
* the state prior to the operation or the state that was reached during the current operation is observed.
*
* ### Overhead
*
* - Every created coroutine is stored in a concurrent hash map, and the hash map is looked up in and
* updated on each suspension and resumption.
* - If [DebugProbes.enableCreationStackTraces] is enabled, stack trace of the current thread is captured on
* each created coroutine that is a rough equivalent of throwing an exception per each created coroutine.
*
* ### Internal machinery and classloading.
*
* Under the hood, debug probes replace internal `kotlin.coroutines.jvm.internal.DebugProbesKt` class that has the following
* empty static methods:
*
* - `probeCoroutineResumed` that is invoked on every [Continuation.resume].
* - `probeCoroutineSuspended` that is invoked on every continuation suspension.
* - `probeCoroutineCreated` that is invoked on every coroutine creation.
*
* with a `kotlinx-coroutines`-specific class to keep track of all the coroutines machinery.
*
* The new class is located in the `kotlinx-coroutines-core` module, meaning that all target application classes that use
* coroutines and `suspend` functions have to be loaded by the classloader in which `kotlinx-coroutines-core` classes are available.
*/
@ExperimentalCoroutinesApi
public object DebugProbes {
/**
* Whether coroutine creation stack traces should be sanitized.
* Sanitization removes all frames from `kotlinx.coroutines` package except
* the first one and the last one to simplify diagnostic.
*
* `true` by default.
*/
public var sanitizeStackTraces: Boolean
get() = DebugProbesImpl.sanitizeStackTraces
@Suppress("INVISIBLE_SETTER") // do not remove the INVISIBLE_SETTER suppression: required in k2
set(value) {
DebugProbesImpl.sanitizeStackTraces = value
}
/**
* Whether coroutine creation stack traces should be captured.
* When enabled, for each created coroutine a stack trace of the current thread is captured and attached to the coroutine.
* This option can be useful during local debug sessions, but is recommended
* to be disabled in production environments to avoid performance overhead of capturing real stacktraces.
*
* `false` by default.
*/
public var enableCreationStackTraces: Boolean
get() = DebugProbesImpl.enableCreationStackTraces
@Suppress("INVISIBLE_SETTER") // do not remove the INVISIBLE_SETTER suppression: required in k2
set(value) {
DebugProbesImpl.enableCreationStackTraces = value
}
/**
* Whether to ignore coroutines whose context is [EmptyCoroutineContext].
*
* Coroutines with empty context are considered to be irrelevant for the concurrent coroutines' observability:
* - They do not contribute to any concurrent executions
* - They do not contribute to the (concurrent) system's liveness and/or deadlocks, as no other coroutines might wait for them
* - The typical usage of such coroutines is a combinator/builder/lookahead parser that can be debugged using more convenient tools.
*
* `true` by default.
*/
public var ignoreCoroutinesWithEmptyContext: Boolean
get() = DebugProbesImpl.ignoreCoroutinesWithEmptyContext
@Suppress("INVISIBLE_SETTER") // do not remove the INVISIBLE_SETTER suppression: required in k2
set(value) {
DebugProbesImpl.ignoreCoroutinesWithEmptyContext = value
}
/**
* Determines whether debug probes were [installed][DebugProbes.install].
*/
public val isInstalled: Boolean get() = DebugProbesImpl.isInstalled
/**
* Installs a [DebugProbes] instead of no-op stdlib probes by redefining
* debug probes class using the same class loader as one loaded [DebugProbes] class.
*/
public fun install() {
DebugProbesImpl.install()
}
/**
* Uninstall debug probes.
*/
public fun uninstall() {
DebugProbesImpl.uninstall()
}
/**
* Invokes given block of code with installed debug probes and uninstall probes in the end.
*/
public inline fun withDebugProbes(block: () -> Unit) {
install()
try {
block()
} finally {
uninstall()
}
}
/**
* Returns string representation of the coroutines [job] hierarchy with additional debug information.
* Hierarchy is printed from the [job] as a root transitively to all children.
*/
public fun jobToString(job: Job): String = DebugProbesImpl.hierarchyToString(job)
/**
* Returns string representation of all coroutines launched within the given [scope].
* Throws [IllegalStateException] if the scope has no a job in it.
*/
public fun scopeToString(scope: CoroutineScope): String =
jobToString(scope.coroutineContext[Job] ?: error("Job is not present in the scope"))
/**
* Prints [job] hierarchy representation from [jobToString] to the given [out].
*/
public fun printJob(job: Job, out: PrintStream = System.out): Unit =
out.println(DebugProbesImpl.hierarchyToString(job))
/**
* Prints all coroutines launched within the given [scope].
* Throws [IllegalStateException] if the scope has no a job in it.
*/
public fun printScope(scope: CoroutineScope, out: PrintStream = System.out): Unit =
printJob(scope.coroutineContext[Job] ?: error("Job is not present in the scope"), out)
/**
* Returns all existing coroutines' info.
* The resulting collection represents a consistent snapshot of all existing coroutines at the moment of invocation.
*/
public fun dumpCoroutinesInfo(): List =
DebugProbesImpl.dumpCoroutinesInfo().map { CoroutineInfo(it) }
/**
* Dumps all active coroutines into the given output stream, providing a consistent snapshot of all existing coroutines at the moment of invocation.
* The output of this method is similar to `jstack` or a full thread dump. It can be used as the replacement to
* "Dump threads" action.
*
* Example of the output:
* ```
* Coroutines dump 2018/11/12 19:45:14
*
* Coroutine "coroutine#42":StandaloneCoroutine{Active}@58fdd99, state: SUSPENDED
* at MyClass$awaitData.invokeSuspend(MyClass.kt:37)
* at _COROUTINE._CREATION._(CoroutineDebugging.kt)
* at MyClass.createIoRequest(MyClass.kt:142)
* at MyClass.fetchData(MyClass.kt:154)
* at MyClass.showData(MyClass.kt:31)
* ...
* ```
*/
public fun dumpCoroutines(out: PrintStream = System.out): Unit = DebugProbesImpl.dumpCoroutines(out)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy