Merge "Enable perfetto capture testing back to API 21/23" into androidx-main
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
index cfa0050..17134b9 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
@@ -33,7 +33,7 @@
import kotlin.test.assertTrue
@LargeTest
-@SdkSuppress(minSdkVersion = 28)
+@SdkSuppress(minSdkVersion = 21)
@RunWith(AndroidJUnit4::class)
class PerfettoHelperTest {
@Before
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 83b1cb1..1e3b309 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
@@ -36,6 +36,26 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object Shell {
+ /**
+ * Returns true if the line from ps output contains the given process/package name.
+ *
+ * NOTE: On API 25 and earlier, the processName of unbundled executables will include the
+ * relative path they were invoked from:
+ *
+ * ```
+ * root 10065 10061 14848 3932 poll_sched 7bcaf1fc8c S /data/local/tmp/tracebox
+ * root 10109 1 11552 1140 poll_sched 78c86eac8c S ./tracebox
+ * ```
+ *
+ * On higher API levels, the process name will simply be e.g. "tracebox".
+ *
+ * As this function is also used for package names (which never have a leading `/`), we
+ * simply check for either.
+ */
+ private fun psLineContainsProcess(psOutputLine: String, processName: String): Boolean {
+ return psOutputLine.endsWith(" $processName") || psOutputLine.endsWith("/$processName")
+ }
+
fun connectUiAutomation() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ShellImpl // force initialization
@@ -213,7 +233,7 @@
return executeScript("ps | grep $processName")
.split(Regex("\r?\n"))
.map { it.trim() }
- .filter { it.endsWith(" $processName") }
+ .filter { psLineContainsProcess(psOutputLine = it, processName = processName) }
.map {
// map to int - split, and take 2nd column (PID)
it.split(Regex("\\s+"))[1]
@@ -230,7 +250,7 @@
fun isProcessAlive(pid: Int, processName: String): Boolean {
return executeCommand("ps $pid")
.split(Regex("\r?\n"))
- .any { it.trim().endsWith(" $processName") }
+ .any { psLineContainsProcess(psOutputLine = it, processName = processName) }
}
@RequiresApi(21)
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 21fb7ce..5d5e266a 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
@@ -160,7 +160,7 @@
*
* @return true if perfetto is stopped successfully.
*/
- public fun stopPerfetto() {
+ private fun stopPerfetto() {
val pid = perfettoPid
require(pid != null)
@@ -187,7 +187,7 @@
// Unbundled perfetto can read configuration from a file that it has permissions to
// read from. This because it assumes the identity of the shell and therefore has
// access to /data/local/tmp directory.
- "$UNBUNDLED_ENV_PREFIX $unbundledPerfettoShellPath --background" +
+ "$unbundledPerfettoShellPath --background" +
" -c $configFilePath" +
" -o $outputPath"
}
@@ -290,13 +290,6 @@
private const val UNBUNDLED_TEMP_OUTPUT_FILE =
"$UNBUNDLED_PERFETTO_ROOT_DIR/trace_output.pb"
- // The environment variables necessary for unbundled perfetto (unnamed domain sockets).
- // We need unnamed sockets here because SELinux dictates that we cannot use real, file
- // based, domain sockets on Platform versions prior to S.
- private const val UNBUNDLED_ENV_PREFIX =
- "PERFETTO_PRODUCER_SOCK_NAME=@macrobenchmark_producer " +
- "PERFETTO_CONSUMER_SOCK_NAME=@macrobenchmark_consumer"
-
// A set of supported ABIs
private val SUPPORTED_64_ABIS = setOf("arm64-v8a", "x86_64")
private val SUPPORTED_32_ABIS = setOf("armeabi")
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 32bdfaa..4cf3f2b 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
@@ -16,7 +16,6 @@
package androidx.benchmark.macro.perfetto
-import android.graphics.Bitmap
import androidx.benchmark.macro.FileLinkingRule
import androidx.benchmark.macro.Packages
import androidx.benchmark.perfetto.PerfettoCapture
@@ -37,7 +36,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@@ -47,7 +45,7 @@
* Note: this test is defined in benchmark-macro instead of benchmark-common so that it can
* validate trace contents with PerfettoTraceProcessor
*/
-@SdkSuppress(minSdkVersion = 28) // Lowering blocked by b/131359446
+@SdkSuppress(minSdkVersion = 23)
@RunWith(AndroidJUnit4::class)
class PerfettoCaptureTest {
@get:Rule
@@ -60,7 +58,7 @@
}
@SdkSuppress(
- minSdkVersion = 28, // must redeclare, or minSdkVersion from this annotation (unset) wins
+ minSdkVersion = 21,
maxSdkVersion = LOWEST_BUNDLED_VERSION_SUPPORTED - 1
)
@SmallTest
@@ -82,18 +80,6 @@
@Test
fun captureAndValidateTrace_unbundled() = captureAndValidateTrace(unbundled = true)
- /** Trigger tracing that doesn't require app tracing tag enabled */
- private fun triggerBitmapTraceSection() {
- val outFile = File.createTempFile("tempJpg", "jpg")
- try {
- Bitmap
- .createBitmap(100, 100, Bitmap.Config.ARGB_8888)
- .compress(Bitmap.CompressFormat.JPEG, 100, outFile.outputStream())
- } finally {
- outFile.delete()
- }
- }
-
private fun captureAndValidateTrace(unbundled: Boolean) {
assumeTrue(isAbiSupported())
@@ -109,36 +95,33 @@
// TODO: figure out why this sleep (200ms+) is needed - possibly related to b/194105203
Thread.sleep(500)
- triggerBitmapTraceSection()
- trace(CUSTOM_TRACE_SECTION_LABEL) {
- // Tracing non-trivial duration for manual debugging/verification
- Thread.sleep(20)
- }
+ // 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) }
perfettoCapture.stop(traceFilePath)
val matchingSlices = PerfettoTraceProcessor.querySlices(
absoluteTracePath = traceFilePath,
- CUSTOM_TRACE_SECTION_LABEL,
- BITMAP_TRACE_SECTION_LABEL
+ CUSTOM_TRACE_SECTION_LABEL_1,
+ CUSTOM_TRACE_SECTION_LABEL_2
)
- // We trigger and verify both bitmap trace section (res-tag), and then custom trace
- // section (app-tag) which makes it easier to identify when app-tag-specific issues arise
+ // Note: this test avoids validating platform-triggered trace sections, to avoid flakes
+ // from legitimate (and coincidental) platform use during test.
assertEquals(
- listOf(BITMAP_TRACE_SECTION_LABEL, CUSTOM_TRACE_SECTION_LABEL),
+ listOf(CUSTOM_TRACE_SECTION_LABEL_1, CUSTOM_TRACE_SECTION_LABEL_2),
matchingSlices.sortedBy { it.ts }.map { it.name }
)
matchingSlices
- .single { it.name == CUSTOM_TRACE_SECTION_LABEL }
- .apply {
- assertTrue(dur > 15_000_000) // should be at least 15ms
+ .forEach {
+ assertTrue(it.dur > 15_000_000) // should be at least 15ms
}
}
companion object {
- const val CUSTOM_TRACE_SECTION_LABEL = "PerfettoCaptureTest"
- const val BITMAP_TRACE_SECTION_LABEL = "Bitmap.compress"
+ const val CUSTOM_TRACE_SECTION_LABEL_1 = "PerfettoCaptureTest_1"
+ const val CUSTOM_TRACE_SECTION_LABEL_2 = "PerfettoCaptureTest_2"
}
}