blob: b5a7141469b975aea0033e6e516f7b2cc80f80d3 [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 com.android.traceur;
import android.os.Build;
import android.os.SystemProperties;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TreeMap;
/**
* Utility functions for calling atrace
*/
public class AtraceUtils {
static final String TAG = "Traceur";
public static final String TRACE_DIRECTORY = "/data/local/traces/";
private static final String DEBUG_TRACING_FILE = "/sys/kernel/debug/tracing/tracing_on";
private static final String TRACING_FILE = "/sys/kernel/tracing/tracing_on";
private static final Runtime RUNTIME = Runtime.getRuntime();
public static boolean atraceStart(String tags, int bufferSizeKb, boolean apps) {
String appParameter = apps ? "-a '*' " : "";
String cmd = "atrace --async_start -c -b " + bufferSizeKb + " " + appParameter + tags;
Log.v(TAG, "Starting async atrace: " + cmd);
try {
Process atrace = exec(cmd);
if (atrace.waitFor() != 0) {
Log.e(TAG, "atraceStart failed with: " + atrace.exitValue());
return false;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return true;
}
public static void atraceStop() {
String cmd = "atrace --async_stop > /dev/null";
Log.v(TAG, "Stopping async atrace: " + cmd);
try {
Process atrace = exec(cmd);
if (atrace.waitFor() != 0) {
Log.e(TAG, "atraceStop failed with: " + atrace.exitValue());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean atraceDump(File outFile) {
String cmd = "atrace --async_stop -z -c -o " + outFile;
Log.v(TAG, "Dumping async atrace: " + cmd);
try {
Process atrace = exec(cmd);
if (atrace.waitFor() != 0) {
Log.e(TAG, "atraceDump failed with: " + atrace.exitValue());
return false;
}
Process ps = exec("ps -AT");
new Streamer("atraceDump:ps:stdout",
ps.getInputStream(), new FileOutputStream(outFile, true /* append */));
if (ps.waitFor() != 0) {
Log.e(TAG, "atraceDump:ps failed with: " + ps.exitValue());
return false;
}
// Set the new file world readable to allow it to be adb pulled.
outFile.setReadable(true, false); // (readable, ownerOnly)
} catch (Exception e) {
throw new RuntimeException(e);
}
return true;
}
public static TreeMap<String,String> atraceListCategories() {
String cmd = "atrace --list_categories";
Log.v(TAG, "Listing tags: " + cmd);
try {
Process atrace = exec(cmd);
new Logger("atraceListCat:stderr", atrace.getErrorStream());
BufferedReader stdout = new BufferedReader(
new InputStreamReader(atrace.getInputStream()));
if (atrace.waitFor() != 0) {
Log.e(TAG, "atraceListCategories failed with: " + atrace.exitValue());
}
TreeMap<String, String> result = new TreeMap<>();
String line;
while ((line = stdout.readLine()) != null) {
String[] fields = line.trim().split(" - ", 2);
if (fields.length == 2) {
result.put(fields[0], fields[1]);
}
}
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void clearSavedTraces() {
String cmd = "rm -f " + TRACE_DIRECTORY + "trace-*.ctrace";
Log.v(TAG, "Clearing trace directory: " + cmd);
try {
Process rm = exec(cmd);
if (rm.waitFor() != 0) {
Log.e(TAG, "clearSavedTraces failed with: " + rm.exitValue());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Process exec(String cmd) throws IOException {
String[] cmdarray = {"sh", "-c", cmd};
Log.v(TAG, "exec: " + Arrays.toString(cmdarray));
return RUNTIME.exec(cmdarray);
}
public static boolean isTracingOn() {
boolean userInitiatedTracingFlag =
"1".equals(SystemProperties.get("debug.atrace.user_initiated", ""));
if (!userInitiatedTracingFlag) {
return false;
}
boolean tracingOnFlag = false;
try {
List<String> tracingOnContents;
Path debugTracingOnPath = Paths.get(DEBUG_TRACING_FILE);
Path tracingOnPath = Paths.get(TRACING_FILE);
if (Files.isReadable(debugTracingOnPath)) {
tracingOnContents = Files.readAllLines(debugTracingOnPath);
} else if (Files.isReadable(tracingOnPath)) {
tracingOnContents = Files.readAllLines(tracingOnPath);
} else {
return false;
}
tracingOnFlag = !tracingOnContents.get(0).equals("0");
} catch (IOException e) {
throw new RuntimeException(e);
}
return userInitiatedTracingFlag && tracingOnFlag;
}
public static String getOutputFilename() {
String format = "yyyy-MM-dd-HH-mm-ss";
String now = new SimpleDateFormat(format, Locale.US).format(new Date());
return String.format("trace-%s-%s-%s.ctrace", Build.BOARD, Build.ID, now);
}
public static File getOutputFile(String filename) {
return new File(AtraceUtils.TRACE_DIRECTORY, filename);
}
/**
* Streams data from an InputStream to an OutputStream
*/
private static class Streamer {
private boolean mDone;
Streamer(final String tag, final InputStream in, final OutputStream out) {
new Thread(tag) {
@Override
public void run() {
int read;
byte[] buf = new byte[2 << 10];
try {
while ((read = in.read(buf)) != -1) {
out.write(buf, 0, read);
}
} catch (IOException e) {
Log.e(TAG, "Error while streaming " + tag);
} finally {
try {
out.close();
} catch (IOException e) {
// Welp.
}
synchronized (Streamer.this) {
mDone = true;
Streamer.this.notify();
}
}
}
}.start();
}
synchronized boolean isDone() {
return mDone;
}
synchronized void waitForDone() {
while (!isDone()) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
/**
* Streams data from an InputStream to an OutputStream
*/
private static class Logger {
Logger(final String tag, final InputStream in) {
new Thread(tag) {
@Override
public void run() {
int read;
String line;
BufferedReader r = new BufferedReader(new InputStreamReader(in));
try {
while ((line = r.readLine()) != null) {
Log.e(TAG, tag + ": " + line);
}
} catch (IOException e) {
Log.e(TAG, "Error while streaming " + tag);
} finally {
try {
r.close();
} catch (IOException e) {
// Welp.
}
}
}
}.start();
}
}
}