Skip to content

Commit 4ebc130

Browse files
authored
Merge pull request #1023 from Kotlin/debugger-sm-bypass
Improve stacktrace merge heuristic: skip state machine meaningless fr…
2 parents 74b250f + 1ea4da4 commit 4ebc130

File tree

14 files changed

+289
-210
lines changed

14 files changed

+289
-210
lines changed

binary-compatibility-validator/reference-public-api/kotlinx-coroutines-debug.txt

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
public final class kotlinx/coroutines/debug/CoroutineState {
2-
public final fun component1 ()Lkotlin/coroutines/Continuation;
3-
public final fun copy (Lkotlin/coroutines/Continuation;Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;J)Lkotlinx/coroutines/debug/CoroutineState;
4-
public static synthetic fun copy$default (Lkotlinx/coroutines/debug/CoroutineState;Lkotlin/coroutines/Continuation;Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;JILjava/lang/Object;)Lkotlinx/coroutines/debug/CoroutineState;
1+
public final class kotlinx/coroutines/debug/CoroutineInfo {
2+
public final fun component1 ()Lkotlin/coroutines/CoroutineContext;
3+
public final fun copy (Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;J)Lkotlinx/coroutines/debug/CoroutineInfo;
4+
public static synthetic fun copy$default (Lkotlinx/coroutines/debug/CoroutineInfo;Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;JILjava/lang/Object;)Lkotlinx/coroutines/debug/CoroutineInfo;
55
public fun equals (Ljava/lang/Object;)Z
6-
public final fun getContinuation ()Lkotlin/coroutines/Continuation;
6+
public final fun getContext ()Lkotlin/coroutines/CoroutineContext;
77
public final fun getCreationStackTrace ()Ljava/util/List;
8-
public final fun getJobOrNull ()Lkotlinx/coroutines/Job;
8+
public final fun getJob ()Lkotlinx/coroutines/Job;
99
public final fun getState ()Lkotlinx/coroutines/debug/State;
1010
public fun hashCode ()I
1111
public final fun lastObservedStackTrace ()Ljava/util/List;
@@ -16,7 +16,7 @@ public final class kotlinx/coroutines/debug/DebugProbes {
1616
public static final field INSTANCE Lkotlinx/coroutines/debug/DebugProbes;
1717
public final fun dumpCoroutines (Ljava/io/PrintStream;)V
1818
public static synthetic fun dumpCoroutines$default (Lkotlinx/coroutines/debug/DebugProbes;Ljava/io/PrintStream;ILjava/lang/Object;)V
19-
public final fun dumpCoroutinesState ()Ljava/util/List;
19+
public final fun dumpCoroutinesInfo ()Ljava/util/List;
2020
public final fun getSanitizeStackTraces ()Z
2121
public final fun install ()V
2222
public final fun jobToString (Lkotlinx/coroutines/Job;)Ljava/lang/String;

kotlinx-coroutines-core/common/src/Timeout.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ private open class TimeoutCoroutine<U, in T: U>(
8383
@JvmField val uCont: Continuation<U> // unintercepted continuation
8484
) : AbstractCoroutine<T>(uCont.context, active = true), Runnable, Continuation<T>, CoroutineStackFrame {
8585
override val defaultResumeMode: Int get() = MODE_DIRECT
86-
override val callerFrame: CoroutineStackFrame? get() = (uCont as? CoroutineStackFrame)?.callerFrame
87-
override fun getStackTraceElement(): StackTraceElement? = (uCont as? CoroutineStackFrame)?.getStackTraceElement()
86+
override val callerFrame: CoroutineStackFrame? get() = (uCont as? CoroutineStackFrame)
87+
override fun getStackTraceElement(): StackTraceElement? = null
8888

8989
override val cancelsParent: Boolean
9090
get() = false // it throws exception to parent instead of cancelling it

kotlinx-coroutines-debug/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Call to [DebugProbes.install] installs debug agent via ByteBuddy and starts spyi
1010

1111
After that, you can use [DebugProbes.dumpCoroutines] to print all active (suspended or running) coroutines, including their state, creation and
1212
suspension stacktraces.
13-
Additionally, it is possible to process the list of such coroutines via [DebugProbes.dumpCoroutinesState] or dump isolated parts
13+
Additionally, it is possible to process the list of such coroutines via [DebugProbes.dumpCoroutinesInfo] or dump isolated parts
1414
of coroutines hierarchy referenced by a [Job] or [CoroutineScope] instances using [DebugProbes.printJob] and [DebugProbes.printScope] respectively.
1515

1616
### Using in your project
@@ -159,7 +159,7 @@ java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/Mana
159159
[DebugProbes]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/index.html
160160
[DebugProbes.install]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/install.html
161161
[DebugProbes.dumpCoroutines]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines.html
162-
[DebugProbes.dumpCoroutinesState]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines-state.html
162+
[DebugProbes.dumpCoroutinesInfo]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines-info.html
163163
[DebugProbes.printJob]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/print-job.html
164164
[DebugProbes.printScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/print-scope.html
165165
<!--- INDEX kotlinx.coroutines.debug.junit4 -->

kotlinx-coroutines-debug/src/CoroutineState.kt renamed to kotlinx-coroutines-debug/src/CoroutineInfo.kt

+23-22
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import kotlin.coroutines.*
1212
import kotlin.coroutines.jvm.internal.*
1313

1414
/**
15-
* Class describing coroutine state.
15+
* Class describing coroutine info such as its context, state and stacktrace.
1616
*/
1717
@ExperimentalCoroutinesApi
18-
public data class CoroutineState internal constructor(
19-
public val continuation: Continuation<*>,
18+
public data class CoroutineInfo internal constructor(
19+
val context: CoroutineContext,
2020
private val creationStackBottom: CoroutineStackFrame,
2121
@JvmField internal val sequenceNumber: Long
2222
) {
@@ -25,7 +25,7 @@ public data class CoroutineState internal constructor(
2525
* [Job] associated with a current coroutine or null.
2626
* May be later used in [DebugProbes.printJob].
2727
*/
28-
public val jobOrNull: Job? get() = continuation.context[Job]
28+
public val job: Job? get() = context[Job]
2929

3030
/**
3131
* Creation stacktrace of the coroutine.
@@ -42,18 +42,34 @@ public data class CoroutineState internal constructor(
4242
@JvmField
4343
internal var lastObservedThread: Thread? = null
4444

45-
private var lastObservedFrame: CoroutineStackFrame? = null
45+
@JvmField
46+
internal var lastObservedFrame: CoroutineStackFrame? = null
4647

4748
// Copy constructor
48-
internal constructor(coroutine: Continuation<*>, state: CoroutineState) : this(
49-
coroutine,
49+
internal constructor(coroutine: Continuation<*>, state: CoroutineInfo) : this(
50+
coroutine.context,
5051
state.creationStackBottom,
5152
state.sequenceNumber
5253
) {
5354
_state = state.state
5455
this.lastObservedFrame = state.lastObservedFrame
5556
}
5657

58+
/**
59+
* Last observed stacktrace of the coroutine captured on its suspension or resumption point.
60+
* It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and
61+
* reflects stacktrace of the resumption point, not the actual current stacktrace.
62+
*/
63+
public fun lastObservedStackTrace(): List<StackTraceElement> {
64+
var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList()
65+
val result = ArrayList<StackTraceElement>()
66+
while (frame != null) {
67+
frame.getStackTraceElement()?.let { result.add(sanitize(it)) }
68+
frame = frame.callerFrame
69+
}
70+
return result
71+
}
72+
5773
private fun creationStackTrace(): List<StackTraceElement> {
5874
// Skip "Coroutine creation stacktrace" frame
5975
return sequence<StackTraceElement> { yieldFrames(creationStackBottom.callerFrame) }.toList()
@@ -79,21 +95,6 @@ public data class CoroutineState internal constructor(
7995
lastObservedThread = null
8096
}
8197
}
82-
83-
/**
84-
* Last observed stacktrace of the coroutine captured on its suspension or resumption point.
85-
* It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and
86-
* reflects stacktrace of the resumption point, not the actual current stacktrace.
87-
*/
88-
public fun lastObservedStackTrace(): List<StackTraceElement> {
89-
var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList()
90-
val result = ArrayList<StackTraceElement>()
91-
while (frame != null) {
92-
frame.getStackTraceElement()?.let { result.add(sanitize(it)) }
93-
frame = frame.callerFrame
94-
}
95-
return result
96-
}
9798
}
9899

99100
/**

kotlinx-coroutines-debug/src/DebugProbes.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ public object DebugProbes {
9595
printJob(scope.coroutineContext[Job] ?: error("Job is not present in the scope"), out)
9696

9797
/**
98-
* Returns all existing coroutine states.
98+
* Returns all existing coroutines info.
9999
* The resulting collection represents a consistent snapshot of all existing coroutines at the moment of invocation.
100100
*/
101-
public fun dumpCoroutinesState(): List<CoroutineState> = DebugProbesImpl.dumpCoroutinesState()
101+
public fun dumpCoroutinesInfo(): List<CoroutineInfo> = DebugProbesImpl.dumpCoroutinesInfo()
102102

103103
/**
104104
* Dumps all active coroutines into the given output stream, providing a consistent snapshot of all existing coroutines at the moment of invocation.

0 commit comments

Comments
 (0)