Add perfetto extractor status and gcs support.

Bug: b/131418996, b/131356191

Test: PerfettoPullerMetricCollectorTest.
Change-Id: I90faf6aad1d99858447dc0ba48a129d6fecdeeb2
diff --git a/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java b/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java
index 5b3af5f..e633c9d 100644
--- a/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java
+++ b/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollector.java
@@ -47,11 +47,15 @@
 
     private static final String LINE_SEPARATOR = "\\r?\\n";
     private static final char KEY_VALUE_SEPARATOR = ':';
+    private static final String EXTRACTOR_STATUS = "trace_extractor_status";
+    private static final String EXTRACTOR_SUCCESS = "1";
+    private static final String EXTRACTOR_FAILURE = "0";
+    private static final String EXTRACTOR_RUNTIME = "trace_extractor_runtime";
 
     @Option(
             name = "perfetto-binary-path",
             description = "Path to the script files used to analyze the trace files.")
-    private List<String> mScriptPaths = new ArrayList<>();
+    private List<File> mScriptFiles = new ArrayList<>();
 
     @Option(
             name = "perfetto-metric-prefix",
@@ -87,9 +91,9 @@
     public void processMetricFile(String key, File metricFile,
             DeviceMetricData data) {
         // Extract the metrics from the trace file.
-        for (String scriptPath : mScriptPaths) {
+        for (File scriptFile : mScriptFiles) {
             List<String> commandArgsList = new ArrayList<String>();
-            commandArgsList.add(scriptPath);
+            commandArgsList.add(scriptFile.getAbsolutePath());
             commandArgsList.add("-trace_file");
             commandArgsList.add(metricFile.getAbsolutePath());
 
@@ -98,12 +102,26 @@
                 commandArgsList.add(Joiner.on(",").join(mProcessNames));
             }
 
+            String traceExtractorStatus = EXTRACTOR_SUCCESS;
+
+            double scriptDuration = 0;
+            double scriptStartTime = System.currentTimeMillis();
             CommandResult cr = runHostCommand(commandArgsList.toArray(new String[commandArgsList
                     .size()]));
+            scriptDuration = System.currentTimeMillis() - scriptStartTime;
+
+            // Update the script duration metrics.
+            Metric.Builder metricDurationBuilder = Metric.newBuilder();
+            metricDurationBuilder.getMeasurementsBuilder().setSingleDouble(scriptDuration);
+            data.addMetric(
+                    String.format("%s_%s", mMetricPrefix, EXTRACTOR_RUNTIME),
+                    metricDurationBuilder.setType(DataType.RAW));
+
             if (CommandStatus.SUCCESS.equals(cr.getStatus())) {
                 String[] metrics = cr.getStdout().split(LINE_SEPARATOR);
                 for (String metric : metrics) {
                     Pair<String, String> kv = splitKeyValue(metric);
+
                     if (kv != null) {
                         Metric.Builder metricBuilder = Metric.newBuilder();
                         metricBuilder.getMeasurementsBuilder().setSingleString(kv.second);
@@ -116,9 +134,16 @@
                 }
                 CLog.i(cr.getStdout());
             } else {
+                traceExtractorStatus = EXTRACTOR_FAILURE;
                 CLog.e("Unable to parse the trace file %s due to %s - Status - %s ",
                         metricFile.getName(), cr.getStderr(), cr.getStatus());
             }
+
+            Metric.Builder metricStatusBuilder = Metric.newBuilder();
+            metricStatusBuilder.getMeasurementsBuilder().setSingleString(traceExtractorStatus);
+            data.addMetric(
+                    String.format("%s_%s", mMetricPrefix, EXTRACTOR_STATUS),
+                    metricStatusBuilder.setType(DataType.RAW));
         }
 
         // Upload and delete the host trace file.
diff --git a/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java b/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java
index 485f295..3aabd79 100644
--- a/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java
+++ b/tests/src/com/android/tradefed/device/metric/PerfettoPullerMetricCollectorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.tradefed.device.metric;
 
+import static org.junit.Assert.assertTrue;
+
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.invoker.IInvocationContext;
@@ -69,7 +71,6 @@
         OptionSetter setter = new OptionSetter(mPerfettoMetricCollector);
         setter.setOptionValue("pull-pattern-keys", "perfettofile");
         HashMap<String, Metric> currentMetrics = new HashMap<>();
-        currentMetrics.put("perfettofile", TfMetricProtoUtil.stringToMetric("/data/trace.pb"));
 
         Mockito.when(mMockDevice.pullFile(Mockito.eq("/data/trace.pb")))
                 .thenReturn(new File("trace"));
@@ -78,8 +79,8 @@
         mPerfettoMetricCollector.testStarted(testDesc);
         mPerfettoMetricCollector.testEnded(testDesc, currentMetrics);
 
-        Mockito.verify(mMockListener)
-                .testLog(Mockito.eq("trace"), Mockito.eq(LogDataType.PB), Mockito.any());
+        assertTrue("Trace duration available but not expected.",
+                currentMetrics.size() == 0);
     }
 
     @Test
@@ -106,6 +107,44 @@
         Mockito.verify(mPerfettoMetricCollector).runHostCommand(Mockito.any());
         Mockito.verify(mMockListener)
                 .testLog(Mockito.eq("trace"), Mockito.eq(LogDataType.PB), Mockito.any());
+        assertTrue("Expected two metrics that includes success status",
+                currentMetrics.get("perfetto_trace_extractor_status").getMeasurements()
+                        .getSingleString().equals("1"));
+        assertTrue("Trace duration metrics not available but expected.",
+                currentMetrics.get("perfetto_trace_extractor_runtime").getMeasurements()
+                        .getSingleDouble() >= 0);
+    }
+
+    @Test
+    public void testScriptFailureStatus() throws Exception {
+
+        OptionSetter setter = new OptionSetter(mPerfettoMetricCollector);
+        setter.setOptionValue("pull-pattern-keys", "perfettofile");
+        setter.setOptionValue("perfetto-binary-path", "trx");
+        HashMap<String, Metric> currentMetrics = new HashMap<>();
+        currentMetrics.put("perfettofile", TfMetricProtoUtil.stringToMetric("/data/trace.pb"));
+        Mockito.when(mMockDevice.pullFile(Mockito.eq("/data/trace.pb")))
+                .thenReturn(new File("trace"));
+
+        TestDescription testDesc = new TestDescription("xyz", "abc");
+        CommandResult cr = new CommandResult();
+        cr.setStatus(CommandStatus.FAILED);
+        cr.setStdout("abc:efg");
+
+        Mockito.doReturn(cr).when(mPerfettoMetricCollector).runHostCommand(Mockito.any());
+
+        mPerfettoMetricCollector.testStarted(testDesc);
+        mPerfettoMetricCollector.testEnded(testDesc, currentMetrics);
+
+        Mockito.verify(mPerfettoMetricCollector).runHostCommand(Mockito.any());
+        Mockito.verify(mMockListener)
+                .testLog(Mockito.eq("trace"), Mockito.eq(LogDataType.PB), Mockito.any());
+        assertTrue("Expected two metrics that includes failure status",
+                currentMetrics.get("perfetto_trace_extractor_status").getMeasurements()
+                        .getSingleString().equals("0"));
+        assertTrue("Trace duration metrics not available but expected.",
+                currentMetrics.get("perfetto_trace_extractor_runtime").getMeasurements()
+                        .getSingleDouble() >= 0);
     }
 
     @Test