SamplingProfilerIntegration.java revision c9ad7c6dbb1d70b831cd79416cbe493ade50ed2c
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; 29import java.util.concurrent.atomic.AtomicBoolean; 30 31import android.content.pm.PackageInfo; 32import android.util.Log; 33import android.os.*; 34 35/** 36 * Integrates the framework with Dalvik's sampling profiler. 37 */ 38public class SamplingProfilerIntegration { 39 40 private static final String TAG = "SamplingProfilerIntegration"; 41 42 public static final String SNAPSHOT_DIR = "/data/snapshots"; 43 44 private static final boolean enabled; 45 private static final Executor snapshotWriter; 46 private static final int samplingProfilerHz; 47 48 /** Whether or not we've created the snapshots dir. */ 49 private static boolean dirMade = false; 50 51 /** Whether or not a snapshot is being persisted. */ 52 private static final AtomicBoolean pending = new AtomicBoolean(false); 53 54 static { 55 samplingProfilerHz = SystemProperties.getInt("persist.sys.profiler_hz", 0); 56 if (samplingProfilerHz > 0) { 57 snapshotWriter = Executors.newSingleThreadExecutor(); 58 enabled = true; 59 Log.i(TAG, "Profiler is enabled. Sampling Profiler Hz: " + samplingProfilerHz); 60 } else { 61 snapshotWriter = null; 62 enabled = false; 63 Log.i(TAG, "Profiler is disabled."); 64 } 65 } 66 67 private static SamplingProfiler INSTANCE; 68 69 /** 70 * Is profiling enabled? 71 */ 72 public static boolean isEnabled() { 73 return enabled; 74 } 75 76 /** 77 * Starts the profiler if profiling is enabled. 78 */ 79 public static void start() { 80 if (!enabled) { 81 return; 82 } 83 ThreadGroup group = Thread.currentThread().getThreadGroup(); 84 SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group); 85 INSTANCE = new SamplingProfiler(4, threadSet); 86 INSTANCE.start(samplingProfilerHz); 87 } 88 89 /** 90 * Writes a snapshot if profiling is enabled. 91 */ 92 public static void writeSnapshot(final String processName, final PackageInfo packageInfo) { 93 if (!enabled) { 94 return; 95 } 96 97 /* 98 * If we're already writing a snapshot, don't bother enqueueing another 99 * request right now. This will reduce the number of individual 100 * snapshots and in turn the total amount of memory consumed (one big 101 * snapshot is smaller than N subset snapshots). 102 */ 103 if (pending.compareAndSet(false, true)) { 104 snapshotWriter.execute(new Runnable() { 105 public void run() { 106 if (!dirMade) { 107 File dir = new File(SNAPSHOT_DIR); 108 dir.mkdirs(); 109 // the directory needs to be writable to anybody 110 dir.setWritable(true, false); 111 // the directory needs to be executable to anybody 112 // don't know why yet, but mode 723 would work, while 113 // mode 722 throws FileNotFoundExecption at line 151 114 dir.setExecutable(true, false); 115 if (new File(SNAPSHOT_DIR).isDirectory()) { 116 dirMade = true; 117 } else { 118 Log.w(TAG, "Creation of " + SNAPSHOT_DIR + " failed."); 119 pending.set(false); 120 return; 121 } 122 } 123 try { 124 writeSnapshot(SNAPSHOT_DIR, processName, packageInfo); 125 } finally { 126 pending.set(false); 127 } 128 } 129 }); 130 } 131 } 132 133 /** 134 * Writes the zygote's snapshot to internal storage if profiling is enabled. 135 */ 136 public static void writeZygoteSnapshot() { 137 if (!enabled) { 138 return; 139 } 140 writeSnapshot("zygote", null); 141 INSTANCE.shutdown(); 142 INSTANCE = null; 143 } 144 145 /** 146 * pass in PackageInfo to retrieve various values for snapshot header 147 */ 148 private static void writeSnapshot(String dir, String processName, PackageInfo packageInfo) { 149 if (!enabled) { 150 return; 151 } 152 INSTANCE.stop(); 153 154 /* 155 * We use the current time as a unique ID. We can't use a counter 156 * because processes restart. This could result in some overlap if 157 * we capture two snapshots in rapid succession. 158 */ 159 long start = System.currentTimeMillis(); 160 String name = processName.replaceAll(":", "."); 161 String path = dir + "/" + name + "-" +System.currentTimeMillis() + ".snapshot"; 162 PrintStream out = null; 163 try { 164 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path))); 165 } catch (IOException e) { 166 Log.e(TAG, "Could not open " + path + ":" + e); 167 return; 168 } 169 try { 170 generateSnapshotHeader(name, packageInfo, out); 171 INSTANCE.writeHprofData(out); 172 } finally { 173 out.close(); 174 } 175 if (out.checkError()) { 176 Log.e(TAG, "Error writing snapshot."); 177 return; 178 } 179 // set file readable to the world so that SamplingProfilerService 180 // can put it to dropbox 181 new File(path).setReadable(true, false); 182 183 long elapsed = System.currentTimeMillis() - start; 184 Log.i(TAG, "Wrote snapshot for " + name + " in " + elapsed + "ms."); 185 } 186 187 /** 188 * generate header for snapshots, with the following format (like http header): 189 * 190 * Version: <version number of profiler>\n 191 * Process: <process name>\n 192 * Package: <package name, if exists>\n 193 * Package-Version: <version number of the package, if exists>\n 194 * Build: <fingerprint>\n 195 * \n 196 * <the actual snapshot content begins here...> 197 */ 198 private static void generateSnapshotHeader(String processName, PackageInfo packageInfo, 199 PrintStream out) { 200 // profiler version 201 out.println("Version: 2"); 202 out.println("Process: " + processName); 203 if (packageInfo != null) { 204 out.println("Package: " + packageInfo.packageName); 205 out.println("Package-Version: " + packageInfo.versionCode); 206 } 207 out.println("Build: " + Build.FINGERPRINT); 208 // single blank line means the end of snapshot header. 209 out.println(); 210 } 211} 212