Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2009 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.internal.os;
     18 
     19 import dalvik.system.SamplingProfiler;
     20 
     21 import java.io.BufferedOutputStream;
     22 import java.io.File;
     23 import java.io.FileNotFoundException;
     24 import java.io.FileOutputStream;
     25 import java.io.IOException;
     26 import java.io.PrintStream;
     27 import java.util.concurrent.Executor;
     28 import java.util.concurrent.Executors;
     29 
     30 import android.util.Log;
     31 import android.os.*;
     32 
     33 /**
     34  * Integrates the framework with Dalvik's sampling profiler.
     35  */
     36 public class SamplingProfilerIntegration {
     37 
     38     private static final String TAG = "SamplingProfilerIntegration";
     39 
     40     private static final boolean enabled;
     41     private static final Executor snapshotWriter;
     42     static {
     43         enabled = "1".equals(SystemProperties.get("persist.sampling_profiler"));
     44         if (enabled) {
     45             snapshotWriter = Executors.newSingleThreadExecutor();
     46             Log.i(TAG, "Profiler is enabled.");
     47         } else {
     48             snapshotWriter = null;
     49             Log.i(TAG, "Profiler is disabled.");
     50         }
     51     }
     52 
     53     private static SamplingProfiler INSTANCE;
     54 
     55     /**
     56      * Is profiling enabled?
     57      */
     58     public static boolean isEnabled() {
     59         return enabled;
     60     }
     61 
     62     /**
     63      * Starts the profiler if profiling is enabled.
     64      */
     65     public static void start() {
     66         if (!enabled) {
     67             return;
     68         }
     69         ThreadGroup group = Thread.currentThread().getThreadGroup();
     70         SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
     71         INSTANCE = new SamplingProfiler(4, threadSet);
     72         INSTANCE.start(10);
     73     }
     74 
     75     /** Whether or not we've created the snapshots dir. */
     76     static boolean dirMade = false;
     77 
     78     /** Whether or not a snapshot is being persisted. */
     79     static volatile boolean pending;
     80 
     81     /**
     82      * Writes a snapshot to the SD card if profiling is enabled.
     83      */
     84     public static void writeSnapshot(final String name) {
     85         if (!enabled) {
     86             return;
     87         }
     88 
     89         /*
     90          * If we're already writing a snapshot, don't bother enqueing another
     91          * request right now. This will reduce the number of individual
     92          * snapshots and in turn the total amount of memory consumed (one big
     93          * snapshot is smaller than N subset snapshots).
     94          */
     95         if (!pending) {
     96             pending = true;
     97             snapshotWriter.execute(new Runnable() {
     98                 public void run() {
     99                     String dir = "/sdcard/snapshots";
    100                     if (!dirMade) {
    101                         new File(dir).mkdirs();
    102                         if (new File(dir).isDirectory()) {
    103                             dirMade = true;
    104                         } else {
    105                             Log.w(TAG, "Creation of " + dir + " failed.");
    106                             return;
    107                         }
    108                     }
    109                     try {
    110                         writeSnapshot(dir, name);
    111                     } finally {
    112                         pending = false;
    113                     }
    114                 }
    115             });
    116         }
    117     }
    118 
    119     /**
    120      * Writes the zygote's snapshot to internal storage if profiling is enabled.
    121      */
    122     public static void writeZygoteSnapshot() {
    123         if (!enabled) {
    124             return;
    125         }
    126 
    127         String dir = "/data/zygote/snapshots";
    128         new File(dir).mkdirs();
    129         writeSnapshot(dir, "zygote");
    130         INSTANCE.shutdown();
    131         INSTANCE = null;
    132     }
    133 
    134     private static void writeSnapshot(String dir, String name) {
    135         if (!enabled) {
    136             return;
    137         }
    138         INSTANCE.stop();
    139 
    140         /*
    141          * We use the current time as a unique ID. We can't use a counter
    142          * because processes restart. This could result in some overlap if
    143          * we capture two snapshots in rapid succession.
    144          */
    145         long start = System.currentTimeMillis();
    146         String path = dir + "/" + name.replace(':', '.') + "-"
    147                 + System.currentTimeMillis() + ".snapshot";
    148 
    149         // Try to open the file a few times. The SD card may not be mounted.
    150         PrintStream out;
    151         int count = 0;
    152         while (true) {
    153             try {
    154                 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
    155                 break;
    156             } catch (FileNotFoundException e) {
    157                 if (++count > 3) {
    158                     Log.e(TAG, "Could not open " + path + ".");
    159                     return;
    160                 }
    161 
    162                 // Sleep for a bit and then try again.
    163                 try {
    164                     Thread.sleep(2500);
    165                 } catch (InterruptedException e1) { /* ignore */ }
    166             }
    167         }
    168 
    169         try {
    170             INSTANCE.writeHprofData(out);
    171         } finally {
    172             out.close();
    173         }
    174         if (out.checkError()) {
    175             Log.e(TAG, "Error writing snapshot.");
    176         } else {
    177             long elapsed = System.currentTimeMillis() - start;
    178             Log.i(TAG, "Wrote snapshot for " + name
    179                   + " in " + elapsed + "ms.");
    180         }
    181     }
    182 }
    183