@@ -156,7 +156,11 @@ internal object DebugProbesImpl {
156156 // Stable ordering of coroutines by their sequence number
157157 .sortedBy { it.info.sequenceNumber }
158158 // Leave in the dump only the coroutines that were not collected while we were dumping them
159- .mapNotNull { owner -> owner.info.context?.let { context -> create(owner, context) } }
159+ .mapNotNull { owner ->
160+ // Fuse map and filter into one operation to save an inline
161+ if (owner.isFinished()) null
162+ else owner.info.context?.let { context -> create(owner, context) }
163+ }
160164 }
161165
162166 /*
@@ -183,10 +187,27 @@ internal object DebugProbesImpl {
183187 dumpCoroutinesSynchronized(out )
184188 }
185189
190+ /*
191+ * Filters out coroutines that do not call probeCoroutineCompleted,
192+ * are completed, but not yet garbage collected.
193+ *
194+ * Typically, we intercept completion of the coroutine so it invokes "probeCoroutineCompleted",
195+ * but it's not the case for lazy coroutines that get cancelled before start.
196+ */
197+ private fun CoroutineOwner <* >.isFinished (): Boolean {
198+ // Guarded by lock
199+ val job = info.context?.get(Job ) ? : return false
200+ if (! job.isCompleted) return false
201+ capturedCoroutinesMap.remove(this ) // Clean it up by the way
202+ return true
203+ }
204+
186205 private fun dumpCoroutinesSynchronized (out : PrintStream ): Unit = coroutineStateLock.write {
187206 check(isInstalled) { " Debug probes are not installed" }
188207 out .print (" Coroutines dump ${dateFormat.format(System .currentTimeMillis())} " )
189208 capturedCoroutines
209+ .asSequence()
210+ .filter { ! it.isFinished() }
190211 .sortedBy { it.info.sequenceNumber }
191212 .forEach { owner ->
192213 val info = owner.info
0 commit comments