package com.android.internal.os; import dalvik.system.SamplingProfiler; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.FileNotFoundException; 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."); } } /** * Is profiling enabled? */ public static boolean isEnabled() { return enabled; } /** * Starts the profiler if profiling is enabled. */ public static void start() { if (!enabled) return; SamplingProfiler.getInstance().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"); } private static void writeSnapshot(String dir, String name) { byte[] snapshot = SamplingProfiler.getInstance().snapshot(); if (snapshot == null) { return; } /* * 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 { // Try to open the file a few times. The SD card may not be mounted. FileOutputStream out; int count = 0; while (true) { try { out = 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 { out.write(snapshot); } finally { out.close(); } long elapsed = System.currentTimeMillis() - start; Log.i(TAG, "Wrote snapshot for " + name + " in " + elapsed + "ms."); } catch (IOException e) { Log.e(TAG, "Error writing snapshot.", e); } } }