SamplingProfilerIntegration.java revision a9602db3d5a6c9bc5a7a31b4fe3cc141235ad332
115a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root/* 215a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Copyright (C) 2009 The Android Open Source Project 315a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * 415a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Licensed under the Apache License, Version 2.0 (the "License"); 515a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * you may not use this file except in compliance with the License. 615a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * You may obtain a copy of the License at 715a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * 815a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * http://www.apache.org/licenses/LICENSE-2.0 915a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * 1015a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Unless required by applicable law or agreed to in writing, software 1115a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * distributed under the License is distributed on an "AS IS" BASIS, 1215a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1315a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * See the License for the specific language governing permissions and 1415a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * limitations under the License. 1515a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root */ 1615a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root 17e540833fdff4d58e37c9ba859388e24e2945ed45Bob Leepackage com.android.internal.os; 18e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 19db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilsonimport android.content.pm.PackageInfo; 20db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilsonimport android.os.Build; 21db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilsonimport android.os.SystemProperties; 22db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilsonimport android.util.Log; 23a9602db3d5a6c9bc5a7a31b4fe3cc141235ad332Brian Carlstromimport dalvik.system.profiler.AsciiHprofWriter; 24a9602db3d5a6c9bc5a7a31b4fe3cc141235ad332Brian Carlstromimport dalvik.system.profiler.SamplingProfiler; 251751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstromimport java.io.BufferedOutputStream; 26e540833fdff4d58e37c9ba859388e24e2945ed45Bob Leeimport java.io.File; 27e540833fdff4d58e37c9ba859388e24e2945ed45Bob Leeimport java.io.FileOutputStream; 28e540833fdff4d58e37c9ba859388e24e2945ed45Bob Leeimport java.io.IOException; 291751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstromimport java.io.PrintStream; 30e540833fdff4d58e37c9ba859388e24e2945ed45Bob Leeimport java.util.concurrent.Executor; 31e540833fdff4d58e37c9ba859388e24e2945ed45Bob Leeimport java.util.concurrent.Executors; 32def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstromimport java.util.concurrent.ThreadFactory; 33db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilsonimport java.util.concurrent.atomic.AtomicBoolean; 34db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilsonimport libcore.io.IoUtils; 35e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 36e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee/** 37e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee * Integrates the framework with Dalvik's sampling profiler. 38e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee */ 39e540833fdff4d58e37c9ba859388e24e2945ed45Bob Leepublic class SamplingProfilerIntegration { 40e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 41e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee private static final String TAG = "SamplingProfilerIntegration"; 42e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 43bde75706592c77379fb6546283e733abaca6fe04Sen Hu public static final String SNAPSHOT_DIR = "/data/snapshots"; 44bde75706592c77379fb6546283e733abaca6fe04Sen Hu 45e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee private static final boolean enabled; 46e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee private static final Executor snapshotWriter; 47def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom private static final int samplingProfilerMilliseconds; 48def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom private static final int samplingProfilerDepth; 49bde75706592c77379fb6546283e733abaca6fe04Sen Hu 50bde75706592c77379fb6546283e733abaca6fe04Sen Hu /** Whether or not a snapshot is being persisted. */ 51bde75706592c77379fb6546283e733abaca6fe04Sen Hu private static final AtomicBoolean pending = new AtomicBoolean(false); 52bde75706592c77379fb6546283e733abaca6fe04Sen Hu 53e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee static { 54def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0); 55def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4); 56def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom if (samplingProfilerMilliseconds > 0) { 57def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom File dir = new File(SNAPSHOT_DIR); 58def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom dir.mkdirs(); 59def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom // the directory needs to be writable to anybody to allow file writing 60def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom dir.setWritable(true, false); 61def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom // the directory needs to be executable to anybody to allow file creation 62def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom dir.setExecutable(true, false); 63def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom if (dir.isDirectory()) { 64def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() { 65def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom public Thread newThread(Runnable r) { 66def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom return new Thread(r, TAG); 67def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom } 68def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom }); 69def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom enabled = true; 70def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom Log.i(TAG, "Profiling enabled. Sampling interval ms: " 71def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom + samplingProfilerMilliseconds); 72def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom } else { 73def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom snapshotWriter = null; 74def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom enabled = true; 75def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR); 76def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom } 77e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } else { 78e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee snapshotWriter = null; 79bde75706592c77379fb6546283e733abaca6fe04Sen Hu enabled = false; 80def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom Log.i(TAG, "Profiling disabled."); 81e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 82e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 83e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 841751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom private static SamplingProfiler INSTANCE; 851751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom 86e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee /** 87e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee * Is profiling enabled? 88e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee */ 89e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee public static boolean isEnabled() { 90e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee return enabled; 91e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 92e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 93e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee /** 94e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee * Starts the profiler if profiling is enabled. 95e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee */ 96e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee public static void start() { 971751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom if (!enabled) { 981751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom return; 991751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom } 1001751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom ThreadGroup group = Thread.currentThread().getThreadGroup(); 1011751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group); 102def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom INSTANCE = new SamplingProfiler(samplingProfilerDepth, threadSet); 103def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom INSTANCE.start(samplingProfilerMilliseconds); 104e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 105e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 106e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee /** 107bde75706592c77379fb6546283e733abaca6fe04Sen Hu * Writes a snapshot if profiling is enabled. 108e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee */ 109bde75706592c77379fb6546283e733abaca6fe04Sen Hu public static void writeSnapshot(final String processName, final PackageInfo packageInfo) { 1101751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom if (!enabled) { 1111751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom return; 1121751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom } 113e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 114eec2f41d607c3eacba4f7d9cc098b335c7310d23Bob Lee /* 115bde75706592c77379fb6546283e733abaca6fe04Sen Hu * If we're already writing a snapshot, don't bother enqueueing another 116eec2f41d607c3eacba4f7d9cc098b335c7310d23Bob Lee * request right now. This will reduce the number of individual 117eec2f41d607c3eacba4f7d9cc098b335c7310d23Bob Lee * snapshots and in turn the total amount of memory consumed (one big 118eec2f41d607c3eacba4f7d9cc098b335c7310d23Bob Lee * snapshot is smaller than N subset snapshots). 119eec2f41d607c3eacba4f7d9cc098b335c7310d23Bob Lee */ 120bde75706592c77379fb6546283e733abaca6fe04Sen Hu if (pending.compareAndSet(false, true)) { 121e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee snapshotWriter.execute(new Runnable() { 122e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee public void run() { 123e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee try { 124def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom writeSnapshotFile(processName, packageInfo); 125e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } finally { 126bde75706592c77379fb6546283e733abaca6fe04Sen Hu pending.set(false); 127e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 128e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 129e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee }); 130e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 131e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 132e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 133e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee /** 134e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee * Writes the zygote's snapshot to internal storage if profiling is enabled. 135e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee */ 136e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee public static void writeZygoteSnapshot() { 1371751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom if (!enabled) { 1381751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom return; 1391751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom } 140def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom writeSnapshotFile("zygote", null); 1411751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom INSTANCE.shutdown(); 1421751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom INSTANCE = null; 143e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 144e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 145bde75706592c77379fb6546283e733abaca6fe04Sen Hu /** 146bde75706592c77379fb6546283e733abaca6fe04Sen Hu * pass in PackageInfo to retrieve various values for snapshot header 147bde75706592c77379fb6546283e733abaca6fe04Sen Hu */ 148def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom private static void writeSnapshotFile(String processName, PackageInfo packageInfo) { 1491751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom if (!enabled) { 150e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee return; 151e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 1521751086360056bc60d00f2ed2988bc82be9e7bd9Brian Carlstrom INSTANCE.stop(); 153e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 154e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee /* 155e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee * We use the current time as a unique ID. We can't use a counter 156e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee * because processes restart. This could result in some overlap if 157e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee * we capture two snapshots in rapid succession. 158e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee */ 159e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee long start = System.currentTimeMillis(); 160bde75706592c77379fb6546283e733abaca6fe04Sen Hu String name = processName.replaceAll(":", "."); 161def41ec2e88a70e63590117c93476276f8d0bf4cBrian Carlstrom String path = SNAPSHOT_DIR + "/" + name + "-" +System.currentTimeMillis() + ".snapshot"; 162c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom PrintStream out = null; 163e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee try { 164c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path))); 165db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilson generateSnapshotHeader(name, packageInfo, out); 166a9602db3d5a6c9bc5a7a31b4fe3cc141235ad332Brian Carlstrom new AsciiHprofWriter(INSTANCE.getHprofData(), out).write(); 167db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilson if (out.checkError()) { 168db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilson throw new IOException(); 169db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilson } 170bde75706592c77379fb6546283e733abaca6fe04Sen Hu } catch (IOException e) { 171db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilson Log.e(TAG, "Error writing snapshot to " + path, e); 172c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom return; 173bde75706592c77379fb6546283e733abaca6fe04Sen Hu } finally { 174db35269ae4e6a6572156e39557a993d5b730c167Jesse Wilson IoUtils.closeQuietly(out); 175bde75706592c77379fb6546283e733abaca6fe04Sen Hu } 176bde75706592c77379fb6546283e733abaca6fe04Sen Hu // set file readable to the world so that SamplingProfilerService 177bde75706592c77379fb6546283e733abaca6fe04Sen Hu // can put it to dropbox 178bde75706592c77379fb6546283e733abaca6fe04Sen Hu new File(path).setReadable(true, false); 179e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee 180bde75706592c77379fb6546283e733abaca6fe04Sen Hu long elapsed = System.currentTimeMillis() - start; 181bde75706592c77379fb6546283e733abaca6fe04Sen Hu Log.i(TAG, "Wrote snapshot for " + name + " in " + elapsed + "ms."); 182bde75706592c77379fb6546283e733abaca6fe04Sen Hu } 183bde75706592c77379fb6546283e733abaca6fe04Sen Hu 184bde75706592c77379fb6546283e733abaca6fe04Sen Hu /** 185bde75706592c77379fb6546283e733abaca6fe04Sen Hu * generate header for snapshots, with the following format (like http header): 186bde75706592c77379fb6546283e733abaca6fe04Sen Hu * 187bde75706592c77379fb6546283e733abaca6fe04Sen Hu * Version: <version number of profiler>\n 188bde75706592c77379fb6546283e733abaca6fe04Sen Hu * Process: <process name>\n 189bde75706592c77379fb6546283e733abaca6fe04Sen Hu * Package: <package name, if exists>\n 190bde75706592c77379fb6546283e733abaca6fe04Sen Hu * Package-Version: <version number of the package, if exists>\n 191bde75706592c77379fb6546283e733abaca6fe04Sen Hu * Build: <fingerprint>\n 192bde75706592c77379fb6546283e733abaca6fe04Sen Hu * \n 193bde75706592c77379fb6546283e733abaca6fe04Sen Hu * <the actual snapshot content begins here...> 194bde75706592c77379fb6546283e733abaca6fe04Sen Hu */ 195bde75706592c77379fb6546283e733abaca6fe04Sen Hu private static void generateSnapshotHeader(String processName, PackageInfo packageInfo, 196c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom PrintStream out) { 197bde75706592c77379fb6546283e733abaca6fe04Sen Hu // profiler version 198c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom out.println("Version: 2"); 199c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom out.println("Process: " + processName); 200c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom if (packageInfo != null) { 201c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom out.println("Package: " + packageInfo.packageName); 202c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom out.println("Package-Version: " + packageInfo.versionCode); 203e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 204c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom out.println("Build: " + Build.FINGERPRINT); 205bde75706592c77379fb6546283e733abaca6fe04Sen Hu // single blank line means the end of snapshot header. 206c9ad7c6dbb1d70b831cd79416cbe493ade50ed2cBrian Carlstrom out.println(); 207e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee } 208e540833fdff4d58e37c9ba859388e24e2945ed45Bob Lee} 209