From a17c0f5e164729210210ad3f75aea72ed34ca330 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 20 Jun 2013 18:59:11 -0700 Subject: [PATCH] More procstats work: separate global proc account, more dumping. We now keep track of the time actually process run independently of the time packages run in process, so we can give an accurate summary of how long each physical process runs. New command line options can be supplied to restrict printing to a specific package, dump in a new csv format, control what is printed in the csv format, and print a checkin report. Add toString methods to ArrayMap and ArraySet. Change-Id: I47b8f68472592ecc0088c5286d3564aa615f4e0a --- core/java/android/util/ArrayMap.java | 37 + core/java/android/util/ArraySet.java | 30 + .../server/am/ActivityManagerService.java | 7 +- .../com/android/server/am/ProcessList.java | 3 +- .../com/android/server/am/ProcessRecord.java | 8 +- .../com/android/server/am/ProcessTracker.java | 872 +++++++++++++++--- 6 files changed, 816 insertions(+), 141 deletions(-) diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 4bd9b37e154b7..26bcce4993483 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -550,6 +550,43 @@ public final class ArrayMap implements Map { return result; } + /** + * {@inheritDoc} + * + *

This implementation composes a string by iterating over its mappings. If + * this map contains itself as a key or a value, the string "(this Map)" + * will appear in its place. + */ + @Override + public String toString() { + if (isEmpty()) { + return "{}"; + } + + StringBuilder buffer = new StringBuilder(mSize * 28); + buffer.append('{'); + for (int i=0; i 0) { + buffer.append(", "); + } + Object key = keyAt(i); + if (key != this) { + buffer.append(key); + } else { + buffer.append("(this Map)"); + } + buffer.append('='); + Object value = valueAt(i); + if (value != this) { + buffer.append(value); + } else { + buffer.append("(this Map)"); + } + } + buffer.append('}'); + return buffer.toString(); + } + // ------------------------------------------------------------------------ // Interop with traditional Java containers. Not as efficient as using // specialized collection APIs. diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index a84c47c3057cc..2d3380e14982c 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -493,6 +493,36 @@ public final class ArraySet implements Collection, Set { return result; } + /** + * {@inheritDoc} + * + *

This implementation composes a string by iterating over its values. If + * this set contains itself as a value, the string "(this Set)" + * will appear in its place. + */ + @Override + public String toString() { + if (isEmpty()) { + return "{}"; + } + + StringBuilder buffer = new StringBuilder(mSize * 14); + buffer.append('{'); + for (int i=0; i 0) { + buffer.append(", "); + } + Object value = valueAt(i); + if (value != this) { + buffer.append(value); + } else { + buffer.append("(this Set)"); + } + } + buffer.append('}'); + return buffer.toString(); + } + // ------------------------------------------------------------------------ // Interop with traditional Java containers. Not as efficient as using // specialized collection APIs. diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index bdfa44709560c..6b13fc7f6295e 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -406,7 +406,7 @@ public final class ActivityManagerService extends ActivityManagerNative * Tracking long-term execution of processes to look for abuse and other * bad app behavior. */ - final ProcessTracker mProcessTracker = new ProcessTracker(); + ProcessTracker mProcessTracker; /** * The currently running isolated processes. @@ -1547,6 +1547,7 @@ public final class ActivityManagerService extends ActivityManagerNative m.mContext = context; m.mFactoryTest = factoryTest; m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface()); + m.mProcessTracker = new ProcessTracker(context); m.mStackSupervisor = new ActivityStackSupervisor(m, context, thr.mLooper); @@ -10173,12 +10174,12 @@ public final class ActivityManagerService extends ActivityManagerNative } String[] newArgs = new String[args.length - opti]; - if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); + System.arraycopy(args, opti, newArgs, 0, args.length - opti); TaskRecord lastTask = null; boolean needSep = false; for (int i=activities.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)activities.get(i); + ActivityRecord r = activities.get(i); if (needSep) { pw.println(); } diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index b5d783d341e86..7ea4a32e02ad5 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -244,7 +244,8 @@ final class ProcessList { } int adjToTrackedState(int adj) { - return adj >= FOREGROUND_APP_ADJ ? mAdjToTrackedState[adj] : ProcessTracker.STATE_NOTHING; + return adj >= FOREGROUND_APP_ADJ + ? mAdjToTrackedState[adj] : ProcessTracker.STATE_PERSISTENT; } private void writeFile(String path, String data) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index cc0a5a3e4e18b..e72656f8e3f13 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -451,8 +451,8 @@ final class ProcessRecord { ProcessList plist) { int state = this == TOP_APP ? ProcessTracker.STATE_TOP : plist.adjToTrackedState(setAdj); - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pkgList.valueAt(ip).setState(state, memFactor, now); + if (pkgList.size() > 0) { + pkgList.valueAt(0).setState(state, memFactor, now, pkgList); } } @@ -461,8 +461,8 @@ final class ProcessRecord { */ public void resetPackageList() { long now = SystemClock.uptimeMillis(); - for (int i=0; i 0) { + pkgList.valueAt(0).setState(ProcessTracker.STATE_NOTHING, 0, now, pkgList); } pkgList.clear(); pkgList.put(info.packageName, baseProcessTracker); diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java index ec8a0b24b3791..8a83412dd1493 100644 --- a/services/java/com/android/server/am/ProcessTracker.java +++ b/services/java/com/android/server/am/ProcessTracker.java @@ -16,9 +16,12 @@ package com.android.server.am; +import android.content.Context; +import android.content.pm.PackageManager; import android.os.SystemClock; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.SparseArray; import android.util.TimeUtils; import com.android.server.ProcessMap; @@ -26,20 +29,22 @@ import com.android.server.ProcessMap; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; public final class ProcessTracker { public static final int STATE_NOTHING = -1; - public static final int STATE_TOP = 0; - public static final int STATE_FOREGROUND = 1; - public static final int STATE_VISIBLE = 2; - public static final int STATE_PERCEPTIBLE = 3; - public static final int STATE_BACKUP = 4; - public static final int STATE_SERVICE = 5; - public static final int STATE_HOME = 6; - public static final int STATE_PREVIOUS = 7; - public static final int STATE_CACHED = 8; + public static final int STATE_PERSISTENT = 0; + public static final int STATE_TOP = 1; + public static final int STATE_FOREGROUND = 2; + public static final int STATE_VISIBLE = 3; + public static final int STATE_PERCEPTIBLE = 4; + public static final int STATE_BACKUP = 5; + public static final int STATE_SERVICE = 6; + public static final int STATE_HOME = 7; + public static final int STATE_PREVIOUS = 8; + public static final int STATE_CACHED = 9; public static final int STATE_COUNT = STATE_CACHED+1; public static final int ADJ_NOTHING = -1; @@ -53,12 +58,32 @@ public final class ProcessTracker { public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD; public static final int ADJ_COUNT = ADJ_SCREEN_ON*2; - static String[] STATE_NAMES = new String[] { - "Top ", "Foreground ", "Visible ", "Perceptible", "Backup ", - "Service ", "Home ", "Previous ", "Cached " + static final String[] STATE_NAMES = new String[] { + "Persistent ", "Top ", "Foreground ", "Visible ", "Perceptible", + "Backup ", "Service ", "Home ", "Previous ", "Cached " }; + static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { + "off", "on" + }; + + static final String[] ADJ_MEM_NAMES_CSV = new String[] { + "norm", "mod", "low", "crit" + }; + + static final String[] STATE_NAMES_CSV = new String[] { + "pers", "top", "fore", "vis", "percept", + "backup", "service", "home", "prev", "cached" + }; + + static final String CSV_SEP = "\t"; + + final Context mContext; + final State mState = new State(); + public static final class ProcessState { + final State mState; + final ProcessState mCommonProcess; final String mPackage; final int mUid; final String mName; @@ -67,26 +92,96 @@ public final class ProcessTracker { int mCurState = STATE_NOTHING; long mStartTime; + boolean mMultiPackage; + long mTmpTotalTime; - public ProcessState(String pkg, int uid, String name) { + /** + * Create a new top-level process state, for the initial case where there is only + * a single package running in a process. The initial state is not running. + */ + public ProcessState(State state, String pkg, int uid, String name) { + mState = state; + mCommonProcess = null; mPackage = pkg; mUid = uid; mName = name; } - public void setState(int state, int memFactor, long now) { + /** + * Create a new per-package process state for an existing top-level process + * state. The current running state of the top-level process is also copied, + * marked as started running at 'now'. + */ + public ProcessState(ProcessState commonProcess, String pkg, int uid, String name, + long now) { + mState = commonProcess.mState; + mCommonProcess = commonProcess; + mPackage = pkg; + mUid = uid; + mName = name; + mCurState = commonProcess.mCurState; + mStartTime = now; + } + + ProcessState clone(String pkg, long now) { + ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now); + pnew.add(this, now); + return pnew; + } + + public void setState(int state, int memFactor, long now, + ArrayMap pkgList) { if (state != STATE_NOTHING) { state += memFactor*STATE_COUNT; } + + if (mCommonProcess != null) { + // First update the common process. + mCommonProcess.setState(state, now); + if (!mCommonProcess.mMultiPackage) { + // This common process is for a single package, so it is shared + // with the per-package state. Nothing more to do. + return; + } + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + ProcessState proc = pkgList.valueAt(ip); + if (proc.mMultiPackage) { + // The array map is still pointing to a common process state + // that is now shared across packages. Update it to point to + // the new per-package state. + proc = mState.mPackages.get(pkgList.keyAt(ip), + proc.mUid).mProcesses.get(proc.mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process"); + } + pkgList.setValueAt(ip, proc); + } + proc.setState(state, now); + } + } + + void setState(int state, long now) { if (mCurState != state) { if (mCurState != STATE_NOTHING) { - mDurations[mCurState] += now - mStartTime; + long dur = now - mStartTime; + mDurations[mCurState] += dur; } mCurState = state; mStartTime = now; } } + + void add(ProcessState other, long now) { + for (int i=0; i<(STATE_COUNT*ADJ_COUNT); i++) { + mDurations[i] += other.mDurations[i]; + if (other.mCurState == i) { + mDurations[i] += now - other.mStartTime; + } + } + } } public static final class ServiceState { @@ -157,39 +252,78 @@ public final class ProcessTracker { static final class State { final ProcessMap mPackages = new ProcessMap(); + final ProcessMap mProcesses = new ProcessMap(); final long[] mMemFactorDurations = new long[ADJ_COUNT]; int mMemFactor = STATE_NOTHING; long mStartTime; - } - final State mState = new State(); - - public ProcessTracker() { - } - - private PackageState getPackageStateLocked(String packageName, int uid) { - PackageState as = mState.mPackages.get(packageName, uid); - if (as != null) { + PackageState getPackageStateLocked(String packageName, int uid) { + PackageState as = mPackages.get(packageName, uid); + if (as != null) { + return as; + } + as = new PackageState(uid); + mPackages.put(packageName, uid, as); return as; } - as = new PackageState(uid); - mState.mPackages.put(packageName, uid, as); - return as; + + ProcessState getProcessStateLocked(String packageName, int uid, String processName) { + final PackageState pkgState = getPackageStateLocked(packageName, uid); + ProcessState ps = pkgState.mProcesses.get(processName); + if (ps != null) { + return ps; + } + ProcessState commonProc = mProcesses.get(processName, uid); + if (commonProc == null) { + commonProc = new ProcessState(this, packageName, uid, processName); + mProcesses.put(processName, uid, commonProc); + } + if (!commonProc.mMultiPackage) { + if (packageName.equals(commonProc.mPackage)) { + // This common process is not in use by multiple packages, and + // is for the calling package, so we can just use it directly. + ps = commonProc; + } else { + // This common process has not been in use by multiple packages, + // but it was created for a different package than the caller. + // We need to convert it to a multi-package process. + commonProc.mMultiPackage = true; + // The original package it was created for now needs to point + // to its own copy. + long now = SystemClock.uptimeMillis(); + pkgState.mProcesses.put(commonProc.mPackage, commonProc.clone( + commonProc.mPackage, now)); + ps = new ProcessState(commonProc, packageName, uid, processName, now); + } + } else { + // The common process is for multiple packages, we need to create a + // separate object for the per-package data. + ps = new ProcessState(commonProc, packageName, uid, processName, + SystemClock.uptimeMillis()); + } + pkgState.mProcesses.put(processName, ps); + return ps; + } + + void reset() { + mPackages.getMap().clear(); + mProcesses.getMap().clear(); + Arrays.fill(mMemFactorDurations, 0); + mMemFactor = STATE_NOTHING; + mStartTime = 0; + } + } + + public ProcessTracker(Context context) { + mContext = context; } public ProcessState getProcessStateLocked(String packageName, int uid, String processName) { - final PackageState as = getPackageStateLocked(packageName, uid); - ProcessState ps = as.mProcesses.get(processName); - if (ps != null) { - return ps; - } - ps = new ProcessState(packageName, uid, processName); - as.mProcesses.put(processName, ps); - return ps; + return mState.getProcessStateLocked(packageName, uid, processName); } public ServiceState getServiceStateLocked(String packageName, int uid, String className) { - final PackageState as = getPackageStateLocked(packageName, uid); + final PackageState as = mState.getPackageStateLocked(packageName, uid); ServiceState ss = as.mServices.get(className); if (ss != null) { return ss; @@ -252,6 +386,22 @@ public final class ProcessTracker { } } + static private void printScreenLabelCsv(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + break; + case ADJ_SCREEN_OFF: + pw.print(ADJ_SCREEN_NAMES_CSV[0]); + break; + case ADJ_SCREEN_ON: + pw.print(ADJ_SCREEN_NAMES_CSV[1]); + break; + default: + pw.print("???"); + break; + } + } + static private void printMemLabel(PrintWriter pw, int offset) { switch (offset) { case ADJ_NOTHING: @@ -275,6 +425,16 @@ public final class ProcessTracker { } } + static private void printMemLabelCsv(PrintWriter pw, int offset) { + if (offset >= ADJ_MEM_FACTOR_NORMAL) { + if (offset <= ADJ_MEM_FACTOR_CRITICAL) { + pw.print(ADJ_MEM_NAMES_CSV[offset]); + } else { + pw.print("???"); + } + } + } + static void dumpSingleTime(PrintWriter pw, String prefix, long[] durations, int curState, long curStartTime, long now) { long totalTime = 0; @@ -310,6 +470,43 @@ public final class ProcessTracker { } } + static void dumpSingleTimeCsv(PrintWriter pw, String sep, long[] durations, + int curState, long curStartTime, long now) { + for (int iscreen=0; iscreen collectProcessesLocked(int[] screenStates, int[] memStates, - int[] procStates, long now) { - ArrayList outProcs = new ArrayList(); - ArrayMap> pmap = mState.mPackages.getMap(); - for (int ip=0; ip procs = pmap.valueAt(ip); + int[] procStates, long now, String reqPackage) { + ArraySet foundProcs = new ArraySet(); + ArrayMap> pkgMap = mState.mPackages.getMap(); + for (int ip=0; ip procs = pkgMap.valueAt(ip); for (int iu=0; iu 0) { - outProcs.add(state.mProcesses.valueAt(iproc)); + ProcessState proc = state.mProcesses.valueAt(iproc); + if (proc.mCommonProcess != null) { + proc = proc.mCommonProcess; } + foundProcs.add(proc); } } } + ArrayList outProcs = new ArrayList(foundProcs.size()); + for (int i=0; i 0) { + outProcs.add(proc); + } + } Collections.sort(outProcs, new Comparator() { @Override public int compare(ProcessState lhs, ProcessState rhs) { @@ -376,7 +585,6 @@ public final class ProcessTracker { time += now - proc.mStartTime; running = " (running)"; } - totalTime += time; if (time != 0) { pw.print(prefix); if (screenStates.length > 1) { @@ -409,26 +617,119 @@ public final class ProcessTracker { } } + void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, + int[] memStates, int[] procStates) { + final int NS = screenStates != null ? screenStates.length : 1; + final int NM = memStates != null ? memStates.length : 1; + final int NP = procStates != null ? procStates.length : 1; + for (int is=0; is 1) { + printScreenLabelCsv(pw, screenStates[is]); + printed = true; + } + if (memStates != null && memStates.length > 1) { + if (printed) { + pw.print("-"); + } + printMemLabelCsv(pw, memStates[im]); + printed = true; + } + if (procStates != null && procStates.length > 1) { + if (printed) { + pw.print("-"); + } + pw.print(STATE_NAMES_CSV[procStates[ip]]); + } + } + } + } + } + + void dumpProcessStateCsv(PrintWriter pw, ProcessState proc, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now) { + final int NSS = sepScreenStates ? screenStates.length : 1; + final int NMS = sepMemStates ? memStates.length : 1; + final int NPS = sepProcStates ? procStates.length : 1; + for (int iss=0; iss procs, int[] screenStates, int[] memStates, int[] procStates, long now) { String innerPrefix = prefix + " "; for (int i=procs.size()-1; i>=0; i--) { ProcessState proc = procs.get(i); pw.print(prefix); - pw.print(proc.mPackage); + pw.print(proc.mName); pw.print(" / "); UserHandle.formatUid(pw, proc.mUid); - pw.print(" / "); - pw.print(proc.mName); pw.println(":"); dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now); } } + void dumpProcessListCsv(PrintWriter pw, ArrayList procs, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now) { + pw.print("process"); + pw.print(CSV_SEP); + pw.print("uid"); + dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null, + sepMemStates ? memStates : null, + sepProcStates ? procStates : null); + pw.println(); + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(proc.mName); + pw.print(CSV_SEP); + UserHandle.formatUid(pw, proc.mUid); + dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates, + sepMemStates, memStates, sepProcStates, procStates, now); + pw.println(); + } + } + void dumpFilteredProcesses(PrintWriter pw, String header, String prefix, - int[] screenStates, int[] memStates, int[] procStates, long now) { + int[] screenStates, int[] memStates, int[] procStates, long now, String reqPackage) { ArrayList procs = collectProcessesLocked(screenStates, memStates, - procStates, now); + procStates, now, reqPackage); if (procs.size() > 0) { pw.println(); pw.println(header); @@ -436,100 +737,405 @@ public final class ProcessTracker { } } - public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { - final long now = SystemClock.uptimeMillis(); - ArrayMap> pmap = mState.mPackages.getMap(); - pw.println("Per-Package Process Stats:"); - for (int ip=0; ip procs = pmap.valueAt(ip); - for (int iu=0; iu procs = collectProcessesLocked(screenStates, memStates, + procStates, now, reqPackage); + if (procs.size() > 0) { + if (header != null) { + pw.println(header); + } + dumpProcessListCsv(pw, procs, sepScreenStates, screenStates, + sepMemStates, memStates, sepProcStates, procStates, now); + return true; + } + return false; + } + + void dumpAllProcessState(PrintWriter pw, String prefix, boolean isCheckin, + ProcessState proc, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen res = new ArrayList(); + 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]"); + pw.println(" --checkin: format output for a checkin report."); + 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(" --reset: reset the stats, clearing all current data."); + pw.println(" -h: print this help text."); + pw.println(" : optional name of package to filter output by."); + } + + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { + final long now = SystemClock.uptimeMillis(); + + boolean isCheckin = false; + boolean isCsv = false; + String reqPackage = null; + boolean csvSepScreenStats = false; + int[] csvScreenStats = new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}; + boolean csvSepMemStats = false; + int[] csvMemStats = new int[] {ADJ_MEM_FACTOR_CRITICAL}; + boolean csvSepProcStats = true; + int[] csvProcStats = new int[] { + STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, + STATE_PREVIOUS, STATE_CACHED }; + if (args != null) { + for (int i=0; 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(ADJ_SCREEN_NAMES_CSV, 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(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(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 ("--reset".equals(arg)) { + mState.reset(); + pw.println("Process stats reset."); + return; + } else if ("-h".equals(arg)) { + dumpHelp(pw); + return; + } else if ("-a".equals(arg)) { + // ignore + } 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. + try { + mContext.getPackageManager().getPackageUid(arg, + UserHandle.getCallingUserId()); + reqPackage = arg; + } catch (PackageManager.NameNotFoundException e) { + pw.println("Unknown package: " + arg); + dumpHelp(pw); + return; + } + } + } + } + + if (isCsv) { + pw.print("Processes running summed over"); + if (!csvSepScreenStats) { + for (int i=0; i> pkgMap = mState.mPackages.getMap(); + boolean printedHeader = false; + if (isCheckin) { + pw.println("vers,1"); + } + for (int ip=0; ip uids = pkgMap.valueAt(ip); + for (int iu=0; iu 0 || NSRVS > 0) { + if (!printedHeader) { + pw.println("Per-Package Process Stats:"); + printedHeader = true; + } + pw.print(" * "); pw.print(pkgName); pw.print(" / "); + UserHandle.formatUid(pw, uid); pw.println(":"); + } + } + for (int iproc=0; iproc> procMap = mState.mProcesses.getMap(); + for (int ip=0; ip uids = procMap.valueAt(ip); + for (int iu=0; iu