/* * 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.Slog; 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) { Slog.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()) { Slog.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) { Slog.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(); } }