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