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