| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.os; |
| |
| import dalvik.system.SamplingProfiler; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.Executors; |
| |
| import android.util.Log; |
| import android.os.*; |
| |
| /** |
| * Integrates the framework with Dalvik's sampling profiler. |
| */ |
| public class SamplingProfilerIntegration { |
| |
| private static final String TAG = "SamplingProfilerIntegration"; |
| |
| private static final boolean enabled; |
| private static final Executor snapshotWriter; |
| static { |
| enabled = "1".equals(SystemProperties.get("persist.sampling_profiler")); |
| if (enabled) { |
| snapshotWriter = Executors.newSingleThreadExecutor(); |
| Log.i(TAG, "Profiler is enabled."); |
| } else { |
| snapshotWriter = null; |
| Log.i(TAG, "Profiler is disabled."); |
| } |
| } |
| |
| private static SamplingProfiler INSTANCE; |
| |
| /** |
| * Is profiling enabled? |
| */ |
| public static boolean isEnabled() { |
| return enabled; |
| } |
| |
| /** |
| * Starts the profiler if profiling is enabled. |
| */ |
| public static void start() { |
| if (!enabled) { |
| return; |
| } |
| ThreadGroup group = Thread.currentThread().getThreadGroup(); |
| SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group); |
| INSTANCE = new SamplingProfiler(4, threadSet); |
| INSTANCE.start(10); |
| } |
| |
| /** Whether or not we've created the snapshots dir. */ |
| static boolean dirMade = false; |
| |
| /** Whether or not a snapshot is being persisted. */ |
| static volatile boolean pending; |
| |
| /** |
| * Writes a snapshot to the SD card if profiling is enabled. |
| */ |
| public static void writeSnapshot(final String name) { |
| if (!enabled) { |
| return; |
| } |
| |
| /* |
| * If we're already writing a snapshot, don't bother enqueing another |
| * request right now. This will reduce the number of individual |
| * snapshots and in turn the total amount of memory consumed (one big |
| * snapshot is smaller than N subset snapshots). |
| */ |
| if (!pending) { |
| pending = true; |
| snapshotWriter.execute(new Runnable() { |
| public void run() { |
| String dir = "/sdcard/snapshots"; |
| if (!dirMade) { |
| new File(dir).mkdirs(); |
| if (new File(dir).isDirectory()) { |
| dirMade = true; |
| } else { |
| Log.w(TAG, "Creation of " + dir + " failed."); |
| return; |
| } |
| } |
| try { |
| writeSnapshot(dir, name); |
| } finally { |
| pending = false; |
| } |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Writes the zygote's snapshot to internal storage if profiling is enabled. |
| */ |
| public static void writeZygoteSnapshot() { |
| if (!enabled) { |
| return; |
| } |
| |
| String dir = "/data/zygote/snapshots"; |
| new File(dir).mkdirs(); |
| writeSnapshot(dir, "zygote"); |
| INSTANCE.shutdown(); |
| INSTANCE = null; |
| } |
| |
| private static void writeSnapshot(String dir, String name) { |
| if (!enabled) { |
| return; |
| } |
| INSTANCE.stop(); |
| |
| /* |
| * We use the current time as a unique ID. We can't use a counter |
| * because processes restart. This could result in some overlap if |
| * we capture two snapshots in rapid succession. |
| */ |
| long start = System.currentTimeMillis(); |
| String path = dir + "/" + name.replace(':', '.') + "-" |
| + System.currentTimeMillis() + ".snapshot"; |
| |
| // Try to open the file a few times. The SD card may not be mounted. |
| PrintStream out; |
| int count = 0; |
| while (true) { |
| try { |
| out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path))); |
| break; |
| } catch (FileNotFoundException e) { |
| if (++count > 3) { |
| Log.e(TAG, "Could not open " + path + "."); |
| return; |
| } |
| |
| // Sleep for a bit and then try again. |
| try { |
| Thread.sleep(2500); |
| } catch (InterruptedException e1) { /* ignore */ } |
| } |
| } |
| |
| try { |
| INSTANCE.writeHprofData(out); |
| } finally { |
| out.close(); |
| } |
| if (out.checkError()) { |
| Log.e(TAG, "Error writing snapshot."); |
| } else { |
| long elapsed = System.currentTimeMillis() - start; |
| Log.i(TAG, "Wrote snapshot for " + name |
| + " in " + elapsed + "ms."); |
| } |
| } |
| } |