SamplingProfilerIntegration.java revision 41e99538638795e38b0a02711ceb572d67a779a3
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.BufferedOutputStream; 22import java.io.File; 23import java.io.FileNotFoundException; 24import java.io.FileOutputStream; 25import java.io.IOException; 26import java.io.PrintStream; 27import java.util.concurrent.Executor; 28import java.util.concurrent.Executors; 29 30import android.util.Log; 31import android.os.*; 32 33/** 34 * Integrates the framework with Dalvik's sampling profiler. 35 */ 36public 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 = 100 Environment.getExternalStorageDirectory().getPath() + "/snapshots"; 101 if (!dirMade) { 102 new File(dir).mkdirs(); 103 if (new File(dir).isDirectory()) { 104 dirMade = true; 105 } else { 106 Log.w(TAG, "Creation of " + dir + " failed."); 107 return; 108 } 109 } 110 try { 111 writeSnapshot(dir, name); 112 } finally { 113 pending = false; 114 } 115 } 116 }); 117 } 118 } 119 120 /** 121 * Writes the zygote's snapshot to internal storage if profiling is enabled. 122 */ 123 public static void writeZygoteSnapshot() { 124 if (!enabled) { 125 return; 126 } 127 128 String dir = "/data/zygote/snapshots"; 129 new File(dir).mkdirs(); 130 writeSnapshot(dir, "zygote"); 131 INSTANCE.shutdown(); 132 INSTANCE = null; 133 } 134 135 private static void writeSnapshot(String dir, String name) { 136 if (!enabled) { 137 return; 138 } 139 INSTANCE.stop(); 140 141 /* 142 * We use the current time as a unique ID. We can't use a counter 143 * because processes restart. This could result in some overlap if 144 * we capture two snapshots in rapid succession. 145 */ 146 long start = System.currentTimeMillis(); 147 String path = dir + "/" + name.replace(':', '.') + "-" 148 + System.currentTimeMillis() + ".snapshot"; 149 150 // Try to open the file a few times. The SD card may not be mounted. 151 PrintStream out; 152 int count = 0; 153 while (true) { 154 try { 155 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path))); 156 break; 157 } catch (FileNotFoundException e) { 158 if (++count > 3) { 159 Log.e(TAG, "Could not open " + path + "."); 160 return; 161 } 162 163 // Sleep for a bit and then try again. 164 try { 165 Thread.sleep(2500); 166 } catch (InterruptedException e1) { /* ignore */ } 167 } 168 } 169 170 try { 171 INSTANCE.writeHprofData(out); 172 } finally { 173 out.close(); 174 } 175 if (out.checkError()) { 176 Log.e(TAG, "Error writing snapshot."); 177 } else { 178 long elapsed = System.currentTimeMillis() - start; 179 Log.i(TAG, "Wrote snapshot for " + name 180 + " in " + elapsed + "ms."); 181 } 182 } 183} 184