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