Create LyricCpuUtilizationCollector for collecting Lyric CPU utilization metrics.
Added test in ag/14494388
Bug: 159682839
Test: atest
Test: forrest sponge2/6aa83458-2424-498b-90d7-27747a0c6268
Change-Id: Icfd85a25f1ab70fe3826ca98ce8391c2980c2668
diff --git a/libraries/collectors-helper/lyric/Android.bp b/libraries/collectors-helper/lyric/Android.bp
new file mode 100644
index 0000000..811caca
--- /dev/null
+++ b/libraries/collectors-helper/lyric/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Used for collecting Lyric specific metrics..
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "lyric-metric-helper",
+ defaults: ["tradefed_errorprone_defaults"],
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.runner",
+ "androidx.test.uiautomator",
+ "collector-helper-utilities",
+ "guava",
+ ],
+
+ sdk_version: "current",
+}
diff --git a/libraries/collectors-helper/lyric/src/com/android/helpers/LyricCpuUtilizationHelper.java b/libraries/collectors-helper/lyric/src/com/android/helpers/LyricCpuUtilizationHelper.java
new file mode 100644
index 0000000..3fadc06
--- /dev/null
+++ b/libraries/collectors-helper/lyric/src/com/android/helpers/LyricCpuUtilizationHelper.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.helpers;
+
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This is a collector helper that collects the dumpsys output for specified services and puts them
+ * into files.
+ */
+public class LyricCpuUtilizationHelper implements ICollectorHelper<Double> {
+
+ private static final String TAG = LyricCpuUtilizationHelper.class.getSimpleName();
+
+ private static final String DUMPSYS_CMD = "dumpsys media.camera";
+
+ private static final Pattern TIME_REGEX_PATTERN = Pattern.compile("(\\d+\\.?\\d*)(ms|us|)");
+
+ private static final String TIME_REGEX = "(\\d+\\.?\\d*)(?:ms|us|)";
+
+ private static final Pattern CPU_USAGE_PATTERN =
+ Pattern.compile(
+ String.format(
+ "CPU Usage during ProcessInput for \\[(?:>|\\s)] p\\d+ (.*) after"
+ + " (\\d+) invocations - User: %s \\(Max: %s Min:%s\\) System: %s"
+ + " \\(Max: %s Min:%s\\) Wall: %s \\(Max: %s Min:%s\\)",
+ TIME_REGEX,
+ TIME_REGEX,
+ TIME_REGEX,
+ TIME_REGEX,
+ TIME_REGEX,
+ TIME_REGEX,
+ TIME_REGEX,
+ TIME_REGEX,
+ TIME_REGEX));
+
+ private static final String METRIC_KEY = "cpu_util_%s_%s";
+
+ private UiDevice mUiDevice;
+
+ @Override
+ public boolean startCollecting() {
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ return true;
+ }
+
+ @Override
+ public Map<String, Double> getMetrics() {
+ Map<String, Double> metrics = new HashMap<>();
+ try {
+ String res = mUiDevice.executeShellCommand(DUMPSYS_CMD);
+ BufferedReader bufReader = new BufferedReader(new StringReader(res));
+ String line = bufReader.readLine();
+ while (line != null) {
+ metrics.putAll(processLine(line));
+ line = bufReader.readLine();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to collect Lyric CPU metrics.");
+ }
+ return metrics;
+ }
+
+ @Override
+ public boolean stopCollecting() {
+ return true;
+ }
+
+ @VisibleForTesting
+ static Map<String, Double> processLine(String line) {
+ Matcher matcher = CPU_USAGE_PATTERN.matcher(line);
+ Map<String, Double> metrics = new HashMap<>();
+ if (!matcher.find()) {
+ return metrics;
+ }
+ String node = matcher.group(1).replace(":", "-");
+ metrics.put(
+ String.format(METRIC_KEY, node, "number_of_invocations"),
+ Double.parseDouble(matcher.group(2)));
+ metrics.put(String.format(METRIC_KEY, node, "user_time"), parseTime(matcher.group(3)));
+ metrics.put(String.format(METRIC_KEY, node, "user_time_max"), parseTime(matcher.group(4)));
+ metrics.put(String.format(METRIC_KEY, node, "user_time_min"), parseTime(matcher.group(5)));
+ metrics.put(String.format(METRIC_KEY, node, "system_time"), parseTime(matcher.group(6)));
+ metrics.put(
+ String.format(METRIC_KEY, node, "system_time_max"), parseTime(matcher.group(7)));
+ metrics.put(
+ String.format(METRIC_KEY, node, "system_time_min"), parseTime(matcher.group(8)));
+ metrics.put(String.format(METRIC_KEY, node, "wall_time"), parseTime(matcher.group(9)));
+ metrics.put(String.format(METRIC_KEY, node, "wall_time_max"), parseTime(matcher.group(10)));
+ metrics.put(String.format(METRIC_KEY, node, "wall_time_min"), parseTime(matcher.group(11)));
+
+ return metrics;
+ }
+
+ private static Double parseTime(String timeString) {
+ Matcher matcher = TIME_REGEX_PATTERN.matcher(timeString);
+ if (!matcher.find()) {
+ throw new IllegalArgumentException("Time string does not match the expected format.");
+ }
+ double value = Double.parseDouble(matcher.group(1));
+ String unit = matcher.group(2);
+ if (unit.equals("us")) {
+ value /= 1000;
+ }
+ return value;
+ }
+}
diff --git a/libraries/collectors-helper/lyric/test/Android.bp b/libraries/collectors-helper/lyric/test/Android.bp
new file mode 100644
index 0000000..4e604b7
--- /dev/null
+++ b/libraries/collectors-helper/lyric/test/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "lyric-helper-test",
+ defaults: ["tradefed_errorprone_defaults"],
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.test.runner",
+ "junit",
+ "mockito-target",
+ "lyric-metric-helper",
+ ],
+
+ sdk_version: "current",
+}
diff --git a/libraries/collectors-helper/lyric/test/src/com/android/helpers/LyricCpuUtilizationHelperTest.java b/libraries/collectors-helper/lyric/test/src/com/android/helpers/LyricCpuUtilizationHelperTest.java
new file mode 100644
index 0000000..0a0bc91
--- /dev/null
+++ b/libraries/collectors-helper/lyric/test/src/com/android/helpers/LyricCpuUtilizationHelperTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.helpers;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+/**
+ * Android unit test for {@link LyricCpuUtilizationHelper}
+ *
+ * <p>To run: atest CollectorsHelperTest:com.android.helpers.tests.LyricCpuUtilizationHelper
+ */
+@RunWith(AndroidJUnit4.class)
+public class LyricCpuUtilizationHelperTest {
+
+ private static final String METRIC_KEY = "cpu_util_%s_%s";
+
+ @Test
+ public void testProcessLine() {
+ String testString =
+ "CPU Usage during ProcessInput for [>] p2 cam2_retiming:empty_group after 593"
+ + " invocations - User: 1.709118ms (Max: 3.67ms Min:0) System: 425.17025us"
+ + " (Max: 3.372ms Min:0) Wall: 50.14003675ms (Max: 55.676595ms"
+ + " Min:50.046468ms)";
+
+ Map<String, Double> metrics = LyricCpuUtilizationHelper.processLine(testString);
+
+ String node = "cam2_retiming-empty_group";
+ assertEquals(
+ Double.valueOf(593),
+ metrics.get(String.format(METRIC_KEY, node, "number_of_invocations")));
+ assertEquals(
+ Double.valueOf(1.709118),
+ metrics.get(String.format(METRIC_KEY, node, "user_time")));
+ assertEquals(
+ Double.valueOf(3.67),
+ metrics.get(String.format(METRIC_KEY, node, "user_time_max")));
+ assertEquals(
+ Double.valueOf(0), metrics.get(String.format(METRIC_KEY, node, "user_time_min")));
+ assertEquals(
+ Double.valueOf(425.17025),
+ metrics.get(String.format(METRIC_KEY, node, "system_time")));
+ assertEquals(
+ Double.valueOf(3.372),
+ metrics.get(String.format(METRIC_KEY, node, "system_time_max")));
+ assertEquals(
+ Double.valueOf(0), metrics.get(String.format(METRIC_KEY, node, "system_time_min")));
+ assertEquals(
+ Double.valueOf(50.14003675),
+ metrics.get(String.format(METRIC_KEY, node, "wall_time")));
+ assertEquals(
+ Double.valueOf(55.676595),
+ metrics.get(String.format(METRIC_KEY, node, "wall_time_max")));
+ assertEquals(
+ Double.valueOf(50.046468),
+ metrics.get(String.format(METRIC_KEY, node, "wall_time_min")));
+ }
+}
diff --git a/libraries/device-collectors/src/main/Android.bp b/libraries/device-collectors/src/main/Android.bp
index 33cd7ba..f9bee71 100644
--- a/libraries/device-collectors/src/main/Android.bp
+++ b/libraries/device-collectors/src/main/Android.bp
@@ -28,6 +28,7 @@
"androidx.test.uiautomator",
"jank-helper",
"junit",
+ "lyric-metric-helper",
"memory-helper",
"perfetto-helper",
"power-helper",
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/LyricCpuUtilizationCollector.java b/libraries/device-collectors/src/main/java/android/device/collectors/LyricCpuUtilizationCollector.java
new file mode 100644
index 0000000..b9719a0
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/LyricCpuUtilizationCollector.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+
+import com.android.helpers.LyricCpuUtilizationHelper;
+
+@OptionClass(alias = "lyric-cpu-utilization-collector")
+public class LyricCpuUtilizationCollector extends BaseCollectionListener<Double> {
+
+ public LyricCpuUtilizationCollector() {
+ createHelperInstance(new LyricCpuUtilizationHelper());
+ }
+}