Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.atrace.cts;
     18 
     19 import com.android.cts.migration.MigrationHelper;
     20 import com.android.ddmlib.Log;
     21 import com.android.tradefed.build.IBuildInfo;
     22 import com.android.tradefed.device.ITestDevice;
     23 import com.android.tradefed.testtype.DeviceTestCase;
     24 import com.android.tradefed.testtype.IBuildReceiver;
     25 
     26 import java.io.BufferedReader;
     27 import java.io.File;
     28 import java.io.Reader;
     29 import java.io.StringReader;
     30 import java.util.Arrays;
     31 import java.util.HashSet;
     32 import java.util.List;
     33 import java.util.Set;
     34 import java.util.regex.Matcher;
     35 import java.util.regex.Pattern;
     36 
     37 /**
     38  * Test to check that atrace is usable, to enable usage of systrace.
     39  */
     40 public class AtraceHostTest extends DeviceTestCase implements IBuildReceiver {
     41     private static final String TEST_APK = "CtsAtraceTestApp.apk";
     42     private static final String TEST_PKG = "com.android.cts.atracetestapp";
     43 
     44     private interface FtraceEntryCallback {
     45         void onTraceEntry(String threadName, int pid, int tid, String eventType, String args);
     46         void onFinished();
     47     }
     48 
     49     /**
     50      * Helper for parsing ftrace data.
     51      * Regexs copied from (and should be kept in sync with) ftrace importer in catapult.
     52      */
     53     private static class FtraceParser {
     54         // Matches the trace record in 3.2 and later with the print-tgid option:
     55         //          <idle>-0    0 [001] d...  1.23: sched_switch
     56         private static final Pattern sLineWithTgid = Pattern.compile(
     57                 "^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]"
     58                 + "\\s+[dX.][N.][Hhs.][0-9a-f.]"
     59                 + "\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)");
     60 
     61         // Matches the default trace record in 3.2 and later (includes irq-info):
     62         //          <idle>-0     [001] d...  1.23: sched_switch
     63         private static final Pattern sLineWithIrqInfo = Pattern.compile(
     64                 "^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]"
     65                 + "\\s+[dX.][N.][Hhs.][0-9a-f.]"
     66                 + "\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$");
     67 
     68         // Matches the default trace record pre-3.2:
     69         //          <idle>-0     [001]  1.23: sched_switch
     70         private static final Pattern sLineLegacy = Pattern.compile(
     71                 "^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]\\s*(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)");
     72         private static void parseLine(String line, FtraceEntryCallback callback) {
     73             Matcher m = sLineWithTgid.matcher(line);
     74             if (m.matches()) {
     75                 callback.onTraceEntry(
     76                         /*threadname*/ m.group(1),
     77                         /*pid*/ m.group(3).startsWith("-") ? -1 : Integer.parseInt(m.group(3)),
     78                         /*tid*/ Integer.parseInt(m.group(2)),
     79                         /*eventName*/ m.group(6),
     80                         /*details*/ m.group(7));
     81                 return;
     82             }
     83 
     84             m = sLineWithIrqInfo.matcher(line);
     85             if (m.matches()) {
     86                 callback.onTraceEntry(
     87                         /*threadname*/ m.group(1),
     88                         /*pid*/ -1,
     89                         /*tid*/ Integer.parseInt(m.group(2)),
     90                         /*eventName*/ m.group(5),
     91                         /*details*/ m.group(6));
     92                 return;
     93             }
     94 
     95             m = sLineLegacy.matcher(line);
     96             if (m.matches()) {
     97                 callback.onTraceEntry(
     98                         /*threadname*/ m.group(1),
     99                         /*pid*/ -1,
    100                         /*tid*/ Integer.parseInt(m.group(2)),
    101                         /*eventName*/ m.group(5),
    102                         /*details*/ m.group(6));
    103                 return;
    104             }
    105             System.err.println("line doesn't match: " + line);
    106         }
    107 
    108         private static void parse(Reader reader, FtraceEntryCallback callback) throws Exception {
    109             try {
    110                 BufferedReader bufferedReader = new BufferedReader(reader);
    111                 String line;
    112                 while ((line = bufferedReader.readLine()) != null) {
    113                     FtraceParser.parseLine(line, callback);
    114                 }
    115             } finally {
    116                 callback.onFinished();
    117             }
    118         }
    119     }
    120 
    121     private IBuildInfo mCtsBuild;
    122 
    123     /**
    124      * {@inheritDoc}
    125      */
    126     @Override
    127     public void setBuild(IBuildInfo buildInfo) {
    128         mCtsBuild = buildInfo;
    129     }
    130 
    131     // Collection of all userspace tags, and 'sched'
    132     private static final List<String> sRequiredCategoriesList = Arrays.asList(
    133             "sched",
    134             "gfx",
    135             "input",
    136             "view",
    137             "webview",
    138             "wm",
    139             "am",
    140             "sm",
    141             "audio",
    142             "video",
    143             "camera",
    144             "hal",
    145             "app",
    146             "res",
    147             "dalvik",
    148             "rs",
    149             "bionic",
    150             "power"
    151     );
    152 
    153     /**
    154      * Tests that atrace exists and is runnable with no args
    155      */
    156     public void testSimpleRun() throws Exception {
    157         String output = getDevice().executeShellCommand("atrace");
    158         String[] lines = output.split("\\r?\\n");
    159 
    160         // check for expected stdout
    161         assertEquals("capturing trace... done", lines[0]);
    162         assertEquals("TRACE:", lines[1]);
    163 
    164         // commented trace marker starts here
    165         assertEquals("# tracer: nop", lines[2]);
    166     }
    167 
    168     /**
    169      * Tests the output of "atrace --list_categories" to ensure required categories exist.
    170      */
    171     public void testCategories() throws Exception {
    172         String output = getDevice().executeShellCommand("atrace --list_categories");
    173         String[] categories = output.split("\\r?\\n");
    174 
    175         Set<String> requiredCategories = new HashSet<String>(sRequiredCategoriesList);
    176 
    177         for (String category : categories) {
    178             int dashIndex = category.indexOf("-");
    179 
    180             assertTrue(dashIndex > 1); // must match category output format
    181             category = category.substring(0, dashIndex).trim();
    182 
    183             requiredCategories.remove(category);
    184         }
    185 
    186         if (!requiredCategories.isEmpty()) {
    187             for (String missingCategory : requiredCategories) {
    188                 System.err.println("missing category: " + missingCategory);
    189             }
    190             fail("Expected categories missing from atrace");
    191         }
    192     }
    193 
    194     /**
    195      * Tests that atrace captures app launch, including app level tracing
    196      */
    197     public void testTracingContent() throws Exception {
    198         String atraceOutput = null;
    199         try {
    200             // cleanup test apps that might be installed from previous partial test run
    201             getDevice().uninstallPackage(TEST_PKG);
    202 
    203             // install the test app
    204             File testAppFile = MigrationHelper.getTestFile(mCtsBuild, TEST_APK);
    205             String installResult = getDevice().installPackage(testAppFile, false);
    206             assertNull(
    207                     String.format("failed to install atrace test app. Reason: %s", installResult),
    208                     installResult);
    209 
    210             // capture a launch of the app with async tracing
    211             // content traced by 'view' tag tested below, 'sched' used to ensure tgid printed
    212             String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
    213             getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
    214             getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
    215             getDevice().executeShellCommand("am start " + TEST_PKG);
    216             getDevice().executeShellCommand("sleep 1");
    217             atraceOutput = getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
    218         } finally {
    219             assertNotNull("unable to capture atrace output", atraceOutput);
    220             getDevice().uninstallPackage(TEST_PKG);
    221         }
    222 
    223 
    224         // now parse the trace data (see external/chromium-trace/systrace.py)
    225         final String MARKER = "TRACE:";
    226         int dataStart = atraceOutput.indexOf(MARKER);
    227         assertTrue(dataStart >= 0);
    228         String traceData = atraceOutput.substring(dataStart + MARKER.length());
    229 
    230         FtraceEntryCallback callback = new FtraceEntryCallback() {
    231             private int userSpaceMatches = 0;
    232             private int beginMatches = 0;
    233             private int nextSectionIndex = -1;
    234             private int appTid = -1;
    235 
    236 
    237             private final String initialSection = "traceable-app-test-section";
    238             // list of tags expected to be seen on app launch, in order, after the initial.
    239             private final String[] requiredSectionList = {
    240                     "inflate",
    241                     "Choreographer#doFrame",
    242                     "traversal",
    243                     "measure",
    244                     "layout",
    245                     "draw",
    246                     "Record View#draw()"
    247             };
    248 
    249             @Override
    250             public void onTraceEntry(String truncatedThreadName, int pid, int tid,
    251                     String eventName, String details) {
    252                 if (!"tracing_mark_write".equals(eventName)) {
    253                     // not userspace trace, ignore
    254                     return;
    255                 }
    256 
    257                 assertNotNull(truncatedThreadName);
    258                 assertTrue(tid > 0);
    259                 userSpaceMatches++;
    260 
    261                 if (details == null || !details.startsWith("B|")) {
    262                     // not a begin event
    263                     return;
    264                 }
    265                 beginMatches++;
    266 
    267                 if (details.endsWith("|" + initialSection)) {
    268                     // initial section observed, start looking for others in order
    269                     assertEquals(nextSectionIndex, -1);
    270                     nextSectionIndex = 0;
    271                     appTid = tid;
    272                     return;
    273                 }
    274 
    275                 if (nextSectionIndex >= 0
    276                         && tid == appTid
    277                         && nextSectionIndex < requiredSectionList.length
    278                         && details.endsWith("|" + requiredSectionList[nextSectionIndex])) {
    279                     // found next required section in sequence
    280                     nextSectionIndex++;
    281                 }
    282             }
    283 
    284             @Override
    285             public void onFinished() {
    286                 assertTrue("Unable to parse any userspace sections from atrace output",
    287                         userSpaceMatches != 0);
    288                 assertTrue("Unable to parse any section begin events from atrace output",
    289                         beginMatches != 0);
    290                 assertTrue("Unable to parse initial userspace sections from test app",
    291                         nextSectionIndex >= 0);
    292                 assertEquals("Didn't see required list of traced sections, in order",
    293                         requiredSectionList.length, nextSectionIndex);
    294             }
    295         };
    296 
    297         FtraceParser.parse(new StringReader(traceData), callback);
    298     }
    299 }
    300