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