@@ -33,9 +33,7 @@ internal object DebugProbesImpl {
33
33
34
34
@Synchronized
35
35
public fun install () {
36
- if (++ installations > 1 ) {
37
- return
38
- }
36
+ if (++ installations > 1 ) return
39
37
40
38
ByteBuddyAgent .install()
41
39
val cl = Class .forName(" kotlin.coroutines.jvm.internal.DebugProbesKt" )
@@ -50,7 +48,7 @@ internal object DebugProbesImpl {
50
48
51
49
@Synchronized
52
50
public fun uninstall () {
53
- if (installations == 0 ) error( " Agent was not installed" )
51
+ check(isInstalled) { " Agent was not installed" }
54
52
if (-- installations != 0 ) return
55
53
56
54
capturedCoroutines.clear()
@@ -66,17 +64,13 @@ internal object DebugProbesImpl {
66
64
67
65
@Synchronized
68
66
public fun hierarchyToString (job : Job ): String {
69
- if (! isInstalled) {
70
- error(" Debug probes are not installed" )
71
- }
72
-
67
+ check(isInstalled) { " Debug probes are not installed" }
73
68
val jobToStack = capturedCoroutines
74
69
.filterKeys { it.delegate.context[Job ] != null }
75
70
.mapKeys { it.key.delegate.context[Job ]!! }
76
-
77
- val sb = StringBuilder ()
78
- job.build(jobToStack, sb, " " )
79
- return sb.toString()
71
+ return buildString {
72
+ job.build(jobToStack, this , " " )
73
+ }
80
74
}
81
75
82
76
private fun Job.build (map : Map <Job , CoroutineState >, builder : StringBuilder , indent : String ) {
@@ -94,57 +88,48 @@ internal object DebugProbesImpl {
94
88
val contState = state.state
95
89
builder.append(" $str , continuation is $contState at line $element \n " )
96
90
}
97
-
98
91
for (child in children) {
99
92
child.build(map, builder, indent + " \t " )
100
93
}
101
94
}
102
95
103
96
@Synchronized
104
97
public fun dumpCoroutinesState (): List <CoroutineState > {
105
- if (! isInstalled) {
106
- error(" Debug probes are not installed" )
107
- }
108
-
98
+ check(isInstalled) { " Debug probes are not installed" }
109
99
return capturedCoroutines.entries.asSequence()
110
100
.map { CoroutineState (it.key.delegate, it.value) }
111
101
.sortedBy { it.sequenceNumber }
112
102
.toList()
113
103
}
114
104
115
105
public fun dumpCoroutines (out : PrintStream ) {
116
- if (! isInstalled) {
117
- error(" Debug probes are not installed" )
118
- }
119
-
120
- // Avoid inference with other out/err invocations
121
- val resultingString = dumpCoroutines()
122
- out .println (resultingString)
106
+ check(isInstalled) { " Debug probes are not installed" }
107
+ // Avoid inference with other out/err invocations by creating a string first
108
+ dumpCoroutines().let { out .println (it) }
123
109
}
124
110
125
111
@Synchronized
126
- private fun dumpCoroutines (): String {
112
+ private fun dumpCoroutines (): String = buildString {
127
113
// Synchronization window can be reduce even more, but no need to do it here
128
- return buildString {
129
- append(" Coroutines dump ${dateFormat.format(System .currentTimeMillis())} " )
130
- capturedCoroutines
131
- .asSequence()
132
- .sortedBy { it.value.sequenceNumber }
133
- .forEach { (key, value) ->
134
- val state = if (value.state == State .RUNNING )
135
- " ${value.state} (Last suspension stacktrace, not an actual stacktrace)"
136
- else value.state.toString()
137
-
138
- append(" \n\n Coroutine $key , state: $state " )
139
- val observedStackTrace = value.lastObservedStackTrace()
140
- if (observedStackTrace.isEmpty()) {
141
- append(" \n\t at ${artificialFrame(ARTIFICIAL_FRAME_MESSAGE )} " )
142
- printStackTrace(value.creationStackTrace)
143
- } else {
144
- printStackTrace(value.lastObservedStackTrace())
145
- }
114
+ append(" Coroutines dump ${dateFormat.format(System .currentTimeMillis())} " )
115
+ capturedCoroutines
116
+ .asSequence()
117
+ .sortedBy { it.value.sequenceNumber }
118
+ .forEach { (key, value) ->
119
+ val state = if (value.state == State .RUNNING )
120
+ " ${value.state} (Last suspension stacktrace, not an actual stacktrace)"
121
+ else
122
+ value.state.toString()
123
+
124
+ append(" \n\n Coroutine $key , state: $state " )
125
+ val observedStackTrace = value.lastObservedStackTrace()
126
+ if (observedStackTrace.isEmpty()) {
127
+ append(" \n\t at ${artificialFrame(ARTIFICIAL_FRAME_MESSAGE )} " )
128
+ printStackTrace(value.creationStackTrace)
129
+ } else {
130
+ printStackTrace(value.lastObservedStackTrace())
146
131
}
147
- }
132
+ }
148
133
}
149
134
150
135
private fun StringBuilder.printStackTrace (frames : List <StackTraceElement >) {
@@ -158,10 +143,7 @@ internal object DebugProbesImpl {
158
143
internal fun probeCoroutineSuspended (frame : Continuation <* >) = updateState(frame, State .SUSPENDED )
159
144
160
145
private fun updateState (frame : Continuation <* >, state : State ) {
161
- if (! isInstalled) {
162
- return
163
- }
164
-
146
+ if (! isInstalled) return
165
147
// Find ArtificialStackFrame of the coroutine
166
148
val owner = frame.owner()
167
149
updateState(owner, frame, state)
@@ -174,28 +156,23 @@ internal object DebugProbesImpl {
174
156
warn(frame, state)
175
157
return
176
158
}
177
-
178
159
coroutineState.updateState(state, frame)
179
160
}
180
161
181
- private fun Continuation <* >.owner (): ArtificialStackFrame <* >? = (this as ? CoroutineStackFrame )?.owner()
162
+ private fun Continuation <* >.owner (): ArtificialStackFrame <* >? =
163
+ (this as ? CoroutineStackFrame )?.owner()
182
164
183
- private tailrec fun CoroutineStackFrame.owner (): ArtificialStackFrame <* >? = if (this is ArtificialStackFrame <* >) this else callerFrame?.owner()
165
+ private tailrec fun CoroutineStackFrame.owner (): ArtificialStackFrame <* >? =
166
+ if (this is ArtificialStackFrame <* >) this else callerFrame?.owner()
184
167
185
168
internal fun <T > probeCoroutineCreated (completion : Continuation <T >): Continuation <T > {
186
- if (! isInstalled) {
187
- return completion
188
- }
189
-
169
+ if (! isInstalled) return completion
190
170
/*
191
171
* If completion already has an owner, it means that we are in scoped coroutine (coroutineScope, withContext etc.),
192
172
* then piggyback on its already existing owner and do not replace completion
193
173
*/
194
174
val owner = completion.owner()
195
- if (owner != null ) {
196
- return completion
197
- }
198
-
175
+ if (owner != null ) return completion
199
176
/*
200
177
* Here we replace completion with a sequence of CoroutineStackFrame objects
201
178
* which represents creation stacktrace, thus making stacktrace recovery mechanism
@@ -208,11 +185,10 @@ internal object DebugProbesImpl {
208
185
override val callerFrame: CoroutineStackFrame ? = acc
209
186
override fun getStackTraceElement (): StackTraceElement = frame
210
187
}
211
- }!!
212
-
213
- val result = ArtificialStackFrame (completion, frame)
214
- storeFrame(result, completion)
215
- return result
188
+ }
189
+ return ArtificialStackFrame (completion, frame!! ).also {
190
+ storeFrame(it, completion)
191
+ }
216
192
}
217
193
218
194
@Synchronized
@@ -227,8 +203,8 @@ internal object DebugProbesImpl {
227
203
228
204
private class ArtificialStackFrame <T >(
229
205
@JvmField val delegate : Continuation <T >,
230
- frame : CoroutineStackFrame ) : Continuation<T> by delegate, CoroutineStackFrame by frame {
231
-
206
+ frame : CoroutineStackFrame
207
+ ) : Continuation<T> by delegate, CoroutineStackFrame by frame {
232
208
override fun resumeWith (result : Result <T >) {
233
209
probeCoroutineCompleted(this )
234
210
delegate.resumeWith(result)
@@ -240,14 +216,7 @@ internal object DebugProbesImpl {
240
216
private fun <T : Throwable > sanitizeStackTrace (throwable : T ): List <StackTraceElement > {
241
217
val stackTrace = throwable.stackTrace
242
218
val size = stackTrace.size
243
-
244
- var probeIndex = - 1
245
- for (i in 0 until size) {
246
- val name = stackTrace[i].className
247
- if (" kotlin.coroutines.jvm.internal.DebugProbesKt" == name) {
248
- probeIndex = i
249
- }
250
- }
219
+ val probeIndex = stackTrace.indexOfLast { it.className == " kotlin.coroutines.jvm.internal.DebugProbesKt" }
251
220
252
221
if (! DebugProbes .sanitizeStackTraces) {
253
222
return List (size - probeIndex) {
0 commit comments