blob: 8c256e0c44d6fe45e8d26fb66e80e76a03d26848 [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
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
Bob Leee5408332009-09-04 18:31:17 -070017package com.android.internal.os;
18
Jesse Wilsondb352692011-01-23 11:41:15 -080019import android.content.pm.PackageInfo;
20import android.os.Build;
21import android.os.SystemProperties;
22import android.util.Log;
Bob Leee5408332009-09-04 18:31:17 -070023import dalvik.system.SamplingProfiler;
Brian Carlstrom17510862010-08-18 14:27:40 -070024import java.io.BufferedOutputStream;
Bob Leee5408332009-09-04 18:31:17 -070025import java.io.File;
26import java.io.FileOutputStream;
27import java.io.IOException;
Brian Carlstrom17510862010-08-18 14:27:40 -070028import java.io.PrintStream;
Bob Leee5408332009-09-04 18:31:17 -070029import java.util.concurrent.Executor;
30import java.util.concurrent.Executors;
Brian Carlstromdef41ec2010-12-01 15:40:38 -080031import java.util.concurrent.ThreadFactory;
Jesse Wilsondb352692011-01-23 11:41:15 -080032import java.util.concurrent.atomic.AtomicBoolean;
33import libcore.io.IoUtils;
Bob Leee5408332009-09-04 18:31:17 -070034
35/**
36 * Integrates the framework with Dalvik's sampling profiler.
37 */
38public class SamplingProfilerIntegration {
39
40 private static final String TAG = "SamplingProfilerIntegration";
41
Sen Hubde75702010-05-28 01:54:03 -070042 public static final String SNAPSHOT_DIR = "/data/snapshots";
43
Bob Leee5408332009-09-04 18:31:17 -070044 private static final boolean enabled;
45 private static final Executor snapshotWriter;
Brian Carlstromdef41ec2010-12-01 15:40:38 -080046 private static final int samplingProfilerMilliseconds;
47 private static final int samplingProfilerDepth;
Sen Hubde75702010-05-28 01:54:03 -070048
49 /** Whether or not a snapshot is being persisted. */
50 private static final AtomicBoolean pending = new AtomicBoolean(false);
51
Bob Leee5408332009-09-04 18:31:17 -070052 static {
Brian Carlstromdef41ec2010-12-01 15:40:38 -080053 samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0);
54 samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4);
55 if (samplingProfilerMilliseconds > 0) {
56 File dir = new File(SNAPSHOT_DIR);
57 dir.mkdirs();
58 // the directory needs to be writable to anybody to allow file writing
59 dir.setWritable(true, false);
60 // the directory needs to be executable to anybody to allow file creation
61 dir.setExecutable(true, false);
62 if (dir.isDirectory()) {
63 snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() {
64 public Thread newThread(Runnable r) {
65 return new Thread(r, TAG);
66 }
67 });
68 enabled = true;
69 Log.i(TAG, "Profiling enabled. Sampling interval ms: "
70 + samplingProfilerMilliseconds);
71 } else {
72 snapshotWriter = null;
73 enabled = true;
74 Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR);
75 }
Bob Leee5408332009-09-04 18:31:17 -070076 } else {
77 snapshotWriter = null;
Sen Hubde75702010-05-28 01:54:03 -070078 enabled = false;
Brian Carlstromdef41ec2010-12-01 15:40:38 -080079 Log.i(TAG, "Profiling disabled.");
Bob Leee5408332009-09-04 18:31:17 -070080 }
81 }
82
Brian Carlstrom17510862010-08-18 14:27:40 -070083 private static SamplingProfiler INSTANCE;
84
Bob Leee5408332009-09-04 18:31:17 -070085 /**
86 * Is profiling enabled?
87 */
88 public static boolean isEnabled() {
89 return enabled;
90 }
91
92 /**
93 * Starts the profiler if profiling is enabled.
94 */
95 public static void start() {
Brian Carlstrom17510862010-08-18 14:27:40 -070096 if (!enabled) {
97 return;
98 }
99 ThreadGroup group = Thread.currentThread().getThreadGroup();
100 SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
Brian Carlstromdef41ec2010-12-01 15:40:38 -0800101 INSTANCE = new SamplingProfiler(samplingProfilerDepth, threadSet);
102 INSTANCE.start(samplingProfilerMilliseconds);
Bob Leee5408332009-09-04 18:31:17 -0700103 }
104
Bob Leee5408332009-09-04 18:31:17 -0700105 /**
Sen Hubde75702010-05-28 01:54:03 -0700106 * Writes a snapshot if profiling is enabled.
Bob Leee5408332009-09-04 18:31:17 -0700107 */
Sen Hubde75702010-05-28 01:54:03 -0700108 public static void writeSnapshot(final String processName, final PackageInfo packageInfo) {
Brian Carlstrom17510862010-08-18 14:27:40 -0700109 if (!enabled) {
110 return;
111 }
Bob Leee5408332009-09-04 18:31:17 -0700112
Bob Leeeec2f412009-09-10 11:01:24 +0200113 /*
Sen Hubde75702010-05-28 01:54:03 -0700114 * If we're already writing a snapshot, don't bother enqueueing another
Bob Leeeec2f412009-09-10 11:01:24 +0200115 * request right now. This will reduce the number of individual
116 * snapshots and in turn the total amount of memory consumed (one big
117 * snapshot is smaller than N subset snapshots).
118 */
Sen Hubde75702010-05-28 01:54:03 -0700119 if (pending.compareAndSet(false, true)) {
Bob Leee5408332009-09-04 18:31:17 -0700120 snapshotWriter.execute(new Runnable() {
121 public void run() {
Bob Leee5408332009-09-04 18:31:17 -0700122 try {
Brian Carlstromdef41ec2010-12-01 15:40:38 -0800123 writeSnapshotFile(processName, packageInfo);
Bob Leee5408332009-09-04 18:31:17 -0700124 } finally {
Sen Hubde75702010-05-28 01:54:03 -0700125 pending.set(false);
Bob Leee5408332009-09-04 18:31:17 -0700126 }
127 }
128 });
129 }
130 }
131
132 /**
133 * Writes the zygote's snapshot to internal storage if profiling is enabled.
134 */
135 public static void writeZygoteSnapshot() {
Brian Carlstrom17510862010-08-18 14:27:40 -0700136 if (!enabled) {
137 return;
138 }
Brian Carlstromdef41ec2010-12-01 15:40:38 -0800139 writeSnapshotFile("zygote", null);
Brian Carlstrom17510862010-08-18 14:27:40 -0700140 INSTANCE.shutdown();
141 INSTANCE = null;
Bob Leee5408332009-09-04 18:31:17 -0700142 }
143
Sen Hubde75702010-05-28 01:54:03 -0700144 /**
145 * pass in PackageInfo to retrieve various values for snapshot header
146 */
Brian Carlstromdef41ec2010-12-01 15:40:38 -0800147 private static void writeSnapshotFile(String processName, PackageInfo packageInfo) {
Brian Carlstrom17510862010-08-18 14:27:40 -0700148 if (!enabled) {
Bob Leee5408332009-09-04 18:31:17 -0700149 return;
150 }
Brian Carlstrom17510862010-08-18 14:27:40 -0700151 INSTANCE.stop();
Bob Leee5408332009-09-04 18:31:17 -0700152
153 /*
154 * We use the current time as a unique ID. We can't use a counter
155 * because processes restart. This could result in some overlap if
156 * we capture two snapshots in rapid succession.
157 */
158 long start = System.currentTimeMillis();
Sen Hubde75702010-05-28 01:54:03 -0700159 String name = processName.replaceAll(":", ".");
Brian Carlstromdef41ec2010-12-01 15:40:38 -0800160 String path = SNAPSHOT_DIR + "/" + name + "-" +System.currentTimeMillis() + ".snapshot";
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700161 PrintStream out = null;
Bob Leee5408332009-09-04 18:31:17 -0700162 try {
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700163 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700164 generateSnapshotHeader(name, packageInfo, out);
Jesse Wilsondb352692011-01-23 11:41:15 -0800165 new SamplingProfiler.AsciiHprofWriter(INSTANCE.getHprofData(), out).write();
166 if (out.checkError()) {
167 throw new IOException();
168 }
169 } catch (IOException e) {
170 Log.e(TAG, "Error writing snapshot to " + path, e);
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700171 return;
Jesse Wilsondb352692011-01-23 11:41:15 -0800172 } finally {
173 IoUtils.closeQuietly(out);
Bob Leee5408332009-09-04 18:31:17 -0700174 }
Sen Hubde75702010-05-28 01:54:03 -0700175 // set file readable to the world so that SamplingProfilerService
176 // can put it to dropbox
177 new File(path).setReadable(true, false);
178
179 long elapsed = System.currentTimeMillis() - start;
180 Log.i(TAG, "Wrote snapshot for " + name + " in " + elapsed + "ms.");
181 }
182
183 /**
184 * generate header for snapshots, with the following format (like http header):
185 *
186 * Version: <version number of profiler>\n
187 * Process: <process name>\n
188 * Package: <package name, if exists>\n
189 * Package-Version: <version number of the package, if exists>\n
190 * Build: <fingerprint>\n
191 * \n
192 * <the actual snapshot content begins here...>
193 */
194 private static void generateSnapshotHeader(String processName, PackageInfo packageInfo,
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700195 PrintStream out) {
Sen Hubde75702010-05-28 01:54:03 -0700196 // profiler version
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700197 out.println("Version: 2");
198 out.println("Process: " + processName);
199 if (packageInfo != null) {
200 out.println("Package: " + packageInfo.packageName);
201 out.println("Package-Version: " + packageInfo.versionCode);
Sen Hubde75702010-05-28 01:54:03 -0700202 }
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700203 out.println("Build: " + Build.FINGERPRINT);
Sen Hubde75702010-05-28 01:54:03 -0700204 // single blank line means the end of snapshot header.
Brian Carlstromc9ad7c62010-09-01 12:41:36 -0700205 out.println();
Bob Leee5408332009-09-04 18:31:17 -0700206 }
Bob Leee5408332009-09-04 18:31:17 -0700207}