add performance tests to tf.

Change-Id: I6be0ea28b7edf63f49f0af28908891f99050be8b
diff --git a/src/com/android/performance/tests/StartupMetricsTest.java b/src/com/android/performance/tests/StartupMetricsTest.java
index 3c281e4..945e451 100644
--- a/src/com/android/performance/tests/StartupMetricsTest.java
+++ b/src/com/android/performance/tests/StartupMetricsTest.java
@@ -16,13 +16,22 @@
 
 package com.android.performance.tests;
 
-import com.android.ddmlib.Log;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.brillopad.BugreportParser;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+import com.android.tradefed.util.brillopad.item.IItem;
+import com.android.tradefed.util.brillopad.section.MemInfoParser;
+import com.android.tradefed.util.brillopad.section.ProcRankParser;
 
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -32,7 +41,7 @@
  * Tests to gather device metrics from during and immediately after boot
  */
 public class StartupMetricsTest implements IDeviceTest, IRemoteTest {
-    private static final String LOG_TAG = "StartupMetricsTest";
+    public static final String BUGREPORT_LOG_NAME = "bugreport_startup.txt";
 
     ITestDevice mTestDevice = null;
 
@@ -41,10 +50,15 @@
         Assert.assertNotNull(mTestDevice);
 
         executeRebootTest(listener);
+        fetchBugReportMetrics(listener);
+        // TODO: pass build metrics? does it make sense to have it here?
     }
 
     /**
-     * Check how long the device takes to come online and become available after a reboot
+     * Check how long the device takes to come online and become available after
+     * a reboot
+     *
+     * @param listener the {@link ITestInvocationListener} of test results
      */
     void executeRebootTest(ITestInvocationListener listener) throws DeviceNotAvailableException {
         Map<String, String> runMetrics = new HashMap<String, String>();
@@ -57,27 +71,140 @@
 
         long offlineDuration = onlineTime - startTime;
         long unavailDuration = availableTime - startTime;
-        Log.d(LOG_TAG, String.format("Reboot: %d millis until online, %d until available",
-                offlineDuration, unavailDuration));
-        runMetrics.put("offline", Long.toString(offlineDuration));
-        runMetrics.put("unavail", Long.toString(unavailDuration));
+        CLog.d("Reboot: %d millis until online, %d until available",
+                offlineDuration, unavailDuration);
+        runMetrics.put("online", Long.toString(offlineDuration));
+        runMetrics.put("bootcomplete", Long.toString(unavailDuration));
 
-        reportMetrics(listener, "reboot", runMetrics);
+        reportMetrics(listener, "boottime", runMetrics);
+    }
+
+    /**
+     * Fetch proc rank metrics from the bugreport after reboot
+     *
+     * @param listener the {@link ITestInvocationListener} of test results
+     */
+    @SuppressWarnings("unchecked")
+    void fetchBugReportMetrics(ITestInvocationListener listener) {
+        BugreportParser parser = new BugreportParser();
+        ItemList bugreport = null;
+        // Retrieve bugreport
+        InputStreamSource bugSource = mTestDevice.getBugreport();
+        try {
+            listener.testLog(BUGREPORT_LOG_NAME, LogDataType.TEXT, bugSource);
+            bugreport = parser.parse(bugSource);
+        } catch (IOException e) {
+            Assert.fail(String.format("Failed to fetch and parse bugreport for device %s: %s",
+                    mTestDevice.getSerialNumber(), e));
+        } finally {
+            bugSource.cancel();
+        }
+        // Process meminfo information and post it to the dashboard
+        IItem item = bugreport.getFirstItemByType(MemInfoParser.SECTION_NAME);
+        if (item != null) {
+            Map<String, String> memInfoMap = convertMap((GenericMapItem<String, Integer>) item);
+            reportMetrics(listener, "startup-meminfo", memInfoMap);
+        }
+
+        // Process procrank information and post it to the dashboard
+        item = bugreport.getFirstItemByType(ProcRankParser.SECTION_NAME);
+        if (item != null) {
+            Map <String, Map<String, Integer>> procRankMap =
+                (GenericMapItem<String, Map<String, Integer>>) item;
+            parseProcRankMap(listener, procRankMap);
+        }
+    }
+
+    /**
+     * Helper method to convert Map<String, Integer> to Map<String, String>
+     *
+     * @param input the {@link Map} to convert from
+     * @return output the converted {@link Map}
+     */
+    Map<String, String> convertMap(Map<String, Integer> input) {
+        Map<String, String> output = new HashMap<String, String>();
+        for (Map.Entry<String, Integer> entry : input.entrySet()) {
+            output.put(entry.getKey(), entry.getValue().toString());
+        }
+        return output;
     }
 
     /**
      * Report run metrics by creating an empty test run to stick them in
      * <p />
      * Exposed for unit testing
+     *
+     * @param listener the {@link ITestInvocationListener} of test results
+     * @param runName the test name
+     * @param metrics the {@link Map} that contains metrics for the given test
      */
     void reportMetrics(ITestInvocationListener listener, String runName,
             Map<String, String> metrics) {
         // Create an empty testRun to report the parsed runMetrics
-        Log.d(LOG_TAG, String.format("About to report metrics: %s", metrics));
+        CLog.d("About to report metrics: %s", metrics);
         listener.testRunStarted(runName, 0);
         listener.testRunEnded(0, metrics);
     }
 
+    /**
+     * Aggregates the procrank data by the pss, rss, and uss values.
+     *
+     * @param listener the {@link ITestInvocationListener} of test results
+     * @param procRankMap the {@link Map} parsed from brillopad for the procrank
+     *            section
+     */
+    void parseProcRankMap(ITestInvocationListener listener,
+            Map<String, Map<String, Integer>> procRankMap) {
+        // final maps for pss, rss, and uss.
+        Map<String, String> pssOutput = new HashMap<String, String>();
+        Map<String, String> rssOutput = new HashMap<String, String>();
+        Map<String, String> ussOutput = new HashMap<String, String>();
+        // total number of processes.
+        Integer numProcess = 0;
+        // aggregate pss, rss, uss across all processes.
+        Integer pssTotal = 0;
+        Integer rssTotal = 0;
+        Integer ussTotal = 0;
+
+        for (Map.Entry<String, Map<String, Integer>> entry : procRankMap.entrySet()) {
+            // Skip empty processes.
+            if (entry.getKey() == null)
+                continue;
+            if (entry.getKey().length() == 0)
+                continue;
+
+            numProcess++;
+            Map<String, Integer> valueMap = entry.getValue();
+            Integer pss = valueMap.get("Pss");
+            Integer rss = valueMap.get("Rss");
+            Integer uss = valueMap.get("Uss");
+            if (pss != null) {
+              pssTotal += pss;
+              pssOutput.put(entry.getKey(), pss.toString());
+            }
+            if (rss != null) {
+                rssTotal += rss;
+                rssOutput.put(entry.getKey(), rss.toString());
+            }
+            if (uss != null) {
+                ussTotal += pss;
+                ussOutput.put(entry.getKey(), uss.toString());
+            }
+        }
+        // Add aggregation data.
+        pssOutput.put("count", numProcess.toString());
+        pssOutput.put("total", pssTotal.toString());
+        rssOutput.put("count", numProcess.toString());
+        rssOutput.put("total", rssTotal.toString());
+        ussOutput.put("count", numProcess.toString());
+        ussOutput.put("total", ussTotal.toString());
+
+        // Report metrics to dashboard
+        reportMetrics(listener, "startup-procrank-pss", pssOutput);
+        reportMetrics(listener, "startup-procrank-rss", rssOutput);
+        reportMetrics(listener, "startup-procrank-uss", ussOutput);
+    }
+
     @Override
     public void setDevice(ITestDevice device) {
         mTestDevice = device;
@@ -88,4 +215,3 @@
         return mTestDevice;
     }
 }
-