Home | History | Annotate | Download | only in traceur
      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.SystemProperties;
     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.text.SimpleDateFormat;
     34 import java.util.Arrays;
     35 import java.util.Date;
     36 import java.util.List;
     37 import java.util.Locale;
     38 import java.util.TreeMap;
     39 
     40 /**
     41  * Utility functions for calling atrace
     42  */
     43 public class AtraceUtils {
     44 
     45     static final String TAG = "Traceur";
     46 
     47     public static final String TRACE_DIRECTORY = "/data/local/traces/";
     48 
     49     private static final String DEBUG_TRACING_FILE = "/sys/kernel/debug/tracing/tracing_on";
     50     private static final String TRACING_FILE = "/sys/kernel/tracing/tracing_on";
     51 
     52     private static final Runtime RUNTIME = Runtime.getRuntime();
     53 
     54     public static boolean atraceStart(String tags, int bufferSizeKb, boolean apps) {
     55         String appParameter = apps ? "-a '*' " : "";
     56         String cmd = "atrace --async_start -c -b " + bufferSizeKb + " " + appParameter + tags;
     57 
     58         Log.v(TAG, "Starting async atrace: " + cmd);
     59         try {
     60             Process atrace = exec(cmd);
     61             if (atrace.waitFor() != 0) {
     62                 Log.e(TAG, "atraceStart failed with: " + atrace.exitValue());
     63                 return false;
     64             }
     65         } catch (Exception e) {
     66             throw new RuntimeException(e);
     67         }
     68         return true;
     69     }
     70 
     71     public static void atraceStop() {
     72         String cmd = "atrace --async_stop > /dev/null";
     73 
     74         Log.v(TAG, "Stopping async atrace: " + cmd);
     75         try {
     76             Process atrace = exec(cmd);
     77 
     78             if (atrace.waitFor() != 0) {
     79                 Log.e(TAG, "atraceStop failed with: " + atrace.exitValue());
     80             }
     81         } catch (Exception e) {
     82             throw new RuntimeException(e);
     83         }
     84     }
     85     public static boolean atraceDump(File outFile) {
     86         String cmd = "atrace --async_stop -z -c -o " + outFile;
     87 
     88         Log.v(TAG, "Dumping async atrace: " + cmd);
     89         try {
     90             Process atrace = exec(cmd);
     91 
     92             if (atrace.waitFor() != 0) {
     93                 Log.e(TAG, "atraceDump failed with: " + atrace.exitValue());
     94                 return false;
     95             }
     96 
     97             Process ps = exec("ps -AT");
     98 
     99             new Streamer("atraceDump:ps:stdout",
    100                     ps.getInputStream(), new FileOutputStream(outFile, true /* append */));
    101 
    102             if (ps.waitFor() != 0) {
    103                 Log.e(TAG, "atraceDump:ps failed with: " + ps.exitValue());
    104                 return false;
    105             }
    106 
    107             // Set the new file world readable to allow it to be adb pulled.
    108             outFile.setReadable(true, false); // (readable, ownerOnly)
    109         } catch (Exception e) {
    110             throw new RuntimeException(e);
    111         }
    112         return true;
    113     }
    114 
    115     public static TreeMap<String,String> atraceListCategories() {
    116         String cmd = "atrace --list_categories";
    117 
    118         Log.v(TAG, "Listing tags: " + cmd);
    119         try {
    120             Process atrace = exec(cmd);
    121 
    122             new Logger("atraceListCat:stderr", atrace.getErrorStream());
    123             BufferedReader stdout = new BufferedReader(
    124                     new InputStreamReader(atrace.getInputStream()));
    125 
    126             if (atrace.waitFor() != 0) {
    127                 Log.e(TAG, "atraceListCategories failed with: " + atrace.exitValue());
    128             }
    129 
    130             TreeMap<String, String> result = new TreeMap<>();
    131             String line;
    132             while ((line = stdout.readLine()) != null) {
    133                 String[] fields = line.trim().split(" - ", 2);
    134                 if (fields.length == 2) {
    135                     result.put(fields[0], fields[1]);
    136                 }
    137             }
    138             return result;
    139         } catch (Exception e) {
    140             throw new RuntimeException(e);
    141         }
    142     }
    143 
    144     public static void clearSavedTraces() {
    145         String cmd = "rm -f " + TRACE_DIRECTORY + "trace-*.ctrace";
    146 
    147         Log.v(TAG, "Clearing trace directory: " + cmd);
    148         try {
    149             Process rm = exec(cmd);
    150 
    151             if (rm.waitFor() != 0) {
    152                 Log.e(TAG, "clearSavedTraces failed with: " + rm.exitValue());
    153             }
    154         } catch (Exception e) {
    155             throw new RuntimeException(e);
    156         }
    157     }
    158 
    159     private static Process exec(String cmd) throws IOException {
    160         String[] cmdarray = {"sh", "-c", cmd};
    161         Log.v(TAG, "exec: " + Arrays.toString(cmdarray));
    162         return RUNTIME.exec(cmdarray);
    163     }
    164 
    165     public static boolean isTracingOn() {
    166         boolean userInitiatedTracingFlag =
    167             "1".equals(SystemProperties.get("debug.atrace.user_initiated", ""));
    168 
    169         if (!userInitiatedTracingFlag) {
    170             return false;
    171         }
    172 
    173         boolean tracingOnFlag = false;
    174 
    175         try {
    176             List<String> tracingOnContents;
    177 
    178             Path debugTracingOnPath = Paths.get(DEBUG_TRACING_FILE);
    179             Path tracingOnPath = Paths.get(TRACING_FILE);
    180 
    181             if (Files.isReadable(debugTracingOnPath)) {
    182                 tracingOnContents = Files.readAllLines(debugTracingOnPath);
    183             } else if (Files.isReadable(tracingOnPath)) {
    184                 tracingOnContents = Files.readAllLines(tracingOnPath);
    185             } else {
    186                 return false;
    187             }
    188 
    189             tracingOnFlag = !tracingOnContents.get(0).equals("0");
    190         } catch (IOException e) {
    191             throw new RuntimeException(e);
    192         }
    193 
    194         return userInitiatedTracingFlag && tracingOnFlag;
    195     }
    196 
    197     public static String getOutputFilename() {
    198         String format = "yyyy-MM-dd-HH-mm-ss";
    199         String now = new SimpleDateFormat(format, Locale.US).format(new Date());
    200         return String.format("trace-%s-%s-%s.ctrace", Build.BOARD, Build.ID, now);
    201     }
    202 
    203     public static File getOutputFile(String filename) {
    204         return new File(AtraceUtils.TRACE_DIRECTORY, filename);
    205     }
    206 
    207     /**
    208      * Streams data from an InputStream to an OutputStream
    209      */
    210     private static class Streamer {
    211         private boolean mDone;
    212 
    213         Streamer(final String tag, final InputStream in, final OutputStream out) {
    214             new Thread(tag) {
    215                 @Override
    216                 public void run() {
    217                     int read;
    218                     byte[] buf = new byte[2 << 10];
    219                     try {
    220                         while ((read = in.read(buf)) != -1) {
    221                             out.write(buf, 0, read);
    222                         }
    223                     } catch (IOException e) {
    224                         Log.e(TAG, "Error while streaming " + tag);
    225                     } finally {
    226                         try {
    227                             out.close();
    228                         } catch (IOException e) {
    229                             // Welp.
    230                         }
    231                         synchronized (Streamer.this) {
    232                             mDone = true;
    233                             Streamer.this.notify();
    234                         }
    235                     }
    236                 }
    237             }.start();
    238         }
    239 
    240         synchronized boolean isDone() {
    241             return mDone;
    242         }
    243 
    244         synchronized void waitForDone() {
    245             while (!isDone()) {
    246                 try {
    247                     wait();
    248                 } catch (InterruptedException e) {
    249                     Thread.currentThread().interrupt();
    250                 }
    251             }
    252         }
    253     }
    254 
    255     /**
    256      * Streams data from an InputStream to an OutputStream
    257      */
    258     private static class Logger {
    259 
    260         Logger(final String tag, final InputStream in) {
    261             new Thread(tag) {
    262                 @Override
    263                 public void run() {
    264                     int read;
    265                     String line;
    266                     BufferedReader r = new BufferedReader(new InputStreamReader(in));
    267                     try {
    268                         while ((line = r.readLine()) != null) {
    269                             Log.e(TAG, tag + ": " + line);
    270                         }
    271                     } catch (IOException e) {
    272                         Log.e(TAG, "Error while streaming " + tag);
    273                     } finally {
    274                         try {
    275                             r.close();
    276                         } catch (IOException e) {
    277                             // Welp.
    278                         }
    279                     }
    280                 }
    281             }.start();
    282         }
    283     }
    284 }
    285