Skip to content

Improve stacktrace merge heuristic: skip state machine meaningless fr… #1023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
public final class kotlinx/coroutines/debug/CoroutineState {
public final fun component1 ()Lkotlin/coroutines/Continuation;
public final fun copy (Lkotlin/coroutines/Continuation;Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;J)Lkotlinx/coroutines/debug/CoroutineState;
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;
public final class kotlinx/coroutines/debug/CoroutineInfo {
public final fun component1 ()Lkotlin/coroutines/CoroutineContext;
public final fun copy (Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;J)Lkotlinx/coroutines/debug/CoroutineInfo;
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;
public fun equals (Ljava/lang/Object;)Z
public final fun getContinuation ()Lkotlin/coroutines/Continuation;
public final fun getContext ()Lkotlin/coroutines/CoroutineContext;
public final fun getCreationStackTrace ()Ljava/util/List;
public final fun getJobOrNull ()Lkotlinx/coroutines/Job;
public final fun getJob ()Lkotlinx/coroutines/Job;
public final fun getState ()Lkotlinx/coroutines/debug/State;
public fun hashCode ()I
public final fun lastObservedStackTrace ()Ljava/util/List;
Expand All @@ -16,7 +16,7 @@ public final class kotlinx/coroutines/debug/DebugProbes {
public static final field INSTANCE Lkotlinx/coroutines/debug/DebugProbes;
public final fun dumpCoroutines (Ljava/io/PrintStream;)V
public static synthetic fun dumpCoroutines$default (Lkotlinx/coroutines/debug/DebugProbes;Ljava/io/PrintStream;ILjava/lang/Object;)V
public final fun dumpCoroutinesState ()Ljava/util/List;
public final fun dumpCoroutinesInfo ()Ljava/util/List;
public final fun getSanitizeStackTraces ()Z
public final fun install ()V
public final fun jobToString (Lkotlinx/coroutines/Job;)Ljava/lang/String;
Expand Down
4 changes: 2 additions & 2 deletions kotlinx-coroutines-core/common/src/Timeout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ private open class TimeoutCoroutine<U, in T: U>(
@JvmField val uCont: Continuation<U> // unintercepted continuation
) : AbstractCoroutine<T>(uCont.context, active = true), Runnable, Continuation<T>, CoroutineStackFrame {
override val defaultResumeMode: Int get() = MODE_DIRECT
override val callerFrame: CoroutineStackFrame? get() = (uCont as? CoroutineStackFrame)?.callerFrame
override fun getStackTraceElement(): StackTraceElement? = (uCont as? CoroutineStackFrame)?.getStackTraceElement()
override val callerFrame: CoroutineStackFrame? get() = (uCont as? CoroutineStackFrame)
override fun getStackTraceElement(): StackTraceElement? = null

override val cancelsParent: Boolean
get() = false // it throws exception to parent instead of cancelling it
Expand Down
4 changes: 2 additions & 2 deletions kotlinx-coroutines-debug/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Call to [DebugProbes.install] installs debug agent via ByteBuddy and starts spyi

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

### Using in your project
Expand Down Expand Up @@ -159,7 +159,7 @@ java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/Mana
[DebugProbes]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/index.html
[DebugProbes.install]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/install.html
[DebugProbes.dumpCoroutines]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines.html
[DebugProbes.dumpCoroutinesState]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines-state.html
[DebugProbes.dumpCoroutinesInfo]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/dump-coroutines-info.html
[DebugProbes.printJob]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/print-job.html
[DebugProbes.printScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-debug/kotlinx.coroutines.debug/-debug-probes/print-scope.html
<!--- INDEX kotlinx.coroutines.debug.junit4 -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import kotlin.coroutines.*
import kotlin.coroutines.jvm.internal.*

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

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

private var lastObservedFrame: CoroutineStackFrame? = null
@JvmField
internal var lastObservedFrame: CoroutineStackFrame? = null

// Copy constructor
internal constructor(coroutine: Continuation<*>, state: CoroutineState) : this(
coroutine,
internal constructor(coroutine: Continuation<*>, state: CoroutineInfo) : this(
coroutine.context,
state.creationStackBottom,
state.sequenceNumber
) {
_state = state.state
this.lastObservedFrame = state.lastObservedFrame
}

/**
* Last observed stacktrace of the coroutine captured on its suspension or resumption point.
* It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and
* reflects stacktrace of the resumption point, not the actual current stacktrace.
*/
public fun lastObservedStackTrace(): List<StackTraceElement> {
var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList()
val result = ArrayList<StackTraceElement>()
while (frame != null) {
frame.getStackTraceElement()?.let { result.add(sanitize(it)) }
frame = frame.callerFrame
}
return result
}

private fun creationStackTrace(): List<StackTraceElement> {
// Skip "Coroutine creation stacktrace" frame
return sequence<StackTraceElement> { yieldFrames(creationStackBottom.callerFrame) }.toList()
Expand All @@ -79,21 +95,6 @@ public data class CoroutineState internal constructor(
lastObservedThread = null
}
}

/**
* Last observed stacktrace of the coroutine captured on its suspension or resumption point.
* It means that for [running][State.RUNNING] coroutines resulting stacktrace is inaccurate and
* reflects stacktrace of the resumption point, not the actual current stacktrace.
*/
public fun lastObservedStackTrace(): List<StackTraceElement> {
var frame: CoroutineStackFrame? = lastObservedFrame ?: return emptyList()
val result = ArrayList<StackTraceElement>()
while (frame != null) {
frame.getStackTraceElement()?.let { result.add(sanitize(it)) }
frame = frame.callerFrame
}
return result
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions kotlinx-coroutines-debug/src/DebugProbes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ public object DebugProbes {
printJob(scope.coroutineContext[Job] ?: error("Job is not present in the scope"), out)

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

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