From 78a369c90f61981c9d0ddd5fcbc77202bfbe74f6 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 11 Jun 2013 17:10:32 -0700 Subject: [PATCH] Improve process tracking. We now keep track of when each process is running, batched by the current memory status of the device. In addition, the stats are organized by package first, and then processes associated with each package inside of that. Finally, we also keep track of the overall time spent in each memory status. This should start to actually get us to some information we can reach some conclusions about. The total time spent in each memory status gives us some indication of how much we are running while memory is low; the new package organization batched by memory status lets us see what packages have what processes running when memory is low. Change-Id: I389d62d39d115a846126cf354e4c20070d8f1180 --- .../com/android/server/am/ActiveServices.java | 2 +- .../server/am/ActivityManagerService.java | 98 ++++--- .../server/am/ActivityStackSupervisor.java | 2 +- .../com/android/server/am/BroadcastQueue.java | 2 +- .../android/server/am/CompatModePackages.java | 2 +- .../com/android/server/am/ProcessList.java | 2 +- .../com/android/server/am/ProcessRecord.java | 39 ++- .../com/android/server/am/ProcessTracker.java | 239 ++++++++++++++++-- 8 files changed, 306 insertions(+), 80 deletions(-) diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index a5d64b2c89dee..912c465b5c162 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -1015,7 +1015,7 @@ public final class ActiveServices { Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); if (app != null && app.thread != null) { try { - app.addPackage(r.appInfo.packageName); + app.addPackage(r.appInfo.packageName, mAm.mProcessTracker); realStartServiceLocked(r, app); return null; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 38fe5e6a65cfb..2124095502f3f 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -817,10 +817,10 @@ public final class ActivityManagerService extends ActivityManagerNative int mNumNonCachedProcs = 0; /** - * Keep track of the number of cached procs, to balance oom adj + * Keep track of the number of cached hidden procs, to balance oom adj * distribution between those and empty procs. */ - int mNumCachedProcs = 0; + int mNumCachedHiddenProcs = 0; /** * Keep track of the number of service processes we last found, to @@ -2151,7 +2151,7 @@ public final class ActivityManagerService extends ActivityManagerNative // come up (we have a pid but not yet its thread), so keep it. if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app); // If this is a new package in the process, add the package to the list - app.addPackage(info.packageName); + app.addPackage(info.packageName, mProcessTracker); return app; } @@ -2206,7 +2206,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } else { // If this is a new package in the process, add the package to the list - app.addPackage(info.packageName); + app.addPackage(info.packageName, mProcessTracker); } // If the system is not ready yet, then hold off on starting this @@ -3039,10 +3039,8 @@ public final class ActivityManagerService extends ActivityManagerNative proc = p; break; } - for (String str : p.pkgList) { - if (str.equals(packageName)) { - proc = p; - } + if (p.pkgList.containsKey(packageName)) { + proc = p; } } } @@ -4014,7 +4012,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (userId != UserHandle.USER_ALL && app.userId != userId) { continue; } - if (!app.pkgList.contains(packageName)) { + if (!app.pkgList.containsKey(packageName)) { continue; } } @@ -6220,7 +6218,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.userId != tr.userId) { continue; } - if (!proc.pkgList.contains(pkg)) { + if (!proc.pkgList.containsKey(pkg)) { continue; } procs.add(proc); @@ -6577,7 +6575,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_MU) Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid); app.pubProviders.put(cpi.name, cpr); - app.addPackage(cpi.applicationInfo.packageName); + app.addPackage(cpi.applicationInfo.packageName, mProcessTracker); ensurePackageDexOpt(cpi.applicationInfo.packageName); } } @@ -7316,7 +7314,7 @@ public final class ActivityManagerService extends ActivityManagerNative ps = stats.getProcessStatsLocked(info.uid, proc); } return new ProcessRecord(ps, thread, info, proc, uid, - mProcessTracker.getStateLocked(info.processName, info.uid)); + mProcessTracker.getProcessStateLocked(info.packageName, uid, proc)); } final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) { @@ -8895,7 +8893,8 @@ public final class ActivityManagerService extends ActivityManagerNative int flags = process.info.flags; IPackageManager pm = AppGlobals.getPackageManager(); sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n"); - for (String pkg : process.pkgList) { + for (int ip=0; ip= ProcessList.FOREGROUND_APP_ADJ) { - app.tracker.setState(mProcessList.adjToTrackedState(app.setAdj), - SystemClock.uptimeMillis()); + app.setAdjChanged = true; + if (!doingAll) { + app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactor(), + now, mProcessList); } } else { success = false; @@ -13997,7 +13997,7 @@ public final class ActivityManagerService extends ActivityManagerNative mAdjSeq++; boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj, - app.emptyAdj, TOP_APP, false); + app.emptyAdj, TOP_APP, false, SystemClock.uptimeMillis()); final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ; if (nowCached != wasCached) { @@ -14011,7 +14011,8 @@ public final class ActivityManagerService extends ActivityManagerNative final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; - final long oldTime = SystemClock.uptimeMillis() - ProcessList.MAX_EMPTY_TIME; + final long now = SystemClock.uptimeMillis(); + final long oldTime = now - ProcessList.MAX_EMPTY_TIME; if (false) { RuntimeException e = new RuntimeException(); @@ -14040,7 +14041,7 @@ public final class ActivityManagerService extends ActivityManagerNative // them. int numSlots = (ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2; - int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedProcs; + int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedHiddenProcs; if (numEmptyProcs > cachedProcessLimit) { // If there are more empty processes than our limit on cached // processes, then use the cached process limit for the factor. @@ -14052,7 +14053,7 @@ public final class ActivityManagerService extends ActivityManagerNative } int emptyFactor = numEmptyProcs/numSlots; if (emptyFactor < 1) emptyFactor = 1; - int cachedFactor = (mNumCachedProcs > 0 ? mNumCachedProcs : 1)/numSlots; + int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots; if (cachedFactor < 1) cachedFactor = 1; int stepCached = 0; int stepEmpty = 0; @@ -14061,7 +14062,7 @@ public final class ActivityManagerService extends ActivityManagerNative int numTrimming = 0; mNumNonCachedProcs = 0; - mNumCachedProcs = 0; + mNumCachedHiddenProcs = 0; // First update the OOM adjustment for each of the // application processes based on their current state. @@ -14071,16 +14072,20 @@ public final class ActivityManagerService extends ActivityManagerNative int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ; int nextEmptyAdj = curEmptyAdj+2; int curClientCachedAdj = curEmptyAdj; + boolean changed = false; while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj); - updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP, true); + app.setAdjChanged = false; + updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP, + true, now); + changed |= app.setAdjChanged; if (!app.killedBackground) { if (app.curRawAdj == curCachedAdj && app.hasActivities) { // This process was assigned as a cached process... step the // cached level. - mNumCachedProcs++; + mNumCachedHiddenProcs++; if (curCachedAdj != nextCachedAdj) { stepCached++; if (stepCached >= cachedFactor) { @@ -14188,6 +14193,7 @@ public final class ActivityManagerService extends ActivityManagerNative // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. + int memFactor = ProcessTracker.STATE_MEM_FACTOR_NORMAL_ADJ; if (numCached <= ProcessList.TRIM_CACHED_APPS && numEmpty <= ProcessList.TRIM_EMPTY_APPS) { final int numCachedAndEmpty = numCached + numEmpty; @@ -14201,10 +14207,13 @@ public final class ActivityManagerService extends ActivityManagerNative int fgTrimLevel; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; + memFactor = ProcessTracker.STATE_MEM_FACTOR_CRITIAL_ADJ; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; + memFactor = ProcessTracker.STATE_MEM_FACTOR_LOW_ADJ; } else { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; + memFactor = ProcessTracker.STATE_MEM_FACTOR_MODERATE_ADJ; } int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; for (i=0; i=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + if (allChanged || app.setAdjChanged) { + app.setProcessTrackerState(TOP_APP, memFactor, now, mProcessList); + } + } + } + + if (DEBUG_OOM_ADJ) { + Slog.d(TAG, "Did OOM ADJ in " + (SystemClock.uptimeMillis()-now) + "ms"); + } } final void trimApplications() { diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 6fe28f4c884ea..ba8de39f9b938 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -987,7 +987,7 @@ public final class ActivityStackSupervisor { if (app != null && app.thread != null) { try { - app.addPackage(r.info.packageName); + app.addPackage(r.info.packageName, mService.mProcessTracker); realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index c7e6010cf445d..0e7513c926a9d 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -795,7 +795,7 @@ public final class BroadcastQueue { info.activityInfo.applicationInfo.uid); if (app != null && app.thread != null) { try { - app.addPackage(info.activityInfo.packageName); + app.addPackage(info.activityInfo.packageName, mService.mProcessTracker); processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java index f56371c266394..59e678738444f 100644 --- a/services/java/com/android/server/am/CompatModePackages.java +++ b/services/java/com/android/server/am/CompatModePackages.java @@ -300,7 +300,7 @@ public final class CompatModePackages { // Tell all processes that loaded this package about the change. for (int i=mService.mLruProcesses.size()-1; i>=0; i--) { ProcessRecord app = mService.mLruProcesses.get(i); - if (!app.pkgList.contains(packageName)) { + if (!app.pkgList.containsKey(packageName)) { continue; } try { diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index e937d3521ddc3..b5d783d341e86 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -244,7 +244,7 @@ final class ProcessList { } int adjToTrackedState(int adj) { - return mAdjToTrackedState[adj]; + return adj >= FOREGROUND_APP_ADJ ? mAdjToTrackedState[adj] : ProcessTracker.STATE_NOTHING; } 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 6cae67c07d730..cc0a5a3e4e18b 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -51,9 +51,10 @@ final class ProcessRecord { final int uid; // uid of process; may be different from 'info' if isolated final int userId; // user of process. final String processName; // name of the process - final ProcessTracker.ProcessState tracker; // tracking execution of process + final ProcessTracker.ProcessState baseProcessTracker; // List of packages running in the process - final HashSet pkgList = new HashSet(); + final ArrayMap pkgList + = new ArrayMap(); IApplicationThread thread; // the actual proc... may be null only if // 'persistent' is true (in which case we // are in the process of launching the app) @@ -87,6 +88,7 @@ final class ProcessRecord { boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower boolean bad; // True if disabled in the bad process list boolean killedBackground; // True when proc has been killed due to too many bg + boolean setAdjChanged; // Keep track of whether we changed 'setAdj'. String waitingToKill; // Process is waiting to be killed when in the bg; reason IBinder forcingToForeground;// Token that is forcing this process to be foreground int adjSeq; // Sequence id for identifying oom_adj assignment cycles @@ -327,15 +329,15 @@ final class ProcessRecord { ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread, ApplicationInfo _info, String _processName, int _uid, - ProcessTracker.ProcessState _tracker) { + ProcessTracker.ProcessState tracker) { batteryStats = _batteryStats; info = _info; isolated = _info.uid != _uid; uid = _uid; userId = UserHandle.getUserId(_uid); processName = _processName; - tracker = _tracker; - pkgList.add(_info.packageName); + baseProcessTracker = tracker; + pkgList.put(_info.packageName, tracker); thread = _thread; maxAdj = ProcessList.CACHED_APP_MAX_ADJ; cachedAdj = clientCachedAdj = emptyAdj = ProcessList.CACHED_APP_MIN_ADJ; @@ -437,20 +439,33 @@ final class ProcessRecord { /* * Return true if package has been added false if not */ - public boolean addPackage(String pkg) { - if (!pkgList.contains(pkg)) { - pkgList.add(pkg); + public boolean addPackage(String pkg, ProcessTracker tracker) { + if (!pkgList.containsKey(pkg)) { + pkgList.put(pkg, tracker.getProcessStateLocked(pkg, info.uid, processName)); return true; } return false; } - + + public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now, + 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); + } + } + /* * Delete all packages from list except the package indicated in info */ public void resetPackageList() { + long now = SystemClock.uptimeMillis(); + for (int i=0; i mProcesses = new ArrayMap(); + final ArrayMap mServices = new ArrayMap(); + final int mUid; + + public PackageState(int uid) { + mUid = uid; } } static final class State { - final ProcessMap mProcesses = new ProcessMap(); + final ProcessMap mPackages = new ProcessMap(); + final long[] mMemFactorDurations = new long[STATE_COUNT/STATE_MEM_FACTOR_MOD]; + int mMemFactor = STATE_NOTHING; + long mStartTime; } final State mState = new State(); @@ -67,44 +98,198 @@ public final class ProcessTracker { public ProcessTracker() { } - public ProcessState getStateLocked(String name, int uid) { - ProcessState ps = mState.mProcesses.get(name, uid); + private PackageState getPackageStateLocked(String packageName, int uid) { + PackageState as = mState.mPackages.get(packageName, uid); + if (as != null) { + return as; + } + as = new PackageState(uid); + mState.mPackages.put(packageName, uid, as); + return as; + } + + 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(); - mState.mProcesses.put(name, uid, ps); + as.mProcesses.put(processName, ps); return ps; } + public ServiceState getServiceStateLocked(String packageName, int uid, String className) { + final PackageState as = getPackageStateLocked(packageName, uid); + ServiceState ss = as.mServices.get(className); + if (ss != null) { + return ss; + } + ss = new ServiceState(); + as.mServices.put(className, ss); + return ss; + } + + public boolean setMemFactor(int memFactor, boolean screenOn, long now) { + if (screenOn) { + memFactor += STATE_SCREEN_ON_MOD; + } + if (memFactor != mState.mMemFactor) { + if (mState.mMemFactor != STATE_NOTHING) { + mState.mMemFactorDurations[mState.mMemFactor/STATE_MEM_FACTOR_MOD] + += now - mState.mStartTime; + } + mState.mMemFactor = memFactor; + mState.mStartTime = now; + return true; + } + return false; + } + + public int getMemFactor() { + return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0; + } + + private void printScreenLabel(PrintWriter pw, int offset) { + switch (offset) { + case STATE_NOTHING: + pw.print(" "); + break; + case STATE_SCREEN_OFF_ADJ: + pw.print("Screen Off / "); + break; + case STATE_SCREEN_ON_ADJ: + pw.print("Screen On / "); + break; + default: + pw.print("?????????? / "); + break; + } + } + + private void printMemLabel(PrintWriter pw, int offset) { + switch (offset) { + case STATE_NOTHING: + pw.print(" "); + break; + case STATE_MEM_FACTOR_NORMAL_ADJ: + pw.print("Norm / "); + break; + case STATE_MEM_FACTOR_MODERATE_ADJ: + pw.print("Mod / "); + break; + case STATE_MEM_FACTOR_LOW_ADJ: + pw.print("Low / "); + break; + case STATE_MEM_FACTOR_CRITIAL_ADJ: + pw.print("Crit / "); + break; + default: + pw.print("???? / "); + break; + } + } + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { final long now = SystemClock.uptimeMillis(); - ArrayMap> pmap = mState.mProcesses.getMap(); + ArrayMap> pmap = mState.mPackages.getMap(); + pw.println("Process Stats:"); for (int ip=0; ip procs = pmap.valueAt(ip); + SparseArray procs = pmap.valueAt(ip); for (int iu=0; iu= 0) { + time += now - svc.mStartedTime; + } + if (time != 0) { + pw.print(" Started: "); + TimeUtils.formatDuration(time, pw); pw.println(); + } } } } + pw.println(); + pw.println("Run time Stats:"); + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen