SimpleperfListener: Map symbols to easy to read keys

Also divide any reported event count by test iterations.

Bug: 230039226
Test: atest SimpleperfListenerTest

Change-Id: I7f0d5a6fe716b933de95d438dd68104d59c2be5c
diff --git a/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java b/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java
index e6a6f30..5f62982 100644
--- a/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java
+++ b/libraries/collectors-helper/simpleperf/src/com/android/helpers/SimpleperfHelper.java
@@ -180,8 +180,8 @@
     public Map<String /*event-process-symbol*/, String /*eventCount*/> getSimpleperfReport(
             String path,
             Map.Entry<String, String> processToPid,
-            String[] symbols,
-            String metricsPrefix) {
+            Map<String, String> symbols,
+            int testIterations) {
         try {
             String reportCommand =
                     String.format(
@@ -190,7 +190,7 @@
                             path, processToPid.getValue(), SIMPLEPERF_REPORT_TMP_FILE_PATH);
             Log.i(LOG_TAG, String.format("Report command: %s", reportCommand));
             mUiDevice.executeShellCommand(reportCommand);
-            return getMetrics(processToPid.getKey(), symbols, metricsPrefix);
+            return getMetrics(processToPid.getKey(), symbols, testIterations);
         } catch (IOException e) {
             Log.e(LOG_TAG, "Could not generate report: " + e.getMessage());
         }
@@ -202,10 +202,10 @@
      *
      * @param process Individually extracted processes recorded in binary record file.
      * @param symbols Symbols to report events from the processes recorded.
-     * @param metricsPrefix
      * @return Map containing recorded event counts from symbols within process
      */
-    private Map<String, String> getMetrics(String process, String[] symbols, String metricsPrefix) {
+    private Map<String, String> getMetrics(
+            String process, Map<String, String> symbols, int testIterations) {
         Map<String, String> results = new HashMap<>();
         try {
             String eventName = "";
@@ -220,8 +220,9 @@
                     if (splitLine[0].equals("Event")) {
                         eventName = splitLine[1].split(" ")[0];
                     } else if (splitLine[0].equals("Event count")) {
-                        String key = String.join("-", metricsPrefix, process, eventName);
-                        results.put(key, splitLine[1]);
+                        String key = String.join("-", process, eventName);
+                        long count = Long.parseLong(splitLine[1]) / testIterations;
+                        results.put(key, String.valueOf(count));
                     }
                 }
                 // Parsing lines for specific symbols in report to store with event count to results
@@ -234,7 +235,7 @@
                     if (matchedSymbol == null) {
                         continue;
                     }
-                    String key = String.join("-", metricsPrefix, process, matchedSymbol, eventName);
+                    String key = String.join("-", process, matchedSymbol, eventName);
                     if (results.containsKey(key + "-percentage")) {
                         // We are searching for symbols with partial matches so only include the
                         // first hit if we get multiple matches.
@@ -245,7 +246,8 @@
                     String percentage = splitLine[0].substring(0, splitLine[0].length() - 1);
                     results.put(key + "-percentage", percentage);
                     String eventCount = splitLine[2].trim();
-                    results.put(key + "-eventCount", eventCount);
+                    long count = Long.parseLong(eventCount) / testIterations;
+                    results.put(key + "-count", String.valueOf(count));
                 }
             }
         } catch (Exception e) {
@@ -254,10 +256,10 @@
         return results;
     }
 
-    private static String getMatchingSymbol(String[] symbols, String parsedSymbol) {
-        for (String candidate : symbols) {
+    private static String getMatchingSymbol(Map<String, String> symbols, String parsedSymbol) {
+        for (String candidate : symbols.keySet()) {
             if (parsedSymbol.contains(candidate)) {
-                return candidate;
+                return symbols.get(candidate);
             }
         }
         return null;
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/SimpleperfListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/SimpleperfListener.java
index a53d68f..1cbc8a0 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/SimpleperfListener.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/SimpleperfListener.java
@@ -66,10 +66,15 @@
     public static final String EVENTS = "events_to_record";
     // Argument to determine if report is generated after recording.
     public static final String REPORT = "report";
-    // Arguments to report event count for specific symbols separated by semicolons and spaces are
-    // ignored.
-    // Ex. "android::Parcel::writeInt32(int);android::SurfaceFlinger::commit(long, long, long)"
+    // Report events per symbol. Symbols are separated by the key to identify the symbol and the
+    // substring used to search for the symbol.
+    // Ex. "writeInt32;android::Parcel::writeInt32(;commit;android::SurfaceFlinger::commit("
+    // symbols matching the substring "android::Parcel::writeInt32(" will be reported as
+    // "writeInt32" and
+    // symbols matching "android::SurfaceFlinger::commit(" will be reported as "commit"
     public static final String REPORT_SYMBOLS = "symbols_to_report";
+    // Test iterations used to divide any reported event counts.
+    public static final String TEST_ITERATIONS = "test_iterations";
 
     // Simpleperf samples collected during the test will be saved under this root folder.
     private String mTestOutputRoot;
@@ -83,7 +88,8 @@
     private String mArguments;
     private Map<String, String> mProcessToPid = new HashMap<>();
     private boolean mReport;
-    private String[] mReportSymbols;
+    private Map<String, String> mSymbolToMetricKey = new HashMap<>();
+    private int mTestIterations;
 
     private SimpleperfHelper mSimpleperfHelper = new SimpleperfHelper();
 
@@ -134,7 +140,10 @@
         mReport = "true".equals(args.getString(REPORT));
 
         // Symbols to look for when reporting events for processes.
-        mReportSymbols = args.getString(REPORT_SYMBOLS, "").trim().split("\\s*;\\s*");
+        String[] symbolAndMetricKey = args.getString(REPORT_SYMBOLS, "").trim().split("\\s*;\\s*");
+        for (int i = 0; i < symbolAndMetricKey.length - 1; i += 2) {
+            mSymbolToMetricKey.put(symbolAndMetricKey[i + 1], symbolAndMetricKey[i]);
+        }
 
         // Appending recording argument for recording specified events if given.
         if (!events.isEmpty()) {
@@ -149,6 +158,9 @@
             mArguments += " -p " + String.join(",", mProcessToPid.values());
         }
 
+        mTestIterations = Integer.parseInt(args.getString(TEST_ITERATIONS, "1"));
+        Log.i(getTag(), "onTestRunStart arguments mTestIterations=" + mTestIterations);
+
         if (!mIsCollectPerRun) {
             return;
         }
@@ -215,7 +227,7 @@
                                     mTestIdInvocationCount.get(getTestFileName(description))));
             stopSimpleperf(path, testData);
             if (mReport) {
-                getSimpleperfReport(path, testData, description.getMethodName());
+                getSimpleperfReport(path, testData);
             }
         }
     }
@@ -240,7 +252,7 @@
                         String.format("%s%s.data", SIMPLEPERF_PREFIX, uniqueId));
         stopSimpleperf(path, runData);
         if (mReport) {
-            getSimpleperfReport(path, runData, uniqueId);
+            getSimpleperfReport(path, runData);
         }
     }
 
@@ -268,11 +280,11 @@
      * @param path Path to read binary record from.
      * @param data DataRecord to store metrics parsed from report
      */
-    private void getSimpleperfReport(Path path, DataRecord data, String metricsPrefix) {
+    private void getSimpleperfReport(Path path, DataRecord data) {
         for (Map.Entry<String, String> entry : mProcessToPid.entrySet()) {
             Map<String, String> metricPerProcess =
                     mSimpleperfHelper.getSimpleperfReport(
-                            path.toString(), entry, mReportSymbols, metricsPrefix);
+                            path.toString(), entry, mSymbolToMetricKey, mTestIterations);
             Log.i(getTag(), "Simpleperf Metrics report collected. " + metricPerProcess);
             for (Map.Entry<String /*event-process-symbol*/, String /*eventCount*/> metric :
                     metricPerProcess.entrySet()) {
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/SimpleperfListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/SimpleperfListenerTest.java
index 5db60f3..7cd4263 100644
--- a/libraries/device-collectors/src/test/java/android/device/collectors/SimpleperfListenerTest.java
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/SimpleperfListenerTest.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -130,36 +131,31 @@
     }
 
     private void testSampleReport() {
-        Map.Entry<String, String>[] processes =
-                new Map.Entry[] {
-                    Map.entry("surfaceflinger", "680"), Map.entry("system_server", "1696")
-                };
+        Map<String, String> processes =
+                Map.of(
+                        "surfaceflinger", "680",
+                        "system_server", "1696");
 
         Map<String /*key*/, String /*eventCount*/> metrics = new ArrayMap<>();
-        for (Map.Entry<String, String> process : processes) {
+        for (Map.Entry<String, String> process : processes.entrySet()) {
             metrics.putAll(
                     mSimpleperfHelper.getSimpleperfReport(
                             "/data/local/tmp/simpleperf/testdata/simpleperf_record_sample.data",
                             process,
-                            new String[] {
-                                "android::Parcel::writeInt32(int)",
-                                "android::SurfaceFlinger::commit(long, long, long)",
-                                "android::SurfaceFlinger::composite("
-                            },
-                            "metricsPrefix"));
+                            Map.of(
+                                    "android::Parcel::writeInt32(int)",
+                                    "writeInt32",
+                                    "android::SurfaceFlinger::commit(long, long, long)",
+                                    "commit",
+                                    "android::SurfaceFlinger::composite(",
+                                    "composite"),
+                            10));
         }
-        // cherrypick a few metrics to test
-        assertEquals(metrics.get("metricsPrefix-surfaceflinger-instructions"), "1107121607");
-        assertEquals(
-                metrics.get(
-                        "metricsPrefix-surfaceflinger-android::SurfaceFlinger::composite("
-                                + "-cpu-cycles-eventCount"),
-                "20433426");
-        assertEquals(
-                metrics.get(
-                        "metricsPrefix-surfaceflinger-android::Parcel::writeInt32(int)-instructions-percentage"),
-                "0.74");
-        assertEquals(metrics.get("metricsPrefix-system_server-cpu-cycles"), "9080947163");
+        // cherry-pick a few metrics to test
+        assertEquals(metrics.get("surfaceflinger-instructions"), "110712160");
+        assertEquals(metrics.get("surfaceflinger-composite-cpu-cycles-count"), "2043342");
+        assertEquals(metrics.get("surfaceflinger-writeInt32-instructions-percentage"), "0.74");
+        assertEquals(metrics.get("system_server-cpu-cycles"), "908094716");
     }
 
     /*
@@ -351,7 +347,7 @@
         mListener.onTestRunEnd(mListener.createDataRecord(), new Result());
         verify(mSimpleperfHelperVisibleUidevice, times(1)).stopCollecting(anyString());
         verify(mSimpleperfHelperVisibleUidevice, times(2))
-                .getSimpleperfReport(anyString(), any(), any(), any());
+                .getSimpleperfReport(anyString(), any(), any(), anyInt());
         testSampleReport();
     }
 
@@ -367,8 +363,8 @@
         b.putString(SimpleperfListener.REPORT, "true");
         b.putString(
                 SimpleperfListener.REPORT_SYMBOLS,
-                "android::Parcel::writeInt32(int); android::SurfaceFlinger::commit(long, long,"
-                        + " long); android::SurfaceFlinger::composite(long, long)");
+                "writeInt32;android::Parcel::writeInt32(int);commit;android::SurfaceFlinger::commit(long,"
+                    + " long, long);composite;android::SurfaceFlinger::composite(long, long)");
         b.putString(SimpleperfListener.EVENTS, "instructions,cpu-cycles");
         mListener = initListener(b, mSimpleperfHelperVisibleUidevice);
         doReturn("680").when(mUiDevice).executeShellCommand(eq("pidof surfaceflinger"));
@@ -386,7 +382,7 @@
         mListener.onTestEnd(mListener.createDataRecord(), mTest3Desc);
         verify(mSimpleperfHelperVisibleUidevice, times(1)).stopCollecting(anyString());
         verify(mSimpleperfHelperVisibleUidevice, times(2))
-                .getSimpleperfReport(anyString(), any(), any(), any());
+                .getSimpleperfReport(anyString(), any(), any(), anyInt());
         testSampleReport();
     }