blob: d3caa9aee627e4cf8a133b255b9f56b27f0978ce [file] [log] [blame] [view]
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +03001**Table of contents**
2
3<!--- TOC -->
4
5* [Debugging coroutines](#debugging-coroutines)
6* [Debug mode](#debug-mode)
7* [Stacktrace recovery](#stacktrace-recovery)
8 * [Stacktrace recovery machinery](#stacktrace-recovery-machinery)
9* [Debug agent](#debug-agent)
Vsevolod Tolstopyatov4764e982019-02-04 12:39:34 +030010 * [Debug agent and Android](#debug-agent-and-android)
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030011
Roman Elizarov660c2d72020-02-14 13:18:37 +030012<!--- END -->
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030013
14## Debugging coroutines
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030015Debugging asynchronous programs is challenging, because multiple concurrent coroutines are typically working at the same time.
16To help with that, `kotlinx.coroutines` comes with additional features for debugging: debug mode, stacktrace recovery
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030017and debug agent.
18
19## Debug mode
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030020
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030021The first debugging feature of `kotlinx.coroutines` is debug mode.
22It can be enabled either by setting system property [DEBUG_PROPERTY_NAME] or by running Java with enabled assertions (`-ea` flag).
23The latter is helpful to have debug mode enabled by default in unit tests.
24
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030025Debug mode attaches a unique [name][CoroutineName] to every launched coroutine.
26Coroutine name can be seen in a regular Java debugger,
27in a string representation of the coroutine or in the thread name executing named coroutine.
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030028Overhead of this feature is negligible and it can be safely turned on by default to simplify logging and diagnostic.
29
30## Stacktrace recovery
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030031
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030032Stacktrace recovery is another useful feature of debug mode. It is enabled by default in the debug mode,
33but can be separately disabled by setting `kotlinx.coroutines.stacktrace.recovery` system property to `false`.
34
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030035Stacktrace recovery tries to stitch asynchronous exception stacktrace with a stacktrace of the receiver by copying it, providing
Vsevolod Tolstopyatov4764e982019-02-04 12:39:34 +030036not only information where an exception was thrown, but also where it was asynchronously rethrown or caught.
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030037
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030038It is easy to demonstrate with actual stacktraces of the same program that awaits asynchronous operation in `main` function
39(runnable code is [here](../kotlinx-coroutines-debug/test/RecoveryExample.kt)):
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030040
41| Without recovery | With recovery |
42| - | - |
43| ![before](images/before.png "before") | ![after](images/after.png "after") |
44
45The only downside of this approach is losing referential transparency of the exception.
46
Vsevolod Tolstopyatov897f02e2019-08-09 17:32:34 +030047### Stacktrace recovery machinery
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030048
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030049This section explains the inner mechanism of stacktrace recovery and can be skipped.
50
51When an exception is rethrown between coroutines (e.g. through `withContext` or `Deferred.await` boundary), stacktrace recovery
52machinery tries to create a copy of the original exception (with the original exception as the cause), then rewrite stacktrace
53of the copy with coroutine-related stack frames (using [Throwable.setStackTrace](https://docs.oracle.com/javase/9/docs/api/java/lang/Throwable.html#setStackTrace-java.lang.StackTraceElement:A-))
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030054and then throws the resulting exception instead of the original one.
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030055
56Exception copy logic is straightforward:
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030057 1) If the exception class implements [CopyableThrowable], [CopyableThrowable.createCopy] is used.
Vsevolod Tolstopyatov897f02e2019-08-09 17:32:34 +030058 `null` can be returned from `createCopy` to opt-out specific exception from being recovered.
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030059 2) If the exception class has class-specific fields not inherited from Throwable, the exception is not copied.
60 3) Otherwise, one of the public exception's constructor is invoked reflectively with an optional `initCause` call.
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030061
62## Debug agent
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030063
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030064[kotlinx-coroutines-debug](../kotlinx-coroutines-debug) module provides one of the most powerful debug capabilities in `kotlinx.coroutines`.
65
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030066This is a separate module with a JVM agent that keeps track of all alive coroutines, introspects and dumps them similar to thread dump command,
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030067additionally enhancing stacktraces with information where coroutine was created.
68
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030069The full tutorial of how to use debug agent can be found in the corresponding [readme](../kotlinx-coroutines-debug/README.md).
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030070
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030071### Debug agent and Android
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030072
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030073Unfortunately, Android runtime does not support Instrument API necessary for `kotlinx-coroutines-debug` to function, triggering `java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/ManagementFactory;`.
74
Mike Nakhimovichda2cf232019-05-22 09:04:22 -040075Nevertheless, it will be possible to support debug agent on Android as soon as [GradleAspectJ-Android](https://github.com/Archinamon/android-gradle-aspectj) will support android-gradle 3.3
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030076
77<!---
78Make an exception googlable
79java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/ManagementFactory;
80 at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm$ForLegacyVm.resolve(ByteBuddyAgent.java:1055)
81 at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider$ForCurrentVm.resolve(ByteBuddyAgent.java:1038)
82 at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:374)
83 at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:342)
84 at kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:328)
85 at kotlinx.coroutines.debug.internal.DebugProbesImpl.install(DebugProbesImpl.kt:39)
86 at kotlinx.coroutines.debug.DebugProbes.install(DebugProbes.kt:49)
87-->
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030088
89<!--- MODULE kotlinx-coroutines-core -->
90<!--- INDEX kotlinx.coroutines -->
91[DEBUG_PROPERTY_NAME]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-d-e-b-u-g_-p-r-o-p-e-r-t-y_-n-a-m-e.html
92[CoroutineName]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-name/index.html
93[CopyableThrowable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-copyable-throwable/index.html
94[CopyableThrowable.createCopy]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-copyable-throwable/create-copy.html
95<!--- MODULE kotlinx-coroutines-debug -->
96<!--- END -->