Name Date Size #Lines LOC

..--

api/H25-Apr-2025-7264

resources/META-INF/services/H25-Apr-2025-11

src/H25-Apr-2025-1,063541

test/H25-Apr-2025-2,6122,147

README.mdH A D25-Apr-202515.3 KiB276224

build.gradle.ktsH A D25-Apr-20254.2 KiB11289

README.md

1# Module kotlinx-coroutines-debug
2
3Debugging facilities for `kotlinx.coroutines` on JVM.
4
5### Overview
6
7This module provides a debug JVM agent that allows to track and trace existing coroutines.
8The main entry point to debug facilities is [DebugProbes] API.
9Call to [DebugProbes.install] installs debug agent via ByteBuddy and starts spying on coroutines when they are created, suspended and resumed.
10
11After that, you can use [DebugProbes.dumpCoroutines] to print all active (suspended or running) coroutines, including their state, creation and
12suspension stacktraces.
13Additionally, it is possible to process the list of such coroutines via [DebugProbes.dumpCoroutinesInfo] or dump isolated parts
14of coroutines hierarchy referenced by a [Job] or [CoroutineScope] instances using  [DebugProbes.printJob] and [DebugProbes.printScope] respectively.
15
16This module also provides an automatic [BlockHound](https://github.com/reactor/BlockHound) integration
17that detects when a blocking operation was called in a coroutine context that prohibits it. In order to use it,
18please follow the BlockHound [quick start guide](
19https://github.com/reactor/BlockHound/blob/1.0.8.RELEASE/docs/quick_start.md).
20
21### Using in your project
22
23Add `kotlinx-coroutines-debug` to your project test dependencies:
24```
25dependencies {
26    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.4.0'
27}
28```
29
30### Using in unit tests
31
32For JUnit4 debug module provides special test rule, [CoroutinesTimeout], for installing debug probes
33and to dump coroutines on timeout to simplify tests debugging.
34
35Its usage is better demonstrated by the example (runnable code is [here](test/TestRuleExample.kt)):
36
37```kotlin
38class TestRuleExample {
39    @get:Rule
40    public val timeout = CoroutinesTimeout.seconds(1)
41
42    private suspend fun someFunctionDeepInTheStack() {
43        withContext(Dispatchers.IO) {
44            delay(Long.MAX_VALUE) // Hang method
45        }
46    }
47
48    @Test
49    fun hangingTest() = runBlocking {
50        val job = launch {
51            someFunctionDeepInTheStack()
52        }
53        job.join() // Join will hang
54    }
55}
56```
57
58After 1 second, test will fail with `TestTimeoutException` and all coroutines (`runBlocking` and `launch`) and their
59stacktraces will be dumped to the console.
60
61### Using as JVM agent
62
63Debug module can also be used as a standalone JVM agent to enable debug probes on the application startup.
64You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.8.1.jar`.
65Additionally, on Linux and Mac OS X you can use `kill -5 $pid` command in order to force your application to print all alive coroutines.
66When used as Java agent, `"kotlinx.coroutines.debug.enable.creation.stack.trace"` system property can be used to control
67[DebugProbes.enableCreationStackTraces] along with agent startup.
68
69### Using in production environment
70
71It is possible to run an application in production environments with debug probes in order to monitor its
72state and improve its observability.
73For that, it is strongly recommended not to enable [DebugProbes.enableCreationStackTraces], as enabling it makes
74the performance overhead of the debug probes non-negligible.
75With creation stack-traces disabled, the typical overhead of enabled debug probes is a single-digit percentage of the total
76application throughput.
77
78
79### Example of usage
80
81Capabilities of this module can be demonstrated by the following example
82(runnable code is [here](test/Example.kt)):
83
84```kotlin
85suspend fun computeValue(): String = coroutineScope {
86    val one = async { computeOne() }
87    val two = async { computeTwo() }
88    combineResults(one, two)
89}
90
91suspend fun combineResults(one: Deferred<String>, two: Deferred<String>): String =
92    one.await() + two.await()
93
94suspend fun computeOne(): String {
95    delay(5000)
96    return "4"
97}
98
99suspend fun computeTwo(): String {
100    delay(5000)
101    return "2"
102}
103
104fun main() = runBlocking {
105    DebugProbes.install()
106    val deferred = async { computeValue() }
107    // Delay for some time
108    delay(1000)
109    // Dump running coroutines
110    DebugProbes.dumpCoroutines()
111    println("\nDumping only deferred")
112    DebugProbes.printJob(deferred)
113}
114```
115
116Printed result will be:
117
118```
119Coroutines dump 2018/11/12 21:44:02
120
121Coroutine "coroutine#2":DeferredCoroutine{Active}@289d1c02, state: SUSPENDED
122	at kotlinx.coroutines.DeferredCoroutine.await$suspendImpl(Builders.common.kt:99)
123	at ExampleKt.combineResults(Example.kt:11)
124	at ExampleKt$computeValue$2.invokeSuspend(Example.kt:7)
125	at ExampleKt$main$1$deferred$1.invokeSuspend(Example.kt:25)
126
127... More coroutines here ...
128
129Dumping only deferred
130"coroutine#2":DeferredCoroutine{Active}, continuation is SUSPENDED at line kotlinx.coroutines.DeferredCoroutine.await$suspendImpl(Builders.common.kt:99)
131    "coroutine#3":DeferredCoroutine{Active}, continuation is SUSPENDED at line ExampleKt.computeOne(Example.kt:14)
132    "coroutine#4":DeferredCoroutine{Active}, continuation is SUSPENDED at line ExampleKt.computeTwo(Example.kt:19)
133```
134
135### Status of the API
136
137API is experimental, and it is not guaranteed it won't be changed (while it is marked as `@ExperimentalCoroutinesApi`).
138Like the rest of experimental API, `DebugProbes` is carefully designed, tested and ready to use in both test and production
139environments. It is marked as experimental to leave us the room to enrich the output data in a potentially backwards incompatible manner
140to further improve diagnostics and debugging experience.
141
142The output format of [DebugProbes] can be changed in the future and it is not recommended to rely on the string representation
143of the dump programmatically.
144
145### Debug agent and Android
146
147Android runtime does not support Instrument API necessary for `kotlinx-coroutines-debug` to function, triggering `java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/ManagementFactory;`,
148and it is not possible to use coroutine debugger along with Android emulator.
149
150<!---
151Make an exception googlable
152java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/ManagementFactory;
153        at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm$ForLegacyVm.resolve(ByteBuddyAgent.java:1055)
154        at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm.resolve(ByteBuddyAgent.java:1038)
155        at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:374)
156        at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:342)
157        at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:328)
158        at kotlinx.coroutines.debug.internal.DebugProbesImpl.install(DebugProbesImpl.kt:39)
159        at kotlinx.coroutines.debug.DebugProbes.install(DebugProbes.kt:49)
160-->
161
162#### Build failures due to duplicate resource files
163
164Building an Android project that depends on `kotlinx-coroutines-debug` (usually introduced by being a transitive
165dependency of `kotlinx-coroutines-test`) may fail with `DuplicateRelativeFileException` for `META-INF/AL2.0`,
166`META-INF/LGPL2.1`, or `win32-x86/attach_hotspot_windows.dll` when trying to merge the Android resource.
167
168The problem is that Android merges the resources of all its dependencies into a single directory and complains about
169conflicts, but:
170* `kotlinx-coroutines-debug` transitively depends on JNA and JNA-platform, both of which include license files in their
171  META-INF directories. Trying to merge these files leads to conflicts, which means that any Android project that
172  depends on JNA and JNA-platform will experience build failures.
173* Additionally, `kotlinx-coroutines-debug` embeds `byte-buddy-agent` and `byte-buddy`, along with their resource files.
174  Then, if the project separately depends on `byte-buddy`, merging the resources of `kotlinx-coroutines-debug` with ones
175  from `byte-buddy` and `byte-buddy-agent` will lead to conflicts as the resource files are duplicated.
176
177One possible workaround for these issues is to add the following to the `android` block in your gradle file for the
178application subproject:
179```groovy
180     packagingOptions {
181         // for JNA and JNA-platform
182         exclude "META-INF/AL2.0"
183         exclude "META-INF/LGPL2.1"
184         // for byte-buddy
185         exclude "META-INF/licenses/ASM"
186         pickFirst "win32-x86-64/attach_hotspot_windows.dll"
187         pickFirst "win32-x86/attach_hotspot_windows.dll"
188     }
189```
190This will cause the resource merge algorithm to exclude the problematic license files altogether and only leave a single
191copy of the files needed for `byte-buddy-agent` to work.
192
193Alternatively, avoid depending on `kotlinx-coroutines-debug`. In particular, if the only reason why this library a
194dependency of your project is that `kotlinx-coroutines-test` in turn depends on it, you may change your dependency on
195`kotlinx.coroutines.test` to exclude `kotlinx-coroutines-debug`. For example, you could replace
196```kotlin
197androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")
198```
199with
200```groovy
201androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version") {
202    exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
203}
204```
205<!---
206Snippets of stacktraces for googling:
207
208org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugAndroidTestJavaResource'.
209        ...
210Caused by: org.gradle.workers.intelrnal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
211        ...
212Caused by: com.android.builder.merge.DuplicateRelativeFileException: More than one file was found with OS independent path 'META-INF/AL2.0'.
213        at com.android.builder.merge.StreamMergeAlgorithms.lambda$acceptOnlyOne$2(StreamMergeAlgorithms.java:85)
214        at com.android.builder.merge.StreamMergeAlgorithms.lambda$select$3(StreamMergeAlgorithms.java:106)
215        at com.android.builder.merge.IncrementalFileMergerOutputs$1.create(IncrementalFileMergerOutputs.java:88)
216        at com.android.builder.merge.DelegateIncrementalFileMergerOutput.create(DelegateIncrementalFileMergerOutput.java:64)
217        at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate$run$output$1.create(MergeJavaResourcesDelegate.kt:230)
218        at com.android.builder.merge.IncrementalFileMerger.updateChangedFile(IncrementalFileMerger.java:242)
219        at com.android.builder.merge.IncrementalFileMerger.mergeChangedInputs(IncrementalFileMerger.java:203)
220        at com.android.builder.merge.IncrementalFileMerger.merge(IncrementalFileMerger.java:80)
221        at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate.run(MergeJavaResourcesDelegate.kt:276)
222        at com.android.build.gradle.internal.tasks.MergeJavaResRunnable.run(MergeJavaResRunnable.kt:81)
223        at com.android.build.gradle.internal.tasks.Workers$ActionFacade.run(Workers.kt:242)
224        at org.gradle.workers.internal.AdapterWorkAction.execute(AdapterWorkAction.java:50)
225        at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:50)
226        at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:63)
227        at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:59)
228        at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:98)
229        at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:59)
230        at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
231        at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
232        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
233        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
234        at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
235        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
236        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
237        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
238        at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
239        at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
240        at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:53)
241        at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$2(DefaultWorkerExecutor.java:200)
242        at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215)
243        at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
244        at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131)
245
246Execution failed for task ':app:mergeStagingDebugAndroidTestJavaResource'.
247Execution failed for task ':app:mergeDebugAndroidTestJavaResource'.
248Execution failed for task ':app:mergeDebugTestJavaResource'
249
250More than one file was found with OS independent path 'META-INF/LGPL2.1'
251More than one file was found with OS independent path 'win32-x86/attach_hotspot_windows.dll'
252More than one file was found with OS independent path 'win32-x86-64/attach_hotspot_windows.dll'
253-->
254<!--- MODULE kotlinx-coroutines-core -->
255<!--- INDEX kotlinx.coroutines -->
256
257[Job]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
258[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
259
260<!--- MODULE kotlinx-coroutines-debug -->
261<!--- INDEX kotlinx.coroutines.debug -->
262
263[DebugProbes]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/index.html
264[DebugProbes.install]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/install.html
265[DebugProbes.dumpCoroutines]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines.html
266[DebugProbes.dumpCoroutinesInfo]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines-info.html
267[DebugProbes.printJob]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/print-job.html
268[DebugProbes.printScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/print-scope.html
269[DebugProbes.enableCreationStackTraces]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/enable-creation-stack-traces.html
270
271<!--- INDEX kotlinx.coroutines.debug.junit4 -->
272
273[CoroutinesTimeout]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug.junit4/-coroutines-timeout/index.html
274
275<!--- END -->
276