Add Atrace test for trace content

bug:21559370

Change-Id: Ie1d7dfa58c88da3b1c8d5663f9852f12590539b0
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 4035af7..c389833 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -67,6 +67,7 @@
 cts_support_packages := \
     CtsAccelerationTestStubs \
     CtsAppTestStubs \
+    CtsAtraceTestApp \
     CtsCertInstallerApp \
     CtsDeviceAdmin \
     CtsDeviceOpenGl \
diff --git a/hostsidetests/atrace/AtraceTestApp/Android.mk b/hostsidetests/atrace/AtraceTestApp/Android.mk
new file mode 100644
index 0000000..0eb7cfd
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2009 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_PACKAGE_NAME := CtsAtraceTestApp
+
+# sign this app with a different cert than CtsSimpleAppInstallDiffCert
+#LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+#LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..a86f7f0
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+       package="com.android.cts.atracetestapp">
+    <!--
+    A simple app with a tracing section to test that apps tracing signals are
+    emitted by atrace.
+    -->
+    <application>
+        <activity android:name=".AtraceTestAppActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
new file mode 100644
index 0000000..0269d0d
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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.cts.atracetestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Trace;
+
+public class AtraceTestAppActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Trace.beginSection("traceable-app-test-section");
+        super.onCreate(savedInstanceState);
+        Trace.endSection();
+    }
+}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index f7af31c..ee0511d 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -16,27 +16,40 @@
 
 package android.atrace.cts;
 
+import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.ddmlib.Log;
+import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Test to check that atrace is usable, to enable usage of systrace.
  */
-public class AtraceHostTest extends DeviceTestCase {
+public class AtraceHostTest extends DeviceTestCase implements IBuildReceiver {
     private static final String TAG = "AtraceHostTest";
 
-    private ITestDevice mDevice;
+    private static final String TEST_APK = "CtsAtraceTestApp.apk";
+    private static final String TEST_PKG = "com.android.cts.atracetestapp";
 
+    private CtsBuildHelper mCtsBuild;
+
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mDevice = getDevice();
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
     }
 
     // Collection of all userspace tags, and 'sched'
@@ -65,7 +78,7 @@
      * Tests that atrace exists and is runnable with no args
      */
     public void testSimpleRun() throws Exception {
-        String output = mDevice.executeShellCommand("atrace");
+        String output = getDevice().executeShellCommand("atrace");
         String[] lines = output.split("\\r?\\n");
 
         // check for expected stdout
@@ -80,7 +93,7 @@
      * Tests the output of "atrace --list_categories" to ensure required categories exist.
      */
     public void testCategories() throws Exception {
-        String output = mDevice.executeShellCommand("atrace --list_categories");
+        String output = getDevice().executeShellCommand("atrace --list_categories");
         String[] categories = output.split("\\r?\\n");
 
         Set<String> requiredCategories = new HashSet<String>(sRequiredCategoriesList);
@@ -101,4 +114,120 @@
             fail("Expected categories missing from atrace");
         }
     }
+
+
+    private static final String TRACE_MARKER_REGEX =
+            "\\s*(\\S+)-(\\d+)\\s+\\(\\s*(\\d+)\\).*tracing_mark_write:\\s*(B|E)(.*)";
+    /**
+     * Tests that atrace captures app launch, including app level tracing
+     */
+    public void testTracingContent() throws Exception {
+        String atraceOutput = null;
+        try {
+            // cleanup test apps that might be installed from previous partial test run
+            getDevice().uninstallPackage(TEST_PKG);
+
+            // install the test app
+            File testAppFile = mCtsBuild.getTestApp(TEST_APK);
+            String installResult = getDevice().installPackage(testAppFile, false);
+            assertNull(
+                    String.format("failed to install simple app. Reason: %s", installResult),
+                    installResult);
+
+            // capture a launch of the app with async tracing
+            String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
+            getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
+            getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
+            String start = getDevice().executeShellCommand("am start " + TEST_PKG);
+            getDevice().executeShellCommand("sleep 1");
+            atraceOutput = getDevice().executeShellCommand("atrace --async_dump " + atraceArgs);
+        } finally {
+            getDevice().uninstallPackage(TEST_PKG);
+        }
+        assertNotNull(atraceOutput);
+
+
+        // now parse the trace data (see external/chromium-trace/systrace.py)
+        final String MARKER = "TRACE:";
+        int dataStart = atraceOutput.indexOf(MARKER);
+        assertTrue(dataStart >= 0);
+        String traceData = atraceOutput.substring(dataStart + MARKER.length());
+
+        /**
+         * Pattern that matches standard begin/end userspace tracing.
+         *
+         * Groups are:
+         * 1 - truncated thread name
+         * 2 - tid
+         * 3 - pid
+         * 4 - B/E
+         * 5 - ignored, for grouping
+         * 6 - if B, section title, else null
+         */
+        final Pattern beginEndPattern = Pattern.compile(
+                "\\s*(\\S+)-(\\d+)\\s+\\(\\s*(\\d+)\\).*tracing_mark_write:\\s*(B|E)(\\|\\d+\\|(.+))?");
+
+        int appPid = -1;
+        String line;
+
+        // list of tags expected to be seen on app launch, in order.
+        String[] requiredSectionList = {
+                "traceable-app-test-section",
+                "inflate",
+                "performTraversals",
+                "measure",
+                "layout",
+                "draw",
+                "Record View#draw()"
+        };
+        int nextSectionIndex = 0;
+        int matches = 0;
+        try (BufferedReader reader = new BufferedReader(new StringReader(traceData))) {
+            while ((line = reader.readLine()) != null) {
+                Matcher matcher = beginEndPattern.matcher(line);
+                if (matcher.find()) {
+                    matches++;
+
+                    String truncatedThreadName = matcher.group(1);
+                    assertNotNull(truncatedThreadName);
+
+                    int tid = assertInt(matcher.group(2));
+                    assertTrue(tid > 0);
+                    int pid = assertInt(matcher.group(3));
+                    assertTrue(pid > 0);
+
+                    if (TEST_PKG.endsWith(truncatedThreadName)) {
+                        // should be something like "s.aptracetestapp" since beginning may be truncated
+                        if (appPid == -1) {
+                            appPid = pid;
+                        } else {
+                            assertEquals(appPid, pid);
+                        }
+
+                        if ("B".equals(matcher.group(4))) {
+                            String sectionTitle = matcher.group(6);
+                            if (nextSectionIndex < requiredSectionList.length
+                                    && requiredSectionList[nextSectionIndex].equals(sectionTitle)) {
+                                nextSectionIndex++;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        assertTrue("Unable to parse any userspace sections from atrace output",
+                matches != 0);
+        assertEquals("Didn't see required list of traced sections, in order",
+                requiredSectionList.length, nextSectionIndex);
+    }
+
+    private static int assertInt(String input) {
+        try {
+            return Integer.parseInt(input);
+        } catch (NumberFormatException e) {
+            fail("Expected an integer but found \"" + input + "\"");
+            // Won't be hit, above throws AssertException
+            return -1;
+        }
+    }
 }