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 com.android.traceur; 18 19 import android.sysprop.TraceProperties; 20 import android.text.TextUtils; 21 import android.util.Log; 22 23 import java.io.BufferedReader; 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.InputStreamReader; 29 import java.io.OutputStream; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.nio.file.Paths; 33 import java.util.Collection; 34 import java.util.List; 35 import java.util.TreeMap; 36 37 /** 38 * Utility functions for calling atrace 39 */ 40 public class AtraceUtils implements TraceUtils.TraceEngine { 41 42 static final String TAG = "Traceur"; 43 44 private static final String DEBUG_TRACING_FILE = "/sys/kernel/debug/tracing/tracing_on"; 45 private static final String TRACING_FILE = "/sys/kernel/tracing/tracing_on"; 46 47 public static String NAME = "ATRACE"; 48 private static String OUTPUT_EXTENSION = "ctrace"; 49 getName()50 public String getName() { 51 return NAME; 52 } 53 getOutputExtension()54 public String getOutputExtension() { 55 return OUTPUT_EXTENSION; 56 } 57 58 /* Note: longTrace and maxLongTrace* parameters are ignored in atrace mode. */ traceStart(Collection<String> tags, int bufferSizeKb, boolean apps, boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)59 public boolean traceStart(Collection<String> tags, int bufferSizeKb, boolean apps, 60 boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) { 61 62 String appParameter = apps ? "-a '*' " : ""; 63 String cmd = "atrace --async_start -c -b " + bufferSizeKb + " " 64 + appParameter + TextUtils.join(" ", tags); 65 66 Log.v(TAG, "Starting async atrace: " + cmd); 67 try { 68 Process atrace = TraceUtils.exec(cmd); 69 if (atrace.waitFor() != 0) { 70 Log.e(TAG, "atraceStart failed with: " + atrace.exitValue()); 71 return false; 72 } 73 } catch (Exception e) { 74 throw new RuntimeException(e); 75 } 76 return true; 77 } 78 traceStop()79 public void traceStop() { 80 String cmd = "atrace --async_stop > /dev/null"; 81 82 Log.v(TAG, "Stopping async atrace: " + cmd); 83 try { 84 Process atrace = TraceUtils.exec(cmd); 85 86 if (atrace.waitFor() != 0) { 87 Log.e(TAG, "atraceStop failed with: " + atrace.exitValue()); 88 } 89 } catch (Exception e) { 90 throw new RuntimeException(e); 91 } 92 } 93 traceDump(File outFile)94 public boolean traceDump(File outFile) { 95 String cmd = "atrace --async_stop -z -c -o " + outFile; 96 97 Log.v(TAG, "Dumping async atrace: " + cmd); 98 try { 99 Process atrace = TraceUtils.exec(cmd); 100 101 if (atrace.waitFor() != 0) { 102 Log.e(TAG, "atraceDump failed with: " + atrace.exitValue()); 103 return false; 104 } 105 106 Process ps = TraceUtils.exec("ps -AT"); 107 108 new Streamer("atraceDump:ps:stdout", 109 ps.getInputStream(), new FileOutputStream(outFile, true /* append */)); 110 111 if (ps.waitFor() != 0) { 112 Log.e(TAG, "atraceDump:ps failed with: " + ps.exitValue()); 113 return false; 114 } 115 116 // Set the new file world readable to allow it to be adb pulled. 117 outFile.setReadable(true, false); // (readable, ownerOnly) 118 outFile.setWritable(true, false); // (readable, ownerOnly) 119 } catch (Exception e) { 120 throw new RuntimeException(e); 121 } 122 return true; 123 } 124 isTracingOn()125 public boolean isTracingOn() { 126 boolean userInitiatedTracingFlag = 127 TraceProperties.user_initiated().orElse(false); 128 129 if (!userInitiatedTracingFlag) { 130 return false; 131 } 132 133 boolean tracingOnFlag = false; 134 135 try { 136 List<String> tracingOnContents; 137 138 Path debugTracingOnPath = Paths.get(DEBUG_TRACING_FILE); 139 Path tracingOnPath = Paths.get(TRACING_FILE); 140 141 if (Files.isReadable(debugTracingOnPath)) { 142 tracingOnContents = Files.readAllLines(debugTracingOnPath); 143 } else if (Files.isReadable(tracingOnPath)) { 144 tracingOnContents = Files.readAllLines(tracingOnPath); 145 } else { 146 return false; 147 } 148 149 tracingOnFlag = !tracingOnContents.get(0).equals("0"); 150 } catch (IOException e) { 151 throw new RuntimeException(e); 152 } 153 154 return userInitiatedTracingFlag && tracingOnFlag; 155 } 156 atraceListCategories()157 public static TreeMap<String,String> atraceListCategories() { 158 String cmd = "atrace --list_categories"; 159 160 Log.v(TAG, "Listing tags: " + cmd); 161 try { 162 Process atrace = TraceUtils.exec(cmd); 163 164 new Logger("atraceListCat:stderr", atrace.getErrorStream()); 165 BufferedReader stdout = new BufferedReader( 166 new InputStreamReader(atrace.getInputStream())); 167 168 if (atrace.waitFor() != 0) { 169 Log.e(TAG, "atraceListCategories failed with: " + atrace.exitValue()); 170 } 171 172 TreeMap<String, String> result = new TreeMap<>(); 173 String line; 174 while ((line = stdout.readLine()) != null) { 175 String[] fields = line.trim().split(" - ", 2); 176 if (fields.length == 2) { 177 result.put(fields[0], fields[1]); 178 } 179 } 180 return result; 181 } catch (Exception e) { 182 throw new RuntimeException(e); 183 } 184 } 185 186 187 /** 188 * Streams data from an InputStream to an OutputStream 189 */ 190 private static class Streamer { 191 private boolean mDone; 192 Streamer(final String tag, final InputStream in, final OutputStream out)193 Streamer(final String tag, final InputStream in, final OutputStream out) { 194 new Thread(tag) { 195 @Override 196 public void run() { 197 int read; 198 byte[] buf = new byte[2 << 10]; 199 try { 200 while ((read = in.read(buf)) != -1) { 201 out.write(buf, 0, read); 202 } 203 } catch (IOException e) { 204 Log.e(TAG, "Error while streaming " + tag); 205 } finally { 206 try { 207 out.close(); 208 } catch (IOException e) { 209 // Welp. 210 } 211 synchronized (Streamer.this) { 212 mDone = true; 213 Streamer.this.notify(); 214 } 215 } 216 } 217 }.start(); 218 } 219 isDone()220 synchronized boolean isDone() { 221 return mDone; 222 } 223 waitForDone()224 synchronized void waitForDone() { 225 while (!isDone()) { 226 try { 227 wait(); 228 } catch (InterruptedException e) { 229 Thread.currentThread().interrupt(); 230 } 231 } 232 } 233 } 234 235 /** 236 * Streams data from an InputStream to an OutputStream 237 */ 238 private static class Logger { 239 Logger(final String tag, final InputStream in)240 Logger(final String tag, final InputStream in) { 241 new Thread(tag) { 242 @Override 243 public void run() { 244 int read; 245 String line; 246 BufferedReader r = new BufferedReader(new InputStreamReader(in)); 247 try { 248 while ((line = r.readLine()) != null) { 249 Log.e(TAG, tag + ": " + line); 250 } 251 } catch (IOException e) { 252 Log.e(TAG, "Error while streaming " + tag); 253 } finally { 254 try { 255 r.close(); 256 } catch (IOException e) { 257 // Welp. 258 } 259 } 260 } 261 }.start(); 262 } 263 } 264 } 265