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