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