diff options
Diffstat (limited to 'services/core/java/com/android/server/am/ProcessStatsService.java')
-rw-r--r-- | services/core/java/com/android/server/am/ProcessStatsService.java | 909 |
1 files changed, 909 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java new file mode 100644 index 0000000..e05fcda --- /dev/null +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -0,0 +1,909 @@ +/* + * Copyright (C) 2013 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.app.AppGlobals; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; +import com.android.internal.app.IProcessStats; +import com.android.internal.app.ProcessStats; +import com.android.internal.os.BackgroundThread; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +public final class ProcessStatsService extends IProcessStats.Stub { + static final String TAG = "ProcessStatsService"; + static final boolean DEBUG = false; + + // Most data is kept in a sparse data structure: an integer array which integer + // holds the type of the entry, and the identifier for a long array that data + // exists in and the offset into the array to find it. The constants below + // define the encoding of that data in an integer. + + static final int MAX_HISTORIC_STATES = 8; // Maximum number of historic states we will keep. + static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames. + static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. + static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. + static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. + + final ActivityManagerService mAm; + final File mBaseDir; + ProcessStats mProcessStats; + AtomicFile mFile; + boolean mCommitPending; + boolean mShuttingDown; + int mLastMemOnlyState = -1; + boolean mMemFactorLowered; + + final ReentrantLock mWriteLock = new ReentrantLock(); + final Object mPendingWriteLock = new Object(); + AtomicFile mPendingWriteFile; + Parcel mPendingWrite; + boolean mPendingWriteCommitted; + long mLastWriteTime; + + public ProcessStatsService(ActivityManagerService am, File file) { + mAm = am; + mBaseDir = file; + mBaseDir.mkdirs(); + mProcessStats = new ProcessStats(true); + updateFile(); + SystemProperties.addChangeCallback(new Runnable() { + @Override public void run() { + synchronized (mAm) { + if (mProcessStats.evaluateSystemProperties(false)) { + mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS; + writeStateLocked(true, true); + mProcessStats.evaluateSystemProperties(true); + } + } + } + }); + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + if (!(e instanceof SecurityException)) { + Slog.wtf(TAG, "Process Stats Crash", e); + } + throw e; + } + } + + public ProcessStats.ProcessState getProcessStateLocked(String packageName, + int uid, String processName) { + return mProcessStats.getProcessStateLocked(packageName, uid, processName); + } + + public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid, + String processName, String className) { + return mProcessStats.getServiceStateLocked(packageName, uid, processName, className); + } + + public boolean isMemFactorLowered() { + return mMemFactorLowered; + } + + public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { + mMemFactorLowered = memFactor < mLastMemOnlyState; + mLastMemOnlyState = memFactor; + if (screenOn) { + memFactor += ProcessStats.ADJ_SCREEN_ON; + } + if (memFactor != mProcessStats.mMemFactor) { + if (mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING) { + mProcessStats.mMemFactorDurations[mProcessStats.mMemFactor] + += now - mProcessStats.mStartTime; + } + mProcessStats.mMemFactor = memFactor; + mProcessStats.mStartTime = now; + ArrayMap<String, SparseArray<ProcessStats.PackageState>> pmap + = mProcessStats.mPackages.getMap(); + for (int i=0; i<pmap.size(); i++) { + SparseArray<ProcessStats.PackageState> uids = pmap.valueAt(i); + for (int j=0; j<uids.size(); j++) { + ProcessStats.PackageState pkg = uids.valueAt(j); + ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices; + for (int k=0; k<services.size(); k++) { + ProcessStats.ServiceState service = services.valueAt(k); + if (service.isInUse()) { + if (service.mStartedState != ProcessStats.STATE_NOTHING) { + service.setStarted(true, memFactor, now); + } + if (service.mBoundState != ProcessStats.STATE_NOTHING) { + service.setBound(true, memFactor, now); + } + if (service.mExecState != ProcessStats.STATE_NOTHING) { + service.setExecuting(true, memFactor, now); + } + } + } + } + } + return true; + } + return false; + } + + public int getMemFactorLocked() { + return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0; + } + + public boolean shouldWriteNowLocked(long now) { + if (now > (mLastWriteTime+WRITE_PERIOD)) { + if (SystemClock.elapsedRealtime() + > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) { + mCommitPending = true; + } + return true; + } + return false; + } + + public void shutdownLocked() { + Slog.w(TAG, "Writing process stats before shutdown..."); + mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN; + writeStateSyncLocked(); + mShuttingDown = true; + } + + public void writeStateAsyncLocked() { + writeStateLocked(false); + } + + public void writeStateSyncLocked() { + writeStateLocked(true); + } + + private void writeStateLocked(boolean sync) { + if (mShuttingDown) { + return; + } + boolean commitPending = mCommitPending; + mCommitPending = false; + writeStateLocked(sync, commitPending); + } + + public void writeStateLocked(boolean sync, final boolean commit) { + synchronized (mPendingWriteLock) { + long now = SystemClock.uptimeMillis(); + if (mPendingWrite == null || !mPendingWriteCommitted) { + mPendingWrite = Parcel.obtain(); + mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); + if (commit) { + mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; + } + mProcessStats.writeToParcel(mPendingWrite, 0); + mPendingWriteFile = new AtomicFile(mFile.getBaseFile()); + mPendingWriteCommitted = commit; + } + if (commit) { + mProcessStats.resetSafely(); + updateFile(); + } + mLastWriteTime = SystemClock.uptimeMillis(); + Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms"); + if (!sync) { + BackgroundThread.getHandler().post(new Runnable() { + @Override public void run() { + performWriteState(); + } + }); + return; + } + } + + performWriteState(); + } + + private void updateFile() { + mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX + + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); + mLastWriteTime = SystemClock.uptimeMillis(); + } + + void performWriteState() { + if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()); + Parcel data; + AtomicFile file; + synchronized (mPendingWriteLock) { + data = mPendingWrite; + file = mPendingWriteFile; + mPendingWriteCommitted = false; + if (data == null) { + return; + } + mPendingWrite = null; + mPendingWriteFile = null; + mWriteLock.lock(); + } + + FileOutputStream stream = null; + try { + stream = file.startWrite(); + stream.write(data.marshall()); + stream.flush(); + file.finishWrite(stream); + if (DEBUG) Slog.d(TAG, "Write completed successfully!"); + } catch (IOException e) { + Slog.w(TAG, "Error writing process statistics", e); + file.failWrite(stream); + } finally { + data.recycle(); + trimHistoricStatesWriteLocked(); + mWriteLock.unlock(); + } + } + + boolean readLocked(ProcessStats stats, AtomicFile file) { + try { + FileInputStream stream = file.openRead(); + stats.read(stream); + stream.close(); + if (stats.mReadError != null) { + Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError); + if (DEBUG) { + ArrayMap<String, SparseArray<ProcessStats.ProcessState>> procMap + = stats.mProcesses.getMap(); + final int NPROC = procMap.size(); + for (int ip=0; ip<NPROC; ip++) { + Slog.w(TAG, "Process: " + procMap.keyAt(ip)); + SparseArray<ProcessStats.ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu)); + } + } + ArrayMap<String, SparseArray<ProcessStats.PackageState>> pkgMap + = stats.mPackages.getMap(); + final int NPKG = pkgMap.size(); + for (int ip=0; ip<NPKG; ip++) { + Slog.w(TAG, "Package: " + pkgMap.keyAt(ip)); + SparseArray<ProcessStats.PackageState> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + Slog.w(TAG, " Uid: " + uids.keyAt(iu)); + ProcessStats.PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc) + + ": " + pkgState.mProcesses.valueAt(iproc)); + } + final int NSRVS = pkgState.mServices.size(); + for (int isvc=0; isvc<NSRVS; isvc++) { + Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc) + + ": " + pkgState.mServices.valueAt(isvc)); + } + } + } + } + return false; + } + } catch (Throwable e) { + stats.mReadError = "caught exception: " + e; + Slog.e(TAG, "Error reading process statistics", e); + return false; + } + return true; + } + + private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent, + boolean inclCheckedIn) { + File[] files = mBaseDir.listFiles(); + if (files == null || files.length <= minNum) { + return null; + } + ArrayList<String> filesArray = new ArrayList<String>(files.length); + String currentFile = mFile.getBaseFile().getPath(); + if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile); + for (int i=0; i<files.length; i++) { + File file = files[i]; + String fileStr = file.getPath(); + if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr); + if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) { + if (DEBUG) Slog.d(TAG, "Skipping: already checked in"); + continue; + } + if (!inclCurrent && fileStr.equals(currentFile)) { + if (DEBUG) Slog.d(TAG, "Skipping: current stats"); + continue; + } + filesArray.add(fileStr); + } + Collections.sort(filesArray); + return filesArray; + } + + public void trimHistoricStatesWriteLocked() { + ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true); + if (filesArray == null) { + return; + } + while (filesArray.size() > MAX_HISTORIC_STATES) { + String file = filesArray.remove(0); + Slog.i(TAG, "Pruning old procstats: " + file); + (new File(file)).delete(); + } + } + + boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now, String reqPackage) { + ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked( + screenStates, memStates, procStates, procStates, now, reqPackage, false); + if (procs.size() > 0) { + if (header != null) { + pw.println(header); + } + ProcessStats.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates, + sepMemStates, memStates, sepProcStates, procStates, now); + return true; + } + return false; + } + + static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep, + String[] outError) { + ArrayList<Integer> res = new ArrayList<Integer>(); + int lastPos = 0; + for (int i=0; i<=arg.length(); i++) { + char c = i < arg.length() ? arg.charAt(i) : 0; + if (c != ',' && c != '+' && c != ' ' && c != 0) { + continue; + } + boolean isSep = c == ','; + if (lastPos == 0) { + // We now know the type of op. + outSep[0] = isSep; + } else if (c != 0 && outSep[0] != isSep) { + outError[0] = "inconsistent separators (can't mix ',' with '+')"; + return null; + } + if (lastPos < (i-1)) { + String str = arg.substring(lastPos, i); + for (int j=0; j<states.length; j++) { + if (str.equals(states[j])) { + res.add(j); + str = null; + break; + } + } + if (str != null) { + outError[0] = "invalid word \"" + str + "\""; + return null; + } + } + lastPos = i + 1; + } + + int[] finalRes = new int[res.size()]; + for (int i=0; i<res.size(); i++) { + finalRes[i] = res.get(i) * mult; + } + return finalRes; + } + + public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) { + mAm.mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS, null); + Parcel current = Parcel.obtain(); + mWriteLock.lock(); + try { + synchronized (mAm) { + mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); + mProcessStats.writeToParcel(current, 0); + } + if (historic != null) { + ArrayList<String> files = getCommittedFiles(0, false, true); + if (files != null) { + for (int i=files.size()-1; i>=0; i--) { + try { + ParcelFileDescriptor pfd = ParcelFileDescriptor.open( + new File(files.get(i)), ParcelFileDescriptor.MODE_READ_ONLY); + historic.add(pfd); + } catch (IOException e) { + Slog.w(TAG, "Failure opening procstat file " + files.get(i), e); + } + } + } + } + } finally { + mWriteLock.unlock(); + } + return current.marshall(); + } + + public ParcelFileDescriptor getStatsOverTime(long minTime) { + mAm.mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS, null); + mWriteLock.lock(); + try { + Parcel current = Parcel.obtain(); + long curTime; + synchronized (mAm) { + mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); + mProcessStats.writeToParcel(current, 0); + curTime = mProcessStats.mTimePeriodEndRealtime + - mProcessStats.mTimePeriodStartRealtime; + } + if (curTime < minTime) { + // Need to add in older stats to reach desired time. + ArrayList<String> files = getCommittedFiles(0, false, true); + if (files != null && files.size() > 0) { + current.setDataPosition(0); + ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current); + current.recycle(); + int i = files.size()-1; + while (i >= 0 && (stats.mTimePeriodEndRealtime + - stats.mTimePeriodStartRealtime) < minTime) { + AtomicFile file = new AtomicFile(new File(files.get(i))); + i--; + ProcessStats moreStats = new ProcessStats(false); + readLocked(moreStats, file); + if (moreStats.mReadError == null) { + stats.add(moreStats); + StringBuilder sb = new StringBuilder(); + sb.append("Added stats: "); + sb.append(moreStats.mTimePeriodStartClockStr); + sb.append(", over "); + TimeUtils.formatDuration(moreStats.mTimePeriodEndRealtime + - moreStats.mTimePeriodStartRealtime, sb); + Slog.i(TAG, sb.toString()); + } else { + Slog.w(TAG, "Failure reading " + files.get(i+1) + "; " + + moreStats.mReadError); + continue; + } + } + current = Parcel.obtain(); + stats.writeToParcel(current, 0); + } + } + final byte[] outData = current.marshall(); + current.recycle(); + final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); + Thread thr = new Thread("ProcessStats pipe output") { + public void run() { + FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]); + try { + fout.write(outData); + fout.close(); + } catch (IOException e) { + Slog.w(TAG, "Failure writing pipe", e); + } + } + }; + thr.start(); + return fds[0]; + } catch (IOException e) { + Slog.w(TAG, "Failed building output pipe", e); + } finally { + mWriteLock.unlock(); + } + return null; + } + + public int getCurrentMemoryState() { + synchronized (mAm) { + return mLastMemOnlyState; + } + } + + private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now, + String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails, + boolean dumpAll, boolean activeOnly) { + ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000 + - (ProcessStats.COMMIT_PERIOD/2)); + if (pfd == null) { + pw.println("Unable to build stats!"); + return; + } + ProcessStats stats = new ProcessStats(false); + InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + stats.read(stream); + if (stats.mReadError != null) { + pw.print("Failure reading: "); pw.println(stats.mReadError); + return; + } + if (isCompact) { + stats.dumpCheckinLocked(pw, reqPackage); + } else { + if (dumpDetails || dumpFullDetails) { + stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly); + } else { + stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); + } + } + } + + static private void dumpHelp(PrintWriter pw) { + pw.println("Process stats (procstats) dump options:"); + pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]"); + pw.println(" [--details] [--full-details] [--current] [--hours] [--active]"); + pw.println(" [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]"); + pw.println(" --checkin: perform a checkin: print and delete old committed states."); + pw.println(" --c: print only state in checkin format."); + pw.println(" --csv: output data suitable for putting in a spreadsheet."); + pw.println(" --csv-screen: on, off."); + pw.println(" --csv-mem: norm, mod, low, crit."); + pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,"); + pw.println(" service, home, prev, cached"); + pw.println(" --details: dump per-package details, not just summary."); + pw.println(" --full-details: dump all timing and active state details."); + pw.println(" --current: only dump current state."); + pw.println(" --hours: aggregate over about N last hours."); + pw.println(" --active: only show currently active processes/services."); + pw.println(" --commit: commit current stats to disk and reset to start new stats."); + pw.println(" --reset: reset current stats, without committing."); + pw.println(" --clear: clear all stats; does both --reset and deletes old stats."); + pw.println(" --write: write current in-memory stats to disk."); + pw.println(" --read: replace current stats with last-written stats."); + pw.println(" -a: print everything."); + pw.println(" -h: print this help text."); + pw.println(" <package.name>: optional name of package to filter output by."); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mAm.checkCallingPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump procstats from from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + + " without permission " + android.Manifest.permission.DUMP); + return; + } + + long ident = Binder.clearCallingIdentity(); + try { + dumpInner(fd, pw, args); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) { + final long now = SystemClock.uptimeMillis(); + + boolean isCheckin = false; + boolean isCompact = false; + boolean isCsv = false; + boolean currentOnly = false; + boolean dumpDetails = false; + boolean dumpFullDetails = false; + boolean dumpAll = false; + int aggregateHours = 0; + boolean activeOnly = false; + String reqPackage = null; + boolean csvSepScreenStats = false; + int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON}; + boolean csvSepMemStats = false; + int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL}; + boolean csvSepProcStats = true; + int[] csvProcStats = ProcessStats.ALL_PROC_STATES; + if (args != null) { + for (int i=0; i<args.length; i++) { + String arg = args[i]; + if ("--checkin".equals(arg)) { + isCheckin = true; + } else if ("-c".equals(arg)) { + isCompact = true; + } else if ("--csv".equals(arg)) { + isCsv = true; + } else if ("--csv-screen".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --csv-screen"); + dumpHelp(pw); + return; + } + boolean[] sep = new boolean[1]; + String[] error = new String[1]; + csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD, + args[i], sep, error); + if (csvScreenStats == null) { + pw.println("Error in \"" + args[i] + "\": " + error[0]); + dumpHelp(pw); + return; + } + csvSepScreenStats = sep[0]; + } else if ("--csv-mem".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --csv-mem"); + dumpHelp(pw); + return; + } + boolean[] sep = new boolean[1]; + String[] error = new String[1]; + csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error); + if (csvMemStats == null) { + pw.println("Error in \"" + args[i] + "\": " + error[0]); + dumpHelp(pw); + return; + } + csvSepMemStats = sep[0]; + } else if ("--csv-proc".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --csv-proc"); + dumpHelp(pw); + return; + } + boolean[] sep = new boolean[1]; + String[] error = new String[1]; + csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error); + if (csvProcStats == null) { + pw.println("Error in \"" + args[i] + "\": " + error[0]); + dumpHelp(pw); + return; + } + csvSepProcStats = sep[0]; + } else if ("--details".equals(arg)) { + dumpDetails = true; + } else if ("--full-details".equals(arg)) { + dumpFullDetails = true; + } else if ("--hours".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --hours"); + dumpHelp(pw); + return; + } + try { + aggregateHours = Integer.parseInt(args[i]); + } catch (NumberFormatException e) { + pw.println("Error: --hours argument not an int -- " + args[i]); + dumpHelp(pw); + return; + } + } else if ("--active".equals(arg)) { + activeOnly = true; + currentOnly = true; + } else if ("--current".equals(arg)) { + currentOnly = true; + } else if ("--commit".equals(arg)) { + synchronized (mAm) { + mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; + writeStateLocked(true, true); + pw.println("Process stats committed."); + } + return; + } else if ("--reset".equals(arg)) { + synchronized (mAm) { + mProcessStats.resetSafely(); + pw.println("Process stats reset."); + } + return; + } else if ("--clear".equals(arg)) { + synchronized (mAm) { + mProcessStats.resetSafely(); + ArrayList<String> files = getCommittedFiles(0, true, true); + if (files != null) { + for (int fi=0; fi<files.size(); fi++) { + (new File(files.get(fi))).delete(); + } + } + pw.println("All process stats cleared."); + } + return; + } else if ("--write".equals(arg)) { + synchronized (mAm) { + writeStateSyncLocked(); + pw.println("Process stats written."); + } + return; + } else if ("--read".equals(arg)) { + synchronized (mAm) { + readLocked(mProcessStats, mFile); + pw.println("Process stats read."); + } + return; + } else if ("-h".equals(arg)) { + dumpHelp(pw); + return; + } else if ("-a".equals(arg)) { + dumpDetails = true; + dumpAll = true; + } else if (arg.length() > 0 && arg.charAt(0) == '-'){ + pw.println("Unknown option: " + arg); + dumpHelp(pw); + return; + } else { + // Not an option, last argument must be a package name. + reqPackage = arg; + // Include all details, since we know we are only going to + // be dumping a smaller set of data. In fact only the details + // container per-package data, so that are needed to be able + // to dump anything at all when filtering by package. + dumpDetails = true; + } + } + } + + if (isCsv) { + pw.print("Processes running summed over"); + if (!csvSepScreenStats) { + for (int i=0; i<csvScreenStats.length; i++) { + pw.print(" "); + ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]); + } + } + if (!csvSepMemStats) { + for (int i=0; i<csvMemStats.length; i++) { + pw.print(" "); + ProcessStats.printMemLabelCsv(pw, csvMemStats[i]); + } + } + if (!csvSepProcStats) { + for (int i=0; i<csvProcStats.length; i++) { + pw.print(" "); + pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]); + } + } + pw.println(); + synchronized (mAm) { + dumpFilteredProcessesCsvLocked(pw, null, + csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, + csvSepProcStats, csvProcStats, now, reqPackage); + /* + dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", + false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, + true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, + true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, + STATE_PREVIOUS, STATE_CACHED}, + now, reqPackage); + dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", + false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, + false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, + ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, + true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, + STATE_PREVIOUS, STATE_CACHED}, + now, reqPackage); + */ + } + return; + } else if (aggregateHours != 0) { + pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:"); + dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact, + dumpDetails, dumpFullDetails, dumpAll, activeOnly); + return; + } + + boolean sepNeeded = false; + if (dumpAll || isCheckin) { + mWriteLock.lock(); + try { + ArrayList<String> files = getCommittedFiles(0, false, !isCheckin); + if (files != null) { + for (int i=0; i<files.size(); i++) { + if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); + try { + AtomicFile file = new AtomicFile(new File(files.get(i))); + ProcessStats processStats = new ProcessStats(false); + readLocked(processStats, file); + if (processStats.mReadError != null) { + if (isCheckin || isCompact) pw.print("err,"); + pw.print("Failure reading "); pw.print(files.get(i)); + pw.print("; "); pw.println(processStats.mReadError); + if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); + (new File(files.get(i))).delete(); + continue; + } + String fileStr = file.getBaseFile().getPath(); + boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); + if (isCheckin || isCompact) { + // Don't really need to lock because we uniquely own this object. + processStats.dumpCheckinLocked(pw, reqPackage); + } else { + if (sepNeeded) { + pw.println(); + } else { + sepNeeded = true; + } + pw.print("COMMITTED STATS FROM "); + pw.print(processStats.mTimePeriodStartClockStr); + if (checkedIn) pw.print(" (checked in)"); + pw.println(":"); + // Don't really need to lock because we uniquely own this object. + // Always dump summary here, dumping all details is just too + // much crud. + if (dumpFullDetails) { + mProcessStats.dumpLocked(pw, reqPackage, now, false, false, + activeOnly); + } else { + processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); + } + } + if (isCheckin) { + // Rename file suffix to mark that it has checked in. + file.getBaseFile().renameTo(new File( + fileStr + STATE_FILE_CHECKIN_SUFFIX)); + } + } catch (Throwable e) { + pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); + e.printStackTrace(pw); + } + } + } + } finally { + mWriteLock.unlock(); + } + } + if (!isCheckin) { + if (!currentOnly) { + if (sepNeeded) { + pw.println(); + } + pw.println("AGGREGATED OVER LAST 24 HOURS:"); + dumpAggregatedStats(pw, 24, now, reqPackage, isCompact, + dumpDetails, dumpFullDetails, dumpAll, activeOnly); + pw.println(); + pw.println("AGGREGATED OVER LAST 3 HOURS:"); + dumpAggregatedStats(pw, 3, now, reqPackage, isCompact, + dumpDetails, dumpFullDetails, dumpAll, activeOnly); + sepNeeded = true; + } + synchronized (mAm) { + if (isCompact) { + mProcessStats.dumpCheckinLocked(pw, reqPackage); + } else { + if (sepNeeded) { + pw.println(); + } + pw.println("CURRENT STATS:"); + if (dumpDetails || dumpFullDetails) { + mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, + activeOnly); + if (dumpAll) { + pw.print(" mFile="); pw.println(mFile.getBaseFile()); + } + } else { + mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); + } + } + } + } + } +} |