SamplingProfilerIntegration.java revision 9db3d07b9620b4269ab33f78604a36327e536ce1
1package com.android.internal.os;
2
3import dalvik.system.SamplingProfiler;
4
5import java.io.File;
6import java.io.FileOutputStream;
7import java.io.IOException;
8import java.io.FileNotFoundException;
9import java.util.concurrent.Executor;
10import java.util.concurrent.Executors;
11
12import android.util.Log;
13import android.os.*;
14
15/**
16 * Integrates the framework with Dalvik's sampling profiler.
17 */
18public class SamplingProfilerIntegration {
19
20    private static final String TAG = "SamplingProfilerIntegration";
21
22    private static final boolean enabled;
23    private static final Executor snapshotWriter;
24    static {
25        enabled = "1".equals(SystemProperties.get("persist.sampling_profiler"));
26        if (enabled) {
27            snapshotWriter = Executors.newSingleThreadExecutor();
28            Log.i(TAG, "Profiler is enabled.");
29        } else {
30            snapshotWriter = null;
31            Log.i(TAG, "Profiler is disabled.");
32        }
33    }
34
35    /**
36     * Is profiling enabled?
37     */
38    public static boolean isEnabled() {
39        return enabled;
40    }
41
42    /**
43     * Starts the profiler if profiling is enabled.
44     */
45    public static void start() {
46        if (!enabled) return;
47        SamplingProfiler.getInstance().start(10);
48    }
49
50    /** Whether or not we've created the snapshots dir. */
51    static boolean dirMade = false;
52
53    /** Whether or not a snapshot is being persisted. */
54    static volatile boolean pending;
55
56    /**
57     * Writes a snapshot to the SD card if profiling is enabled.
58     */
59    public static void writeSnapshot(final String name) {
60        if (!enabled) return;
61
62        /*
63         * If we're already writing a snapshot, don't bother enqueing another
64         * request right now. This will reduce the number of individual
65         * snapshots and in turn the total amount of memory consumed (one big
66         * snapshot is smaller than N subset snapshots).
67         */
68        if (!pending) {
69            pending = true;
70            snapshotWriter.execute(new Runnable() {
71                public void run() {
72                    String dir = "/sdcard/snapshots";
73                    if (!dirMade) {
74                        new File(dir).mkdirs();
75                        if (new File(dir).isDirectory()) {
76                            dirMade = true;
77                        } else {
78                            Log.w(TAG, "Creation of " + dir + " failed.");
79                            return;
80                        }
81                    }
82                    try {
83                        writeSnapshot(dir, name);
84                    } finally {
85                        pending = false;
86                    }
87                }
88            });
89        }
90    }
91
92    /**
93     * Writes the zygote's snapshot to internal storage if profiling is enabled.
94     */
95    public static void writeZygoteSnapshot() {
96        if (!enabled) return;
97
98        String dir = "/data/zygote/snapshots";
99        new File(dir).mkdirs();
100        writeSnapshot(dir, "zygote");
101    }
102
103    private static void writeSnapshot(String dir, String name) {
104        byte[] snapshot = SamplingProfiler.getInstance().snapshot();
105        if (snapshot == null) {
106            return;
107        }
108
109        /*
110         * We use the current time as a unique ID. We can't use a counter
111         * because processes restart. This could result in some overlap if
112         * we capture two snapshots in rapid succession.
113         */
114        long start = System.currentTimeMillis();
115        String path = dir + "/" + name.replace(':', '.') + "-" +
116                + System.currentTimeMillis() + ".snapshot";
117        try {
118            // Try to open the file a few times. The SD card may not be mounted.
119            FileOutputStream out;
120            int count = 0;
121            while (true) {
122                try {
123                    out = new FileOutputStream(path);
124                    break;
125                } catch (FileNotFoundException e) {
126                    if (++count > 3) {
127                        Log.e(TAG, "Could not open " + path + ".");
128                        return;
129                    }
130
131                    // Sleep for a bit and then try again.
132                    try {
133                        Thread.sleep(2500);
134                    } catch (InterruptedException e1) { /* ignore */ }
135                }
136            }
137
138            try {
139                out.write(snapshot);
140            } finally {
141                out.close();
142            }
143            long elapsed = System.currentTimeMillis() - start;
144            Log.i(TAG, "Wrote snapshot for " + name
145                    + " in " + elapsed + "ms.");
146        } catch (IOException e) {
147            Log.e(TAG, "Error writing snapshot.", e);
148        }
149    }
150}
151