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