• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.Build;
20 import android.os.AsyncTask;
21 import android.os.FileUtils;
22 import android.util.Log;
23 
24 import java.io.BufferedReader;
25 import java.io.File;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.text.SimpleDateFormat;
31 import java.util.Arrays;
32 import java.util.Date;
33 import java.util.Locale;
34 import java.util.Collection;
35 import java.util.concurrent.TimeUnit;
36 import java.util.TreeMap;
37 
38 /**
39  * Utility functions for tracing.
40  * Will call atrace or perfetto depending on the setting.
41  */
42 public class TraceUtils {
43 
44     static final String TAG = "Traceur";
45 
46     public static final String TRACE_DIRECTORY = "/data/local/traces/";
47 
48     // To change Traceur to use atrace to collect traces,
49     // change mTraceEngine to point to AtraceUtils().
50     private static TraceEngine mTraceEngine = new PerfettoUtils();
51 
52     private static final Runtime RUNTIME = Runtime.getRuntime();
53     private static final int PROCESS_TIMEOUT_MS = 30000; // 30 seconds
54 
55     public interface TraceEngine {
getName()56         public String getName();
getOutputExtension()57         public String getOutputExtension();
traceStart(Collection<String> tags, int bufferSizeKb, boolean apps, boolean attachToBugreport, boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)58         public boolean traceStart(Collection<String> tags, int bufferSizeKb, boolean apps,
59             boolean attachToBugreport, boolean longTrace, int maxLongTraceSizeMb,
60             int maxLongTraceDurationMinutes);
traceStop()61         public void traceStop();
traceDump(File outFile)62         public boolean traceDump(File outFile);
isTracingOn()63         public boolean isTracingOn();
64     }
65 
currentTraceEngine()66     public static String currentTraceEngine() {
67         return mTraceEngine.getName();
68     }
69 
traceStart(Collection<String> tags, int bufferSizeKb, boolean apps, boolean longTrace, boolean attachToBugreport, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)70     public static boolean traceStart(Collection<String> tags, int bufferSizeKb, boolean apps,
71             boolean longTrace, boolean attachToBugreport, int maxLongTraceSizeMb,
72             int maxLongTraceDurationMinutes) {
73         return mTraceEngine.traceStart(tags, bufferSizeKb, apps,
74             attachToBugreport, longTrace, maxLongTraceSizeMb, maxLongTraceDurationMinutes);
75     }
76 
traceStop()77     public static void traceStop() {
78         mTraceEngine.traceStop();
79     }
80 
traceDump(File outFile)81     public static boolean traceDump(File outFile) {
82         return mTraceEngine.traceDump(outFile);
83     }
84 
isTracingOn()85     public static boolean isTracingOn() {
86         return mTraceEngine.isTracingOn();
87     }
88 
listCategories()89     public static TreeMap<String, String> listCategories() {
90         return PerfettoUtils.perfettoListCategories();
91     }
92 
clearSavedTraces()93     public static void clearSavedTraces() {
94         String cmd = "rm -f " + TRACE_DIRECTORY + "trace-*.*trace";
95 
96         Log.v(TAG, "Clearing trace directory: " + cmd);
97         try {
98             Process rm = exec(cmd);
99 
100             if (rm.waitFor() != 0) {
101                 Log.e(TAG, "clearSavedTraces failed with: " + rm.exitValue());
102             }
103         } catch (Exception e) {
104             throw new RuntimeException(e);
105         }
106     }
107 
exec(String cmd)108     public static Process exec(String cmd) throws IOException {
109         return exec(cmd, null);
110     }
111 
exec(String cmd, String tmpdir)112     public static Process exec(String cmd, String tmpdir) throws IOException {
113         return exec(cmd, tmpdir, true);
114     }
115 
exec(String cmd, String tmpdir, boolean logOutput)116     public static Process exec(String cmd, String tmpdir, boolean logOutput) throws IOException {
117         String[] cmdarray = {"sh", "-c", cmd};
118         String[] envp = {"TMPDIR=" + tmpdir};
119         envp = tmpdir == null ? null : envp;
120 
121         Log.v(TAG, "exec: " + Arrays.toString(envp) + " " + Arrays.toString(cmdarray));
122 
123         Process process = RUNTIME.exec(cmdarray, envp);
124         new Logger("traceService:stderr", process.getErrorStream());
125         if (logOutput) {
126             new Logger("traceService:stdout", process.getInputStream());
127         }
128 
129         return process;
130     }
131 
132     // Returns the Process if the command terminated on time and null if not.
execWithTimeout(String cmd, String tmpdir, long timeout)133     public static Process execWithTimeout(String cmd, String tmpdir, long timeout)
134             throws IOException {
135         Process process = exec(cmd, tmpdir, true);
136         try {
137             if (!process.waitFor(timeout, TimeUnit.MILLISECONDS)) {
138                 Log.e(TAG, "Command '" + cmd + "' has timed out after " + timeout + " ms.");
139                 process.destroyForcibly();
140                 // Return null to signal a timeout and that the Process was destroyed.
141                 return null;
142             }
143         } catch (Exception e) {
144             throw new RuntimeException(e);
145         }
146         return process;
147     }
148 
getOutputFilename()149     public static String getOutputFilename() {
150         String format = "yyyy-MM-dd-HH-mm-ss";
151         String now = new SimpleDateFormat(format, Locale.US).format(new Date());
152         return String.format("trace-%s-%s-%s.%s", Build.BOARD, Build.ID, now,
153             mTraceEngine.getOutputExtension());
154     }
155 
getOutputFile(String filename)156     public static File getOutputFile(String filename) {
157         return new File(TraceUtils.TRACE_DIRECTORY, filename);
158     }
159 
cleanupOlderFiles(final int minCount, final long minAge)160     protected static void cleanupOlderFiles(final int minCount, final long minAge) {
161         new AsyncTask<Void, Void, Void>() {
162             @Override
163             protected Void doInBackground(Void... params) {
164                 try {
165                     FileUtils.deleteOlderFiles(new File(TRACE_DIRECTORY), minCount, minAge);
166                 } catch (RuntimeException e) {
167                     Log.e(TAG, "Failed to delete older traces", e);
168                 }
169                 return null;
170             }
171         }.execute();
172     }
173 
174     /**
175      * Streams data from an InputStream to an OutputStream
176      */
177     static class Streamer {
178         private boolean mDone;
179 
Streamer(final String tag, final InputStream in, final OutputStream out)180         Streamer(final String tag, final InputStream in, final OutputStream out) {
181             new Thread(tag) {
182                 @Override
183                 public void run() {
184                     int read;
185                     byte[] buf = new byte[2 << 10];
186                     try {
187                         while ((read = in.read(buf)) != -1) {
188                             out.write(buf, 0, read);
189                         }
190                     } catch (IOException e) {
191                         Log.e(TAG, "Error while streaming " + tag);
192                     } finally {
193                         try {
194                             out.close();
195                         } catch (IOException e) {
196                             // Welp.
197                         }
198                         synchronized (Streamer.this) {
199                             mDone = true;
200                             Streamer.this.notify();
201                         }
202                     }
203                 }
204             }.start();
205         }
206 
isDone()207         synchronized boolean isDone() {
208             return mDone;
209         }
210 
waitForDone()211         synchronized void waitForDone() {
212             while (!isDone()) {
213                 try {
214                     wait();
215                 } catch (InterruptedException e) {
216                     Thread.currentThread().interrupt();
217                 }
218             }
219         }
220     }
221 
222     /**
223      * Redirects an InputStream to logcat.
224      */
225     private static class Logger {
226 
Logger(final String tag, final InputStream in)227         Logger(final String tag, final InputStream in) {
228             new Thread(tag) {
229                 @Override
230                 public void run() {
231                     String line;
232                     BufferedReader r = new BufferedReader(new InputStreamReader(in));
233                     try {
234                         while ((line = r.readLine()) != null) {
235                             Log.e(TAG, tag + ": " + line);
236                         }
237                     } catch (IOException e) {
238                         Log.e(TAG, "Error while streaming " + tag);
239                     } finally {
240                         try {
241                             r.close();
242                         } catch (IOException e) {
243                             // Welp.
244                         }
245                     }
246                 }
247             }.start();
248         }
249     }
250 }
251