| 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.*; |
| import android.net.Uri; |
| |
| /** |
| * 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 (!pending) { |
| pending = true; |
| snapshotWriter.execute(new Runnable() { |
| public void run() { |
| String dir = "/sdcard/snapshots"; |
| if (!dirMade) { |
| makeDirectory(dir); |
| dirMade = true; |
| } |
| 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"; |
| makeDirectory(dir); |
| 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); |
| } |
| } |
| |
| private static void makeDirectory(String dir) { |
| new File(dir).mkdirs(); |
| } |
| } |