blob: 760723e991fa7dc19d240fd2ba23ee958a42d252 [file] [log] [blame]
/*
* 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 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.Reader;
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 implements IBuildReceiver {
private static final String TEST_APK = "CtsAtraceTestApp.apk";
private static final String TEST_PKG = "com.android.cts.atracetestapp";
private interface FtraceEntryCallback {
void onTraceEntry(String threadName, int pid, int tid, String eventType, String args);
void onFinished();
}
/**
* Helper for parsing ftrace data.
* Regexs copied from (and should be kept in sync with) ftrace importer in catapult.
*/
private static class FtraceParser {
// Matches the trace record in 3.2 and later with the print-tgid option:
// <idle>-0 0 [001] d... 1.23: sched_switch
private static final Pattern sLineWithTgid = Pattern.compile(
"^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]"
+ "\\s+[dX.][N.][Hhs.][0-9a-f.]"
+ "\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)");
// Matches the default trace record in 3.2 and later (includes irq-info):
// <idle>-0 [001] d... 1.23: sched_switch
private static final Pattern sLineWithIrqInfo = Pattern.compile(
"^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]"
+ "\\s+[dX.][N.][Hhs.][0-9a-f.]"
+ "\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$");
// Matches the default trace record pre-3.2:
// <idle>-0 [001] 1.23: sched_switch
private static final Pattern sLineLegacy = Pattern.compile(
"^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]\\s*(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)");
private static void parseLine(String line, FtraceEntryCallback callback) {
Matcher m = sLineWithTgid.matcher(line);
if (m.matches()) {
callback.onTraceEntry(
/*threadname*/ m.group(1),
/*pid*/ m.group(3).startsWith("-") ? -1 : Integer.parseInt(m.group(3)),
/*tid*/ Integer.parseInt(m.group(2)),
/*eventName*/ m.group(6),
/*details*/ m.group(7));
return;
}
m = sLineWithIrqInfo.matcher(line);
if (m.matches()) {
callback.onTraceEntry(
/*threadname*/ m.group(1),
/*pid*/ -1,
/*tid*/ Integer.parseInt(m.group(2)),
/*eventName*/ m.group(5),
/*details*/ m.group(6));
return;
}
m = sLineLegacy.matcher(line);
if (m.matches()) {
callback.onTraceEntry(
/*threadname*/ m.group(1),
/*pid*/ -1,
/*tid*/ Integer.parseInt(m.group(2)),
/*eventName*/ m.group(5),
/*details*/ m.group(6));
return;
}
System.err.println("line doesn't match: " + line);
}
private static void parse(Reader reader, FtraceEntryCallback callback) throws Exception {
try {
BufferedReader bufferedReader = new BufferedReader(reader);
String line;
while ((line = bufferedReader.readLine()) != null) {
FtraceParser.parseLine(line, callback);
}
} finally {
callback.onFinished();
}
}
}
private CtsBuildHelper mCtsBuild;
/**
* {@inheritDoc}
*/
@Override
public void setBuild(IBuildInfo buildInfo) {
mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
}
// Collection of all userspace tags, and 'sched'
private static final List<String> sRequiredCategoriesList = Arrays.asList(
"sched",
"gfx",
"input",
"view",
"webview",
"wm",
"am",
"sm",
"audio",
"video",
"camera",
"hal",
"app",
"res",
"dalvik",
"rs",
"bionic",
"power"
);
/**
* Tests that atrace exists and is runnable with no args
*/
public void testSimpleRun() throws Exception {
String output = getDevice().executeShellCommand("atrace");
String[] lines = output.split("\\r?\\n");
// check for expected stdout
assertEquals("capturing trace... done", lines[0]);
assertEquals("TRACE:", lines[1]);
// commented trace marker starts here
assertEquals("# tracer: nop", lines[2]);
}
/**
* Tests the output of "atrace --list_categories" to ensure required categories exist.
*/
public void testCategories() throws Exception {
String output = getDevice().executeShellCommand("atrace --list_categories");
String[] categories = output.split("\\r?\\n");
Set<String> requiredCategories = new HashSet<String>(sRequiredCategoriesList);
for (String category : categories) {
int dashIndex = category.indexOf("-");
assertTrue(dashIndex > 1); // must match category output format
category = category.substring(0, dashIndex).trim();
requiredCategories.remove(category);
}
if (!requiredCategories.isEmpty()) {
for (String missingCategory : requiredCategories) {
System.err.println("missing category: " + missingCategory);
}
fail("Expected categories missing from atrace");
}
}
/**
* 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 atrace test app. Reason: %s", installResult),
installResult);
// capture a launch of the app with async tracing
// content traced by 'view' tag tested below, 'sched' used to ensure tgid printed
String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
getDevice().executeShellCommand("am start " + TEST_PKG);
getDevice().executeShellCommand("sleep 1");
atraceOutput = getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
} finally {
assertNotNull("unable to capture atrace output", atraceOutput);
getDevice().uninstallPackage(TEST_PKG);
}
// 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());
FtraceEntryCallback callback = new FtraceEntryCallback() {
private int userSpaceMatches = 0;
private int beginMatches = 0;
private int nextSectionIndex = -1;
private int appTid = -1;
private final String initialSection = "traceable-app-test-section";
// list of tags expected to be seen on app launch, in order, after the initial.
private final String[] requiredSectionList = {
"inflate",
"Choreographer#doFrame",
"traversal",
"measure",
"layout",
"draw",
"Record View#draw()"
};
@Override
public void onTraceEntry(String truncatedThreadName, int pid, int tid,
String eventName, String details) {
if (!"tracing_mark_write".equals(eventName)) {
// not userspace trace, ignore
return;
}
assertNotNull(truncatedThreadName);
assertTrue(tid > 0);
userSpaceMatches++;
if (details == null || !details.startsWith("B|")) {
// not a begin event
return;
}
beginMatches++;
if (details.endsWith("|" + initialSection)) {
// initial section observed, start looking for others in order
assertEquals(nextSectionIndex, -1);
nextSectionIndex = 0;
appTid = tid;
return;
}
if (nextSectionIndex >= 0
&& tid == appTid
&& nextSectionIndex < requiredSectionList.length
&& details.endsWith("|" + requiredSectionList[nextSectionIndex])) {
// found next required section in sequence
nextSectionIndex++;
}
}
@Override
public void onFinished() {
assertTrue("Unable to parse any userspace sections from atrace output",
userSpaceMatches != 0);
assertTrue("Unable to parse any section begin events from atrace output",
beginMatches != 0);
assertTrue("Unable to parse initial userspace sections from test app",
nextSectionIndex >= 0);
assertEquals("Didn't see required list of traced sections, in order",
requiredSectionList.length, nextSectionIndex);
}
};
FtraceParser.parse(new StringReader(traceData), callback);
}
}