blob: fc8570126d7e37457c387f8aacf611f5ce4a19ec [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
12<!--- END_TOC -->
13
14
15## Debugging coroutines
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030016Debugging asynchronous programs is challenging, because multiple concurrent coroutines are typically working at the same time.
17To help with that, `kotlinx.coroutines` comes with additional features for debugging: debug mode, stacktrace recovery
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030018and debug agent.
19
20## Debug mode
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030021
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030022The first debugging feature of `kotlinx.coroutines` is debug mode.
23It can be enabled either by setting system property [DEBUG_PROPERTY_NAME] or by running Java with enabled assertions (`-ea` flag).
24The latter is helpful to have debug mode enabled by default in unit tests.
25
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030026Debug mode attaches a unique [name][CoroutineName] to every launched coroutine.
27Coroutine name can be seen in a regular Java debugger,
28in a string representation of the coroutine or in the thread name executing named coroutine.
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030029Overhead of this feature is negligible and it can be safely turned on by default to simplify logging and diagnostic.
30
31## Stacktrace recovery
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030032
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030033Stacktrace recovery is another useful feature of debug mode. It is enabled by default in the debug mode,
34but can be separately disabled by setting `kotlinx.coroutines.stacktrace.recovery` system property to `false`.
35
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030036Stacktrace recovery tries to stitch asynchronous exception stacktrace with a stacktrace of the receiver by copying it, providing
Vsevolod Tolstopyatov4764e982019-02-04 12:39:34 +030037not only information where an exception was thrown, but also where it was asynchronously rethrown or caught.
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030038
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030039It is easy to demonstrate with actual stacktraces of the same program that awaits asynchronous operation in `main` function
40(runnable code is [here](../kotlinx-coroutines-debug/test/RecoveryExample.kt)):
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030041
42| Without recovery | With recovery |
43| - | - |
44| ![before](images/before.png "before") | ![after](images/after.png "after") |
45
46The only downside of this approach is losing referential transparency of the exception.
47
48### Stacktrace recovery machinery
Vsevolod Tolstopyatova68376c2019-02-01 18:13:39 +030049
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030050This section explains the inner mechanism of stacktrace recovery and can be skipped.
51
52When an exception is rethrown between coroutines (e.g. through `withContext` or `Deferred.await` boundary), stacktrace recovery
53machinery tries to create a copy of the original exception (with the original exception as the cause), then rewrite stacktrace
54of 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 +030055and then throws the resulting exception instead of the original one.
Vsevolod Tolstopyatov418ba802019-02-01 14:54:06 +030056
57Exception copy logic is straightforward:
Vsevolod Tolstopyatovf5288982019-02-18 14:46:58 +030058 1) If the exception class implements [CopyableThrowable], [CopyableThrowable.createCopy] is used.
59 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 -->