|
| 1 | +# Module kotlinx-coroutines-debug |
| 2 | + |
| 3 | +Debugging facilities for `kotlinx.coroutines` on JVM. |
| 4 | + |
| 5 | +### Overview |
| 6 | +This module provides a debug JVM agent which allows to track and trace alive coroutines. |
| 7 | +Main entry point to debug facilities is [DebugProbes]. |
| 8 | +Call to [DebugProbes.install] installs debug agent via ByteBuddy and starts to spy on coroutines when they are created, suspended or resumed. |
| 9 | + |
| 10 | +After that you can use [DebugProbes.dumpCoroutines] to print all active (suspended or running) coroutines, including their state, creation and |
| 11 | +suspension stacktraces. |
| 12 | +Additionally, it is possible to process list of such coroutines via [DebugProbes.dumpCoroutinesState] or dump isolated parts |
| 13 | +of coroutines hierarchies referenced by [Job] instance using [DebugProbes.printHierarchy]. |
| 14 | + |
| 15 | +### Using as JVM agent |
| 16 | +Additionally, it is possible to use this module as standalone JVM agent to enable debug probes on the application startup. |
| 17 | +You can run your application with additional argument: `-javaagent:kotlinx-coroutines-debug-1.1.0.jar`. |
| 18 | +Additionally, on Linux and Mac OS X you can use `kill -5 $pid` command in order to force your application to print all alive coroutines. |
| 19 | + |
| 20 | + |
| 21 | +### Example of usage |
| 22 | + |
| 23 | +Capabilities of this module can be demonstrated by the following example: |
| 24 | +```kotlin |
| 25 | +class Computation { |
| 26 | + public fun computeValue(): Deferred<String> = GlobalScope.async { |
| 27 | + val firstPart = computeFirstPart() |
| 28 | + val secondPart = computeSecondPart() |
| 29 | + |
| 30 | + combineResults(firstPart, secondPart) |
| 31 | + } |
| 32 | + |
| 33 | + private suspend fun combineResults(firstPart: Deferred<String>, secondPart: Deferred<String>): String { |
| 34 | + return firstPart.await() + secondPart.await() |
| 35 | + } |
| 36 | + |
| 37 | + |
| 38 | + private suspend fun CoroutineScope.computeFirstPart() = async { |
| 39 | + delay(5000) |
| 40 | + "4" |
| 41 | + } |
| 42 | + |
| 43 | + private suspend fun CoroutineScope.computeSecondPart() = async { |
| 44 | + delay(5000) |
| 45 | + "2" |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +fun main(args: Array<String>) = runBlocking { |
| 50 | + DebugProbes.install() |
| 51 | + val computation = Computation() |
| 52 | + val deferred = computation.computeValue() |
| 53 | + |
| 54 | + // Delay for some time |
| 55 | + delay(1000) |
| 56 | + |
| 57 | + DebugProbes.dumpCoroutines() |
| 58 | + |
| 59 | + println("\nDumping only deferred") |
| 60 | + DebugProbes.printHierarchy(deferred) |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +Printed result will be: |
| 65 | +``` |
| 66 | +Coroutines dump 2018/11/12 21:44:02 |
| 67 | +
|
| 68 | +Coroutine "coroutine#2":DeferredCoroutine{Active}@1b26f7b2, state: SUSPENDED |
| 69 | + at kotlinx.coroutines.DeferredCoroutine.await$suspendImpl(Builders.common.kt:99) |
| 70 | + at Computation.combineResults(Example.kt:18) |
| 71 | + at Computation$computeValue$1.invokeSuspend(Example.kt:14) |
| 72 | + (Coroutine creation stacktrace) |
| 73 | + at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116) |
| 74 | + at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23) |
| 75 | + at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109) |
| 76 | + at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:160) |
| 77 | + at kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt:88) |
| 78 | + at kotlinx.coroutines.BuildersKt.async(Unknown Source) |
| 79 | + at kotlinx.coroutines.BuildersKt__Builders_commonKt.async$default(Builders.common.kt:81) |
| 80 | + at kotlinx.coroutines.BuildersKt.async$default(Unknown Source) |
| 81 | + at Computation.computeValue(Example.kt:10) |
| 82 | + at ExampleKt$main$1.invokeSuspend(Example.kt:36) |
| 83 | + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32) |
| 84 | + at kotlinx.coroutines.DispatchedTask$DefaultImpls.run(Dispatched.kt:237) |
| 85 | + at kotlinx.coroutines.DispatchedContinuation.run(Dispatched.kt:81) |
| 86 | + at kotlinx.coroutines.EventLoopBase.processNextEvent(EventLoop.kt:123) |
| 87 | + at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:69) |
| 88 | + at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:45) |
| 89 | + at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) |
| 90 | + at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:35) |
| 91 | + at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) |
| 92 | + at ExampleKt.main(Example.kt:33) |
| 93 | +
|
| 94 | +... More coroutines here ... |
| 95 | +
|
| 96 | +Dumping only deferred |
| 97 | +"coroutine#2":DeferredCoroutine{Active}, continuation is SUSPENDED at line kotlinx.coroutines.DeferredCoroutine.await$suspendImpl(Builders.common.kt:99) |
| 98 | + "coroutine#3":DeferredCoroutine{Active}, continuation is SUSPENDED at line Computation$computeFirstPart$2.invokeSuspend(Example.kt:23) |
| 99 | + "coroutine#4":DeferredCoroutine{Active}, continuation is SUSPENDED at line Computation$computeSecondPart$2.invokeSuspend(Example.kt:28) |
| 100 | +``` |
| 101 | + |
| 102 | + |
| 103 | +### Status of the API |
| 104 | + |
| 105 | +API is purely experimental and it is not guaranteed that it won't be changed (while it is marked as `@ExperimentalCoroutinesApi`). |
| 106 | +Do not use this module in production environment and do not rely on the format of the data produced by [DebugProbes]. |
| 107 | + |
| 108 | +<!--- MODULE kotlinx-coroutines-core --> |
| 109 | +<!--- INDEX kotlinx.coroutines --> |
| 110 | +[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html |
| 111 | +<!--- MODULE kotlinx-coroutines-debug --> |
| 112 | +<!--- INDEX kotlinx.coroutines.debug --> |
| 113 | +[DebugProbes]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/index.html |
| 114 | +[DebugProbes.install]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/install.html |
| 115 | +[DebugProbes.dumpCoroutines]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines.html |
| 116 | +[DebugProbes.dumpCoroutinesState]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines-state.html |
| 117 | +[DebugProbes.printHierarchy]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/print-hierarchy.html |
| 118 | +<!--- END --> |
0 commit comments