diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /services/java/com/android/server/am/DeviceMonitor.java | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'services/java/com/android/server/am/DeviceMonitor.java')
-rw-r--r-- | services/java/com/android/server/am/DeviceMonitor.java | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/services/java/com/android/server/am/DeviceMonitor.java b/services/java/com/android/server/am/DeviceMonitor.java new file mode 100644 index 0000000..ce07430 --- /dev/null +++ b/services/java/com/android/server/am/DeviceMonitor.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.util.Log; + +import java.io.*; +import java.util.Arrays; + +/** + * Monitors device resources periodically for some period of time. Useful for + * tracking down performance problems. + */ +class DeviceMonitor { + + private static final String LOG_TAG = DeviceMonitor.class.getName(); + + /** Number of samples to take. */ + private static final int SAMPLE_COUNT = 10; + + /** Time to wait in ms between samples. */ + private static final int INTERVAL = 1000; + + /** Time to wait in ms between samples. */ + private static final int MAX_FILES = 30; + + private final byte[] buffer = new byte[1024]; + + /** Is the monitor currently running? */ + private boolean running = false; + + private DeviceMonitor() { + new Thread() { + public void run() { + monitor(); + } + }.start(); + } + + /** + * Loops continuously. Pauses until someone tells us to start monitoring. + */ + @SuppressWarnings("InfiniteLoopStatement") + private void monitor() { + while (true) { + waitForStart(); + + purge(); + + for (int i = 0; i < SAMPLE_COUNT; i++) { + try { + dump(); + } catch (IOException e) { + Log.w(LOG_TAG, "Dump failed.", e); + } + pause(); + } + + stop(); + } + } + + private static final File PROC = new File("/proc"); + private static final File BASE = new File("/data/anr/"); + static { + if (!BASE.isDirectory() && !BASE.mkdirs()) { + throw new AssertionError("Couldn't create " + BASE + "."); + } + } + + private static final File[] PATHS = { + new File(PROC, "zoneinfo"), + new File(PROC, "interrupts"), + new File(PROC, "meminfo"), + new File(PROC, "slabinfo"), + }; + + + /** + * Deletes old files. + */ + private void purge() { + File[] files = BASE.listFiles(); + int count = files.length - MAX_FILES; + if (count > 0) { + Arrays.sort(files); + for (int i = 0; i < count; i++) { + if (!files[i].delete()) { + Log.w(LOG_TAG, "Couldn't delete " + files[i] + "."); + } + } + } + } + + /** + * Dumps the current device stats to a new file. + */ + private void dump() throws IOException { + OutputStream out = new FileOutputStream( + new File(BASE, String.valueOf(System.currentTimeMillis()))); + try { + // Copy /proc/*/stat + for (File processDirectory : PROC.listFiles()) { + if (isProcessDirectory(processDirectory)) { + dump(new File(processDirectory, "stat"), out); + } + } + + // Copy other files. + for (File file : PATHS) { + dump(file, out); + } + } finally { + closeQuietly(out); + } + } + + /** + * Returns true if the given file represents a process directory. + */ + private static boolean isProcessDirectory(File file) { + try { + Integer.parseInt(file.getName()); + return file.isDirectory(); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Copies from a file to an output stream. + */ + private void dump(File from, OutputStream out) throws IOException { + writeHeader(from, out); + + FileInputStream in = null; + try { + in = new FileInputStream(from); + int count; + while ((count = in.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + } finally { + closeQuietly(in); + } + } + + /** + * Writes a header for the given file. + */ + private static void writeHeader(File file, OutputStream out) + throws IOException { + String header = "*** " + file.toString() + "\n"; + out.write(header.getBytes()); + } + + /** + * Closes the given resource. Logs exceptions. + * @param closeable + */ + private static void closeQuietly(Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (IOException e) { + Log.w(LOG_TAG, e); + } + } + + /** + * Pauses momentarily before we start the next dump. + */ + private void pause() { + try { + Thread.sleep(INTERVAL); + } catch (InterruptedException e) { /* ignore */ } + } + + /** + * Stops dumping. + */ + private synchronized void stop() { + running = false; + } + + /** + * Waits until someone starts us. + */ + private synchronized void waitForStart() { + while (!running) { + try { + wait(); + } catch (InterruptedException e) { /* ignore */ } + } + } + + /** + * Instructs the monitoring to start if it hasn't already. + */ + private synchronized void startMonitoring() { + if (!running) { + running = true; + notifyAll(); + } + } + + private static DeviceMonitor instance = new DeviceMonitor(); + + /** + * Starts monitoring if it hasn't started already. + */ + static void start() { + instance.startMonitoring(); + } +} |