Merge "Poll /sys/kernel/tracing/tracing_on before returning from trace capture start" into androidx-main
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
index 61dd1c6..8e7258a6 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
@@ -299,6 +299,11 @@
         }
         throw IllegalStateException("Failed to stop $runningProcesses")
     }
+
+    @RequiresApi(21)
+    fun pathExists(absoluteFilePath: String): Boolean {
+        return executeCommand("ls $absoluteFilePath").trim() == absoluteFilePath
+    }
 }
 
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
index 5d5e266a..2b0cf5d 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
@@ -114,6 +114,58 @@
             throw perfettoStartupException("Perfetto tracing failed to start.", null)
         }
         Log.i(LOG_TAG, "Perfetto tracing started successfully with pid $perfettoPid.")
+
+        checkTracingOn()
+    }
+
+    /**
+     * Poll for tracing_on to be set to 1.
+     *
+     * This is a good indicator that tracing is actually enabled (including the app atrace tag), and
+     * that content will be captured in the trace buffer
+     */
+    private fun checkTracingOn(): Unit = userspaceTrace("poll tracing_on") {
+        val path: String = when {
+            Shell.pathExists(TRACING_ON_PATH) -> {
+                TRACING_ON_PATH
+            }
+            Shell.pathExists(TRACING_ON_FALLBACK_PATH) -> {
+                TRACING_ON_FALLBACK_PATH
+            }
+            else -> {
+                throw perfettoStartupException(
+                    "Unable to find path to tracing_on (e.g. $TRACING_ON_PATH)",
+                    null
+                )
+            }
+        }
+
+        val pollTracingOnMaxCount = 50
+        val pollTracingOnMs = 100L
+
+        repeat(pollTracingOnMaxCount) {
+            when (val output = Shell.executeCommand("cat $path").trim()) {
+                "0" -> {
+                    userspaceTrace("wait for trace to start (tracing_on == 1)") {
+                        SystemClock.sleep(pollTracingOnMs)
+                    }
+                }
+                "1" -> {
+                    // success!
+                    Log.i(LOG_TAG, "$path = 1, polled $it times, capture fully started")
+                    return@checkTracingOn
+                }
+                else -> {
+                    throw perfettoStartupException(
+                        "Saw unexpected tracing_on contents: $output",
+                        null
+                    )
+                }
+            }
+        }
+
+        val duration = pollTracingOnMs * pollTracingOnMaxCount
+        throw perfettoStartupException("Error: did not detect tracing on after $duration ms", null)
     }
 
     /**
@@ -294,6 +346,10 @@
         private val SUPPORTED_64_ABIS = setOf("arm64-v8a", "x86_64")
         private val SUPPORTED_32_ABIS = setOf("armeabi")
 
+        // potential paths that tracing_on may reside in
+        private const val TRACING_ON_PATH = "/sys/kernel/tracing/tracing_on"
+        private const val TRACING_ON_FALLBACK_PATH = "/sys/kernel/debug/tracing/tracing_on"
+
         @TestOnly
         fun isAbiSupported(): Boolean {
             Log.d(LOG_TAG, "Supported ABIs: ${Build.SUPPORTED_ABIS.joinToString()}")
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
index 4cf3f2b..e9996c5 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
@@ -38,6 +38,7 @@
 import org.junit.runner.RunWith
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
 
 /**
  * Tests for PerfettoCapture
@@ -90,39 +91,43 @@
 
         perfettoCapture.start(listOf(Packages.TEST))
 
-        verifyTraceEnable(true)
+        assertTrue(
+            Trace.isEnabled(),
+            "In-process tracing should be enabled immediately after trace capture is started"
+        )
 
-        // TODO: figure out why this sleep (200ms+) is needed - possibly related to b/194105203
-        Thread.sleep(500)
-
-        // Tracing non-trivial duration for manual debugging/verification
-        trace(CUSTOM_TRACE_SECTION_LABEL_1) { Thread.sleep(20) }
-        trace(CUSTOM_TRACE_SECTION_LABEL_2) { Thread.sleep(20) }
+        /**
+         * Trace section labels, in order
+         *
+         * We trace for non-trivial duration both to enable easier manual debugging, but also to
+         * help clarify problems in front/back trace truncation, with indication of severity.
+         *
+         * We use unique, app tag names to avoid conflicting with other legitimate platform tracing.
+         */
+        val traceSectionLabels = List(20) {
+            "PerfettoCaptureTest_$it".also { label ->
+                trace(label) { Thread.sleep(50) }
+            }
+        }
 
         perfettoCapture.stop(traceFilePath)
 
         val matchingSlices = PerfettoTraceProcessor.querySlices(
             absoluteTracePath = traceFilePath,
-            CUSTOM_TRACE_SECTION_LABEL_1,
-            CUSTOM_TRACE_SECTION_LABEL_2
+            "PerfettoCaptureTest_%"
         )
 
         // Note: this test avoids validating platform-triggered trace sections, to avoid flakes
         // from legitimate (and coincidental) platform use during test.
         assertEquals(
-            listOf(CUSTOM_TRACE_SECTION_LABEL_1, CUSTOM_TRACE_SECTION_LABEL_2),
+            traceSectionLabels,
             matchingSlices.sortedBy { it.ts }.map { it.name }
         )
         matchingSlices
             .forEach {
-                assertTrue(it.dur > 15_000_000) // should be at least 15ms
+                assertTrue(it.dur > 30_000_000) // should be at least 30ms
             }
     }
-
-    companion object {
-        const val CUSTOM_TRACE_SECTION_LABEL_1 = "PerfettoCaptureTest_1"
-        const val CUSTOM_TRACE_SECTION_LABEL_2 = "PerfettoCaptureTest_2"
-    }
 }
 
 fun verifyTraceEnable(enabled: Boolean) {
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
index 68803a4c..8bed227 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
@@ -72,6 +72,8 @@
 
     /**
      * Query a trace for a list of slices - name, timestamp, and duration.
+     *
+     * Note that sliceNames may include wildcard matches, such as `foo%`
      */
     fun querySlices(
         absoluteTracePath: String,
@@ -79,7 +81,7 @@
     ): List {
         val whereClause = sliceNames
             .joinToString(separator = " OR ") {
-                "slice.name = '$it'"
+                "slice.name LIKE \"$it\""
             }
 
         return Slice.parseListFromQueryResult(