blob: 63878ae2108bf05aff53bde9d43d45f61d079ff2 [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.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 TAG = "AtraceHostTest";
private static final String TEST_APK = "CtsAtraceTestApp.apk";
private static final String TEST_PKG = "com.android.cts.atracetestapp";
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) {
Log.d(TAG, "missing category: " + missingCategory);
}
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
// content traced by 'view' tag tested below, 'sched' used to ensure tgid printed
String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view sched"; // 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;
}
}
}