Preserve a frame with source code location when sanitizing traces (#2452)

Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/1437
diff --git a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt
index 83bc02c..5ab8a63 100644
--- a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt
+++ b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt
@@ -477,33 +477,40 @@
 
         /*
          * Trim intervals of internal methods from the stacktrace (bounds are excluded from trimming)
-         * E.g. for sequence [e, i1, i2, i3, e, i4, e, i5, i6, e7]
+         * E.g. for sequence [e, i1, i2, i3, e, i4, e, i5, i6, i7]
          * output will be [e, i1, i3, e, i4, e, i5, i7]
+         *
+         * If an interval of internal methods ends in a synthetic method, the outermost non-synthetic method in that
+         * interval will also be included.
          */
         val result = ArrayList<StackTraceElement>(size - probeIndex + 1)
         result += createArtificialFrame(ARTIFICIAL_FRAME_MESSAGE)
-        var includeInternalFrame = true
-        for (i in (probeIndex + 1) until size - 1) {
-            val element = stackTrace[i]
-            if (!element.isInternalMethod) {
-                includeInternalFrame = true
-                result += element
-                continue
-            }
-
-            if (includeInternalFrame) {
-                result += element
-                includeInternalFrame = false
-            } else if (stackTrace[i + 1].isInternalMethod) {
-                continue
+        var i = probeIndex + 1
+        while (i < size) {
+            if (stackTrace[i].isInternalMethod) {
+                result += stackTrace[i] // we include the boundary of the span in any case
+                // first index past the end of the span of internal methods that starts from `i`
+                var j = i + 1
+                while (j < size && stackTrace[j].isInternalMethod) {
+                    ++j
+                }
+                // index of the last non-synthetic internal methods in this span, or `i` if there are no such methods
+                var k = j - 1
+                while (k > i && stackTrace[k].fileName == null) {
+                    k -= 1
+                }
+                if (k > i && k < j - 1) {
+                    /* there are synthetic internal methods at the end of this span, but there is a non-synthetic method
+                    after `i`, so we include it. */
+                    result += stackTrace[k]
+                }
+                result += stackTrace[j - 1] // we include the other boundary of this span in any case, too
+                i = j
             } else {
-                result += element
-                includeInternalFrame = true
+                result += stackTrace[i]
+                ++i
             }
-
         }
-
-        result += stackTrace[size - 1]
         return result
     }
 
diff --git a/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt b/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt
index c897e2e..67a283d 100644
--- a/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt
+++ b/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt
@@ -63,6 +63,7 @@
                     "\t(Coroutine creation stacktrace)\n" +
                     "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" +
                     "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" +
+                    "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" +
                     "\tat kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" +
                     "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.createActiveDeferred(SanitizedProbesTest.kt:62)\n" +
                     "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.access\$createActiveDeferred(SanitizedProbesTest.kt:16)\n" +
@@ -91,6 +92,7 @@
                 "\t(Coroutine creation stacktrace)\n" +
                 "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" +
                 "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:25)\n" +
+                "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.launch\$default(Builders.common.kt)\n" +
                 "\tat kotlinx.coroutines.BuildersKt.launch\$default(Unknown Source)\n" +
                 "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.launchSelector(SanitizedProbesTest.kt:100)\n" +
                 "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.access\$launchSelector(SanitizedProbesTest.kt:16)\n" +