SamplingProfilerIntegration.java revision 41e99538638795e38b0a02711ceb572d67a779a3
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.os;
18
19import dalvik.system.SamplingProfiler;
20
21import java.io.BufferedOutputStream;
22import java.io.File;
23import java.io.FileNotFoundException;
24import java.io.FileOutputStream;
25import java.io.IOException;
26import java.io.PrintStream;
27import java.util.concurrent.Executor;
28import java.util.concurrent.Executors;
29
30import android.util.Log;
31import android.os.*;
32
33/**
34 * Integrates the framework with Dalvik's sampling profiler.
35 */
36public class SamplingProfilerIntegration {
37
38    private static final String TAG = "SamplingProfilerIntegration";
39
40    private static final boolean enabled;
41    private static final Executor snapshotWriter;
42    static {
43        enabled = "1".equals(SystemProperties.get("persist.sampling_profiler"));
44        if (enabled) {
45            snapshotWriter = Executors.newSingleThreadExecutor();
46            Log.i(TAG, "Profiler is enabled.");
47        } else {
48            snapshotWriter = null;
49            Log.i(TAG, "Profiler is disabled.");
50        }
51    }
52
53    private static SamplingProfiler INSTANCE;
54
55    /**
56     * Is profiling enabled?
57     */
58    public static boolean isEnabled() {
59        return enabled;
60    }
61
62    /**
63     * Starts the profiler if profiling is enabled.
64     */
65    public static void start() {
66        if (!enabled) {
67            return;
68        }
69        ThreadGroup group = Thread.currentThread().getThreadGroup();
70        SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
71        INSTANCE = new SamplingProfiler(4, threadSet);
72        INSTANCE.start(10);
73    }
74
75    /** Whether or not we've created the snapshots dir. */
76    static boolean dirMade = false;
77
78    /** Whether or not a snapshot is being persisted. */
79    static volatile boolean pending;
80
81    /**
82     * Writes a snapshot to the SD card if profiling is enabled.
83     */
84    public static void writeSnapshot(final String name) {
85        if (!enabled) {
86            return;
87        }
88
89        /*
90         * If we're already writing a snapshot, don't bother enqueing another
91         * request right now. This will reduce the number of individual
92         * snapshots and in turn the total amount of memory consumed (one big
93         * snapshot is smaller than N subset snapshots).
94         */
95        if (!pending) {
96            pending = true;
97            snapshotWriter.execute(new Runnable() {
98                public void run() {
99                    String dir =
100                        Environment.getExternalStorageDirectory().getPath() + "/snapshots";
101                    if (!dirMade) {
102                        new File(dir).mkdirs();
103                        if (new File(dir).isDirectory()) {
104                            dirMade = true;
105                        } else {
106                            Log.w(TAG, "Creation of " + dir + " failed.");
107                            return;
108                        }
109                    }
110                    try {
111                        writeSnapshot(dir, name);
112                    } finally {
113                        pending = false;
114                    }
115                }
116            });
117        }
118    }
119
120    /**
121     * Writes the zygote's snapshot to internal storage if profiling is enabled.
122     */
123    public static void writeZygoteSnapshot() {
124        if (!enabled) {
125            return;
126        }
127
128        String dir = "/data/zygote/snapshots";
129        new File(dir).mkdirs();
130        writeSnapshot(dir, "zygote");
131        INSTANCE.shutdown();
132        INSTANCE = null;
133    }
134
135    private static void writeSnapshot(String dir, String name) {
136        if (!enabled) {
137            return;
138        }
139        INSTANCE.stop();
140
141        /*
142         * We use the current time as a unique ID. We can't use a counter
143         * because processes restart. This could result in some overlap if
144         * we capture two snapshots in rapid succession.
145         */
146        long start = System.currentTimeMillis();
147        String path = dir + "/" + name.replace(':', '.') + "-"
148                + System.currentTimeMillis() + ".snapshot";
149
150        // Try to open the file a few times. The SD card may not be mounted.
151        PrintStream out;
152        int count = 0;
153        while (true) {
154            try {
155                out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
156                break;
157            } catch (FileNotFoundException e) {
158                if (++count > 3) {
159                    Log.e(TAG, "Could not open " + path + ".");
160                    return;
161                }
162
163                // Sleep for a bit and then try again.
164                try {
165                    Thread.sleep(2500);
166                } catch (InterruptedException e1) { /* ignore */ }
167            }
168        }
169
170        try {
171            INSTANCE.writeHprofData(out);
172        } finally {
173            out.close();
174        }
175        if (out.checkError()) {
176            Log.e(TAG, "Error writing snapshot.");
177        } else {
178            long elapsed = System.currentTimeMillis() - start;
179            Log.i(TAG, "Wrote snapshot for " + name
180                  + " in " + elapsed + "ms.");
181        }
182    }
183}
184