From a4cc205ee840a5374a96c9635dc5121d82a3eaf9 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 8 Jul 2013 17:31:25 -0700 Subject: More procstats work. The historical data is now a more central part of the stats. When a checkin happens, the data is not deleted, just marked as checked in so we can continue to access it. The default procstats dump is now a new "summary" mode that shows a more useful set of data for all of the running processes. By default the current and all committed states are shown; you use "--current" to only show the current. Use "--details" to get the previous more detailed data (which now includes detailed process data like the per-package data). Also tweaked uid printing to be a little more compact. Change-Id: I5414ea7c07134ebd5dc83f6f7b9f6e30151eda85 --- core/java/android/os/BatteryStats.java | 11 +- core/java/android/os/UserHandle.java | 10 +- .../android/server/am/ActivityManagerService.java | 10 +- .../java/com/android/server/am/ProcessRecord.java | 10 +- .../java/com/android/server/am/ProcessTracker.java | 444 +++++++++++++++------ 5 files changed, 355 insertions(+), 130 deletions(-) diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 8e0935da2186..12646bde77f2 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1803,8 +1803,8 @@ public abstract class BatteryStats implements Parcelable { for (int i=0; i= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { sb.append('i'); sb.append(appId - Process.FIRST_ISOLATED_UID); - } else { + } else if (appId >= Process.FIRST_APPLICATION_UID) { sb.append('a'); + sb.append(appId - Process.FIRST_APPLICATION_UID); + } else { + sb.append('s'); sb.append(appId); } } @@ -190,8 +193,11 @@ public final class UserHandle implements Parcelable { if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { pw.print('i'); pw.print(appId - Process.FIRST_ISOLATED_UID); - } else { + } else if (appId >= Process.FIRST_APPLICATION_UID) { pw.print('a'); + pw.print(appId - Process.FIRST_APPLICATION_UID); + } else { + pw.print('s'); pw.print(appId); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c957ebb380d3..39ce0c6410d2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1795,7 +1795,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mActivityManagerService.dumpProcessTracker(fd, pw, args); + mActivityManagerService.mProcessTracker.dump(fd, pw, args); } } @@ -1821,7 +1821,7 @@ public final class ActivityManagerService extends ActivityManagerNative : mBatteryStatsService.getActiveStatistics().getIsOnBattery(); mBatteryStatsService.getActiveStatistics().setCallback(this); - mProcessTracker = new ProcessTracker(new File(systemDir, "procstats")); + mProcessTracker = new ProcessTracker(this, new File(systemDir, "procstats")); mUsageStatsService = new UsageStatsService(new File(systemDir, "usagestats").toString()); mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml")); @@ -11345,12 +11345,6 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } - final void dumpProcessTracker(FileDescriptor fd, PrintWriter pw, String[] args) { - synchronized (this) { - mProcessTracker.dumpLocked(fd, pw, args); - } - } - private final boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr, boolean always) { final boolean inLaunching = mLaunchingProviders.contains(cpr); diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 1671d24b0d8e..14f410206a6b 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -425,8 +425,14 @@ final class ProcessRecord { } else { sb.append('u'); sb.append(userId); - sb.append('a'); - sb.append(UserHandle.getAppId(info.uid)); + int appId = UserHandle.getAppId(info.uid); + if (appId >= Process.FIRST_APPLICATION_UID) { + sb.append('a'); + sb.append(appId - Process.FIRST_APPLICATION_UID); + } else { + sb.append('s'); + sb.append(appId); + } if (uid != info.uid) { sb.append('i'); sb.append(UserHandle.getAppId(uid) - Process.FIRST_ISOLATED_UID); diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java index 488582d4135f..0ea93e828a92 100644 --- a/services/java/com/android/server/am/ProcessTracker.java +++ b/services/java/com/android/server/am/ProcessTracker.java @@ -140,9 +140,12 @@ public final class ProcessTracker { static final int MAX_HISTORIC_STATES = 4; // 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. static long COMMIT_PERIOD = 24*60*60*1000; // Commit current stats every day. + final Object mLock; final File mBaseDir; State mState; boolean mCommitPending; @@ -694,7 +697,7 @@ public final class ProcessTracker { mTimePeriodStartClock).toString(); if (mBaseDir != null) { mFile = new AtomicFile(new File(mBaseDir, - STATE_FILE_PREFIX + mTimePeriodStartClockStr)); + STATE_FILE_PREFIX + mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); } } @@ -1255,8 +1258,7 @@ public final class ProcessTracker { return ps; } - void dumpLocked(PrintWriter pw, String reqPackage, boolean dumpAll) { - final long now = SystemClock.uptimeMillis(); + void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) { ArrayMap> pkgMap = mPackages.getMap(); boolean printedHeader = false; for (int ip=0; ip> procMap = mProcesses.getMap(); + printedHeader = false; + for (int ip=0; ip uids = procMap.valueAt(ip); + for (int iu=0; iu filesArray = getCommittedFiles(MAX_HISTORIC_STATES); + ArrayList filesArray = getCommittedFiles(MAX_HISTORIC_STATES, true); if (filesArray == null) { return; } @@ -1803,6 +1831,76 @@ public final class ProcessTracker { pw.println(); } + static final class ProcessDataCollection { + final int[] screenStates; + final int[] memStates; + final int[] procStates; + + long totalTime; + long numPss; + long minPss; + long avgPss; + long maxPss; + + ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { + screenStates = _screenStates; + memStates = _memStates; + procStates = _procStates; + } + + void print(PrintWriter pw, boolean full) { + TimeUtils.formatDuration(totalTime, pw); + if (numPss > 0) { + pw.print(" ("); + printSizeValue(pw, minPss * 1024); + pw.print("-"); + printSizeValue(pw, avgPss * 1024); + pw.print("-"); + printSizeValue(pw, maxPss * 1024); + if (full) { + pw.print(" over "); + pw.print(numPss); + } + pw.print(")"); + } + } + } + + static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) { + data.totalTime = 0; + data.numPss = data.minPss = data.avgPss = data.maxPss = 0; + for (int is=0; is 0) { + long minPss = proc.getPssMinimum(bucket); + long avgPss = proc.getPssAverage(bucket); + long maxPss = proc.getPssMaximum(bucket); + if (data.numPss == 0) { + data.minPss = minPss; + data.avgPss = avgPss; + data.maxPss = maxPss; + } else { + if (minPss < data.minPss) { + data.minPss = minPss; + } + data.avgPss = (long)( ((data.avgPss*(double)data.numPss) + + (avgPss*(double)samples)) / (data.numPss+samples) ); + if (maxPss > data.maxPss) { + data.maxPss = maxPss; + } + } + data.numPss += samples; + } + } + } + } + } + static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, int[] procStates, long now) { long totalTime = 0; @@ -1818,7 +1916,7 @@ public final class ProcessTracker { for (int is=0; is procs, + int[] screenStates, int[] memStates, int[] procStates, long now) { + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(prefix); + pw.print("* "); + pw.print(proc.mName); + pw.print(" / "); + UserHandle.formatUid(pw, proc.mUid); + pw.println(":"); + dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates, + procStates, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates, + new int[] {STATE_PERSISTENT}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates, + new int[] {STATE_TOP}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Foreground: ", screenStates, memStates, + new int[] {STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates, + new int[] {STATE_BACKUP}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates, + new int[] {STATE_SERVICE}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates, + new int[] {STATE_HOME}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Previous: ", screenStates, memStates, + new int[] {STATE_PREVIOUS}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates, + new int[] {STATE_CACHED}, now, true); + } + } + + private static void printSizeValue(PrintWriter pw, long number) { + float result = number; + String suffix = ""; + if (result > 900) { + suffix = "KB"; + result = result / 1024; + } + if (result > 900) { + suffix = "MB"; + result = result / 1024; + } + if (result > 900) { + suffix = "GB"; + result = result / 1024; + } + if (result > 900) { + suffix = "TB"; + result = result / 1024; + } + if (result > 900) { + suffix = "PB"; + result = result / 1024; + } + String value; + if (result < 1) { + value = String.format("%.2f", result); + } else if (result < 10) { + value = String.format("%.2f", result); + } else if (result < 100) { + value = String.format("%.2f", result); + } else { + value = String.format("%.0f", result); + } + pw.print(value); + pw.print(suffix); + } + static void dumpProcessListCsv(PrintWriter pw, ArrayList procs, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now) { @@ -2171,7 +2357,7 @@ public final class ProcessTracker { 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(" [--include-committed] [--commit] [--write] [-h] []"); + pw.println(" [--details] [--current] [--commit] [--write] [-h] []"); 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."); @@ -2179,7 +2365,8 @@ public final class ProcessTracker { 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(" --include-committed: also dump any old committed states."); + pw.println(" --details: dump all execution details, not just summary."); + pw.println(" --current: only dump current state."); pw.println(" --commit: commit current stats to disk and reset to start new stats."); pw.println(" --write: write current in-memory stats to disk."); pw.println(" --read: replace current stats with last-written stats."); @@ -2188,13 +2375,14 @@ public final class ProcessTracker { pw.println(" : optional name of package to filter output by."); } - public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { final long now = SystemClock.uptimeMillis(); boolean isCheckin = false; boolean isCompact = false; boolean isCsv = false; - boolean includeCommitted = false; + boolean currentOnly = false; + boolean dumpDetails = false; boolean dumpAll = false; String reqPackage = null; boolean csvSepScreenStats = false; @@ -2264,8 +2452,10 @@ public final class ProcessTracker { return; } csvSepProcStats = sep[0]; - } else if ("--include-committed".equals(arg)) { - includeCommitted = true; + } else if ("--details".equals(arg)) { + dumpDetails = true; + } else if ("--current".equals(arg)) { + currentOnly = true; } else if ("--commit".equals(arg)) { mState.writeStateLocked(true, true); pw.println("Process stats committed."); @@ -2282,8 +2472,8 @@ public final class ProcessTracker { dumpHelp(pw); return; } else if ("-a".equals(arg)) { + dumpDetails = true; dumpAll = true; - includeCommitted = true; } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); dumpHelp(pw); @@ -2294,9 +2484,11 @@ public final class ProcessTracker { IPackageManager pm = AppGlobals.getPackageManager(); if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) { reqPackage = arg; - // We will automatically include all committed state, - // since we are going to end up with much less printed. - includeCommitted = true; + // 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; } } catch (RemoteException e) { } @@ -2330,70 +2522,94 @@ public final class ProcessTracker { } } pw.println(); - 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); - */ + synchronized (mLock) { + 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; } boolean sepNeeded = false; - if (includeCommitted || isCheckin) { - ArrayList files = getCommittedFiles(0); - if (files != null) { - for (int i=0; i files = getCommittedFiles(0, !isCheckin); + if (files != null) { + for (int i=0; i