• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 @file:Suppress("unused")
6 
7 package kotlinx.coroutines.debug
8 
9 import kotlinx.coroutines.*
10 import kotlinx.coroutines.debug.internal.*
11 import java.io.*
12 import java.lang.management.*
13 import java.util.*
14 import kotlin.coroutines.*
15 
16 /**
17  * Debug probes support.
18  *
19  * Debug probes is a dynamic attach mechanism which installs multiple hooks into coroutines machinery.
20  * It slows down all coroutine-related code, but in return provides a lot of diagnostic information, including
21  * asynchronous stack-traces and coroutine dumps (similar to [ThreadMXBean.dumpAllThreads] and `jstack` via [DebugProbes.dumpCoroutines].
22  *
23  * Installed hooks:
24  *
25  * * `probeCoroutineResumed` is invoked on every [Continuation.resume].
26  * * `probeCoroutineSuspended` is invoked on every continuation suspension.
27  * * `probeCoroutineCreated` is invoked on every coroutine creation using stdlib intrinsics.
28  *
29  * Overhead:
30  *  * Every created coroutine is stored in a weak hash map, thus adding additional GC pressure.
31  *  * On every created coroutine, stacktrace of the current thread is dumped.
32  *  * On every `resume` and `suspend`, [WeakHashMap] is updated under a global lock.
33  */
34 @ExperimentalCoroutinesApi
35 public object DebugProbes {
36 
37     /**
38      * Whether coroutine creation stacktraces should be sanitized.
39      * Sanitization removes all frames from `kotlinx.coroutines` package except
40      * the first one and the last one to simplify diagnostic.
41      */
42     public var sanitizeStackTraces: Boolean = true
43 
44     /**
45      * Installs a [DebugProbes] instead of no-op stdlib probes by redefining
46      * debug probes class using the same class loader as one loaded [DebugProbes] class.
47      */
installnull48     public fun install() {
49         DebugProbesImpl.install()
50     }
51 
52     /**
53      * Uninstall debug probes.
54      */
uninstallnull55     public fun uninstall() {
56         DebugProbesImpl.uninstall()
57     }
58 
59     /**
60      * Invokes given block of code with installed debug probes and uninstall probes in the end.
61      */
withDebugProbesnull62     public inline fun withDebugProbes(block: () -> Unit) {
63         install()
64         try {
65             block()
66         } finally {
67             uninstall()
68         }
69     }
70 
71     /**
72      * Returns string representation of the coroutines [job] hierarchy with additional debug information.
73      * Hierarchy is printed from the [job] as a root transitively to all children.
74      */
jobToStringnull75     public fun jobToString(job: Job): String = DebugProbesImpl.hierarchyToString(job)
76 
77     /**
78      * Returns string representation of all coroutines launched within the given [scope].
79      * Throws [IllegalStateException] if the scope has no a job in it.
80      */
81     public fun scopeToString(scope: CoroutineScope): String =
82         jobToString(scope.coroutineContext[Job] ?: error("Job is not present in the scope"))
83 
84     /**
85      * Prints [job] hierarchy representation from [jobToString] to the given [out].
86      */
87     public fun printJob(job: Job, out: PrintStream = System.out): Unit =
88         out.println(DebugProbesImpl.hierarchyToString(job))
89 
90     /**
91      * Prints all coroutines launched within the given [scope].
92      * Throws [IllegalStateException] if the scope has no a job in it.
93      */
94     public fun printScope(scope: CoroutineScope, out: PrintStream = System.out): Unit =
95        printJob(scope.coroutineContext[Job] ?: error("Job is not present in the scope"), out)
96 
97     /**
98      * Returns all existing coroutines info.
99      * The resulting collection represents a consistent snapshot of all existing coroutines at the moment of invocation.
100      */
101     public fun dumpCoroutinesInfo(): List<CoroutineInfo> = DebugProbesImpl.dumpCoroutinesInfo()
102 
103     /**
104      * Dumps all active coroutines into the given output stream, providing a consistent snapshot of all existing coroutines at the moment of invocation.
105      * The output of this method is similar to `jstack` or a full thread dump. It can be used as the replacement to
106      * "Dump threads" action.
107      *
108      * Example of the output:
109      * ```
110      * Coroutines dump 2018/11/12 19:45:14
111      *
112      * Coroutine "coroutine#42":StandaloneCoroutine{Active}@58fdd99, state: SUSPENDED
113      *     at MyClass$awaitData.invokeSuspend(MyClass.kt:37)
114      * (Coroutine creation stacktrace)
115      *     at MyClass.createIoRequest(MyClass.kt:142)
116      *     at MyClass.fetchData(MyClass.kt:154)
117      *     at MyClass.showData(MyClass.kt:31)
118      *
119      * ...
120      * ```
121      */
122     public fun dumpCoroutines(out: PrintStream = System.out) = DebugProbesImpl.dumpCoroutines(out)
123 }
124 
125 // Stubs which are injected as coroutine probes. Require direct match of signatures
126 internal fun probeCoroutineResumed(frame: Continuation<*>) = DebugProbesImpl.probeCoroutineResumed(frame)
127 
128 internal fun probeCoroutineSuspended(frame: Continuation<*>) = DebugProbesImpl.probeCoroutineSuspended(frame)
129 internal fun <T> probeCoroutineCreated(completion: kotlin.coroutines.Continuation<T>): kotlin.coroutines.Continuation<T> =
130     DebugProbesImpl.probeCoroutineCreated(completion)
131