blob: 21e7252bf24d5d14e03bf83240b8d1f716e2d9b8 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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.server.am;
18
rpcraigec7ed14c2012-07-25 13:10:37 -040019import android.os.SELinux;
Joe Onorato8a9b2202010-02-26 18:56:32 -080020import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021
22import java.io.*;
23import java.util.Arrays;
24
25/**
26 * Monitors device resources periodically for some period of time. Useful for
27 * tracking down performance problems.
28 */
29class DeviceMonitor {
30
31 private static final String LOG_TAG = DeviceMonitor.class.getName();
32
33 /** Number of samples to take. */
34 private static final int SAMPLE_COUNT = 10;
35
36 /** Time to wait in ms between samples. */
37 private static final int INTERVAL = 1000;
38
39 /** Time to wait in ms between samples. */
40 private static final int MAX_FILES = 30;
41
42 private final byte[] buffer = new byte[1024];
43
44 /** Is the monitor currently running? */
45 private boolean running = false;
46
47 private DeviceMonitor() {
48 new Thread() {
49 public void run() {
50 monitor();
51 }
52 }.start();
53 }
54
55 /**
56 * Loops continuously. Pauses until someone tells us to start monitoring.
57 */
58 @SuppressWarnings("InfiniteLoopStatement")
59 private void monitor() {
60 while (true) {
61 waitForStart();
62
63 purge();
64
65 for (int i = 0; i < SAMPLE_COUNT; i++) {
66 try {
67 dump();
68 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080069 Slog.w(LOG_TAG, "Dump failed.", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 }
71 pause();
72 }
73
74 stop();
75 }
76 }
77
78 private static final File PROC = new File("/proc");
79 private static final File BASE = new File("/data/anr/");
80 static {
81 if (!BASE.isDirectory() && !BASE.mkdirs()) {
82 throw new AssertionError("Couldn't create " + BASE + ".");
83 }
rpcraigec7ed14c2012-07-25 13:10:37 -040084 if (!SELinux.restorecon(BASE)) {
85 throw new AssertionError("Couldn't restorecon " + BASE + ".");
86 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 }
88
89 private static final File[] PATHS = {
90 new File(PROC, "zoneinfo"),
91 new File(PROC, "interrupts"),
92 new File(PROC, "meminfo"),
93 new File(PROC, "slabinfo"),
94 };
95
96
97 /**
98 * Deletes old files.
99 */
100 private void purge() {
101 File[] files = BASE.listFiles();
102 int count = files.length - MAX_FILES;
103 if (count > 0) {
104 Arrays.sort(files);
105 for (int i = 0; i < count; i++) {
106 if (!files[i].delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800107 Slog.w(LOG_TAG, "Couldn't delete " + files[i] + ".");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 }
109 }
110 }
111 }
112
113 /**
114 * Dumps the current device stats to a new file.
115 */
116 private void dump() throws IOException {
117 OutputStream out = new FileOutputStream(
118 new File(BASE, String.valueOf(System.currentTimeMillis())));
119 try {
120 // Copy /proc/*/stat
121 for (File processDirectory : PROC.listFiles()) {
122 if (isProcessDirectory(processDirectory)) {
123 dump(new File(processDirectory, "stat"), out);
124 }
125 }
126
127 // Copy other files.
128 for (File file : PATHS) {
129 dump(file, out);
130 }
131 } finally {
132 closeQuietly(out);
133 }
134 }
135
136 /**
137 * Returns true if the given file represents a process directory.
138 */
139 private static boolean isProcessDirectory(File file) {
140 try {
141 Integer.parseInt(file.getName());
142 return file.isDirectory();
143 } catch (NumberFormatException e) {
144 return false;
145 }
146 }
147
148 /**
149 * Copies from a file to an output stream.
150 */
151 private void dump(File from, OutputStream out) throws IOException {
152 writeHeader(from, out);
153
154 FileInputStream in = null;
155 try {
156 in = new FileInputStream(from);
157 int count;
158 while ((count = in.read(buffer)) != -1) {
159 out.write(buffer, 0, count);
160 }
161 } finally {
162 closeQuietly(in);
163 }
164 }
165
166 /**
167 * Writes a header for the given file.
168 */
169 private static void writeHeader(File file, OutputStream out)
170 throws IOException {
171 String header = "*** " + file.toString() + "\n";
172 out.write(header.getBytes());
173 }
174
175 /**
176 * Closes the given resource. Logs exceptions.
177 * @param closeable
178 */
179 private static void closeQuietly(Closeable closeable) {
180 try {
181 if (closeable != null) {
182 closeable.close();
183 }
184 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800185 Slog.w(LOG_TAG, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 }
187 }
188
189 /**
190 * Pauses momentarily before we start the next dump.
191 */
192 private void pause() {
193 try {
194 Thread.sleep(INTERVAL);
195 } catch (InterruptedException e) { /* ignore */ }
196 }
197
198 /**
199 * Stops dumping.
200 */
201 private synchronized void stop() {
202 running = false;
203 }
204
205 /**
206 * Waits until someone starts us.
207 */
208 private synchronized void waitForStart() {
209 while (!running) {
210 try {
211 wait();
212 } catch (InterruptedException e) { /* ignore */ }
213 }
214 }
215
216 /**
217 * Instructs the monitoring to start if it hasn't already.
218 */
219 private synchronized void startMonitoring() {
220 if (!running) {
221 running = true;
222 notifyAll();
223 }
224 }
225
226 private static DeviceMonitor instance = new DeviceMonitor();
227
228 /**
229 * Starts monitoring if it hasn't started already.
230 */
231 static void start() {
232 instance.startMonitoring();
233 }
234}