Skip to content

Introduce coroutine dump format for better visualisation of structured concurrency #3587

Open
@mvicsokolova

Description

Introduction

Currently kotlinx-coroutines-debug provides the following API for dumping coroutines:

  • DebugProbes.dumpCoroutines prints all active coroutines, including their state, creation and suspension stacktraces. Though the output of this method is similar to jstack , it only presents a flat list of coroutines, that is rather difficult to analyse in case of a large number of coroutines.
  • DebugProbes.dumpCoroutinesInfo provides a list of objects with information about active coroutines.
  • DebugProbes.printJob / DebugProbes.printScope prints isolated parts of coroutines hierarchy referenced by a Job or CoroutineScope instances. Though these methods can not provide suspension stacktrace.

For now, there is no coroutine dump that would group coroutines in a meaningful way visualising relationships of structured concurrency and providing a stack trace. This proposal aims to come up with a more suitable dump format.

Proposed solution:

DebugProbes.dumpCoroutines can provide coroutine dump in JSON format in addition to plain text:

DebugProbes.dumpCoroutines(System.out, CoroutineDumpFormat.JSON)

Pros:

Cons:

  • Currently there are no tools in IDE to visualise this dump format and it is unreadable when it’s just printed to the terminal. Though you can paste it into the scratch file in IDE and collapse/expand objects corresponding to coroutines.

Possible alternatives:

Plain text + indents

We can still print coroutine dump as a plain text, but coroutines may be sorted by their parent and indents will show their relationships.

Example output:

- BlockingCoroutine{Completing}@2a5c959b [BlockingEventLoop@1d80b9da]
    - StandaloneCoroutine{Active}@154d1536, state: SUSPENDED, name: 'root rb 0' [BlockingEventLoop@1d80b9da]
        at kotlinx.coroutines.DelayKt.awaitCancellation(Delay.kt:148)
        at com.intellij.internal.TestCoroutineProgressAction$cancellableBGProgress$1$1$2$2.invokeSuspend(TestCoroutineProgressAction.kt:91)
    - StandaloneCoroutine{Completing}@1b7e4b1f, name: 'root rb 1' [BlockingEventLoop@1d80b9da]
        - StandaloneCoroutine{Active}@56b4c42, state: SUSPENDED, name: 'root rb 1:0' [BlockingEventLoop@1d80b9da]
            at kotlinx.coroutines.DelayKt.awaitCancellation(Delay.kt:148)
            at com.intellij.internal.TestCoroutineProgressAction$cancellableBGProgress$1$1$2$3$1.invokeSuspend(TestCoroutineProgressAction.kt:95)
    - StandaloneCoroutine{Active}@491608fb, state: SUSPENDED, name: 'root rb 2' [BlockingEventLoop@1d80b9da]
        at kotlinx.coroutines.DelayKt.awaitCancellation(Delay.kt:148)
        at com.intellij.internal.TestCoroutineProgressAction$cancellableBGProgress$1$1$2$4.invokeSuspend(TestCoroutineProgressAction.kt:102)
        - StandaloneCoroutine{Active}@2d37afba, state: SUSPENDED, name: 'root rb 2:0' [BlockingEventLoop@1d80b9da]
            at kotlinx.coroutines.DelayKt.awaitCancellation(Delay.kt:148)
            at com.intellij.internal.TestCoroutineProgressAction$cancellableBGProgress$1$1$2$4$1.invokeSuspend(TestCoroutineProgressAction.kt:100)

Pros:

  • A little bit better than just a plain thread dump, though still unreadable in case of a large number of coroutines.

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions