• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.am;
18 
19 import android.util.Log;
20 
21 import java.io.*;
22 import java.util.Arrays;
23 
24 /**
25  * Monitors device resources periodically for some period of time. Useful for
26  * tracking down performance problems.
27  */
28 class 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 
DeviceMonitor()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")
monitor()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) {
68                     Log.w(LOG_TAG, "Dump failed.", e);
69                 }
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      */
purge()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()) {
103                     Log.w(LOG_TAG, "Couldn't delete " + files[i] + ".");
104                 }
105             }
106         }
107     }
108 
109     /**
110      * Dumps the current device stats to a new file.
111      */
dump()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      */
isProcessDirectory(File file)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      */
dump(File from, OutputStream out)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      */
writeHeader(File file, OutputStream out)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      */
closeQuietly(Closeable closeable)175     private static void closeQuietly(Closeable closeable) {
176         try {
177             if (closeable != null) {
178                 closeable.close();
179             }
180         } catch (IOException e) {
181             Log.w(LOG_TAG, e);
182         }
183     }
184 
185     /**
186      * Pauses momentarily before we start the next dump.
187      */
pause()188     private void pause() {
189         try {
190             Thread.sleep(INTERVAL);
191         } catch (InterruptedException e) { /* ignore */ }
192     }
193 
194     /**
195      * Stops dumping.
196      */
stop()197     private synchronized void stop() {
198         running = false;
199     }
200 
201     /**
202      * Waits until someone starts us.
203      */
waitForStart()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      */
startMonitoring()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      */
start()227     static void start() {
228         instance.startMonitoring();
229     }
230 }
231