diff options
7 files changed, 438 insertions, 165 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index bf89fa6d89fe..4568525f8232 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -552,7 +552,7 @@ public final class ActivityThread { private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; // Formatting for checkin service - update version if row format changes - private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 2; + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; private void updatePendingConfiguration(Configuration config) { synchronized (mPackages) { @@ -972,43 +972,48 @@ public final class ActivityThread { pw.print(memInfo.nativePss); pw.print(','); pw.print(memInfo.dalvikPss); pw.print(','); pw.print(memInfo.otherPss); pw.print(','); - pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(','); + pw.print(memInfo.getTotalPss()); pw.print(','); - // Heap info - proportional set size + // Heap info - swappable set size pw.print(memInfo.nativeSwappablePss); pw.print(','); pw.print(memInfo.dalvikSwappablePss); pw.print(','); pw.print(memInfo.otherSwappablePss); pw.print(','); - pw.print(memInfo.nativeSwappablePss + memInfo.dalvikSwappablePss + memInfo.otherSwappablePss); pw.print(','); + pw.print(memInfo.getTotalSwappablePss()); pw.print(','); // Heap info - shared dirty pw.print(memInfo.nativeSharedDirty); pw.print(','); pw.print(memInfo.dalvikSharedDirty); pw.print(','); pw.print(memInfo.otherSharedDirty); pw.print(','); - pw.print(memInfo.nativeSharedDirty + memInfo.dalvikSharedDirty - + memInfo.otherSharedDirty); pw.print(','); + pw.print(memInfo.getTotalSharedDirty()); pw.print(','); // Heap info - shared clean pw.print(memInfo.nativeSharedClean); pw.print(','); pw.print(memInfo.dalvikSharedClean); pw.print(','); pw.print(memInfo.otherSharedClean); pw.print(','); - pw.print(memInfo.nativeSharedClean + memInfo.dalvikSharedClean - + memInfo.otherSharedClean); pw.print(','); + pw.print(memInfo.getTotalSharedClean()); pw.print(','); // Heap info - private Dirty pw.print(memInfo.nativePrivateDirty); pw.print(','); pw.print(memInfo.dalvikPrivateDirty); pw.print(','); pw.print(memInfo.otherPrivateDirty); pw.print(','); - pw.print(memInfo.nativePrivateDirty + memInfo.dalvikPrivateDirty - + memInfo.otherPrivateDirty); pw.print(','); - + pw.print(memInfo.getTotalPrivateDirty()); pw.print(','); // Heap info - private Clean pw.print(memInfo.nativePrivateClean); pw.print(','); pw.print(memInfo.dalvikPrivateClean); pw.print(','); pw.print(memInfo.otherPrivateClean); pw.print(','); - pw.print(memInfo.nativePrivateClean + memInfo.dalvikPrivateClean - + memInfo.otherPrivateClean); pw.print(','); - + pw.print(memInfo.getTotalPrivateClean()); pw.print(','); + + // Heap info - other areas + for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { + pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(','); + pw.print(memInfo.getOtherPss(i)); pw.print(','); + pw.print(memInfo.getOtherSwappablePss(i)); pw.print(','); + pw.print(memInfo.getOtherSharedDirty(i)); pw.print(','); + pw.print(memInfo.getOtherSharedClean(i)); pw.print(','); + pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(','); + pw.print(memInfo.getOtherPrivateClean(i)); pw.print(','); + } // Object counts pw.print(viewInstanceCount); pw.print(','); diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index f4cf2eeab021..8b7f3dd5fd3c 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -394,12 +394,16 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid) if (fp == 0) return 0; while (true) { - if (fgets(line, 1024, fp) == 0) { + if (fgets(line, 1024, fp) == NULL) { break; } - if (sscanf(line, "Pss: %d kB", &temp) == 1) { - pss += temp; + if (strncmp(line, "Pss: ", 5) == 0) { + char* c = line + 5; + while (*c != 0 && (*c < '0' || *c > '9')) { + c++; + } + pss += atoi(c); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index e37eec615ea6..f201563239b9 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -30,6 +30,7 @@ import android.util.ArrayMap; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; +import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessStats; import com.android.internal.os.TransferPipe; @@ -266,6 +267,20 @@ public final class ActivityManagerService extends ActivityManagerNative // The minimum amount of time between successive GC requests for a process. static final int GC_MIN_INTERVAL = 60*1000; + // The minimum amount of time between successive PSS requests for a process. + static final int PSS_MIN_INTERVAL = 2*60*1000; + + // The amount of time we will sample PSS of the current top process while the + // screen is on. + static final int PSS_TOP_INTERVAL = 5*60*1000; + + // The maximum amount of time for a process to be around until we will take + // a PSS snapshot on its next oom change. + static final int PSS_MAX_INTERVAL = 30*60*1000; + + // The minimum amount of time between successive PSS requests for a process. + static final int FULL_PSS_MIN_INTERVAL = 10*60*1000; + // The rate at which we check for apps using excessive power -- 15 mins. static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000; @@ -495,8 +510,17 @@ public final class ActivityManagerService extends ActivityManagerNative /** * List of processes that should gc as soon as things are idle. */ - final ArrayList<ProcessRecord> mProcessesToGc - = new ArrayList<ProcessRecord>(); + final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>(); + + /** + * Processes we want to collect PSS data from. + */ + final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>(); + + /** + * Last time we requested PSS data of all processes. + */ + long mLastFullPssTime = SystemClock.uptimeMillis(); /** * This is the process holding what we currently consider to be @@ -1480,6 +1504,51 @@ public final class ActivityManagerService extends ActivityManagerNative } }; + static final int COLLECT_PSS_BG_MSG = 1; + + final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case COLLECT_PSS_BG_MSG: { + int i=0; + long start = SystemClock.uptimeMillis(); + do { + ProcessRecord proc; + int oomAdj; + int pid; + synchronized (ActivityManagerService.this) { + if (i >= mPendingPssProcesses.size()) { + Slog.i(TAG, "Collected PSS of " + i + " processes in " + + (SystemClock.uptimeMillis()-start) + "ms"); + mPendingPssProcesses.clear(); + return; + } + proc = mPendingPssProcesses.get(i); + if (proc.thread != null) { + oomAdj = proc.setAdj; + pid = proc.pid; + i++; + } else { + proc = null; + oomAdj = 0; + pid = 0; + } + } + if (proc != null) { + long pss = Debug.getPss(pid); + synchronized (ActivityManagerService.this) { + if (proc.thread != null && proc.setAdj == oomAdj && proc.pid == pid) { + proc.baseProcessTracker.addPss(pss, true); + } + } + } + } while (true); + } + } + } + }; + public static void setSystemProcess() { try { ActivityManagerService m = mSelf; @@ -3906,8 +3975,24 @@ public final class ActivityManagerService extends ActivityManagerNative enforceNotIsolatedCaller("getProcessMemoryInfo"); Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length]; for (int i=pids.length-1; i>=0; i--) { + ProcessRecord proc; + int oomAdj; + synchronized (this) { + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(pids[i]); + oomAdj = proc != null ? proc.setAdj : 0; + } + } infos[i] = new Debug.MemoryInfo(); Debug.getMemoryInfo(pids[i], infos[i]); + if (proc != null) { + synchronized (this) { + if (proc.thread != null && proc.setAdj == oomAdj) { + // Record this for posterity if the process has been stable. + proc.baseProcessTracker.addPss(infos[i].getTotalPss(), false); + } + } + } } return infos; } @@ -3917,7 +4002,23 @@ public final class ActivityManagerService extends ActivityManagerNative enforceNotIsolatedCaller("getProcessPss"); long[] pss = new long[pids.length]; for (int i=pids.length-1; i>=0; i--) { + ProcessRecord proc; + int oomAdj; + synchronized (this) { + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(pids[i]); + oomAdj = proc != null ? proc.setAdj : 0; + } + } pss[i] = Debug.getPss(pids[i]); + if (proc != null) { + synchronized (this) { + if (proc.thread != null && proc.setAdj == oomAdj) { + // Record this for posterity if the process has been stable. + proc.baseProcessTracker.addPss(pss[i], false); + } + } + } } return pss; } @@ -4350,7 +4451,7 @@ public final class ActivityManagerService extends ActivityManagerNative thread.asBinder().linkToDeath(adr, 0); app.deathRecipient = adr; } catch (RemoteException e) { - app.resetPackageList(); + app.resetPackageList(mProcessTracker); startProcessLocked(app, "link fail", processName); return false; } @@ -4442,7 +4543,7 @@ public final class ActivityManagerService extends ActivityManagerNative // an infinite loop of restarting processes... Slog.w(TAG, "Exception thrown during bind!", e); - app.resetPackageList(); + app.resetPackageList(mProcessTracker); app.unlinkDeathRecipient(); startProcessLocked(app, "bind fail", processName); return false; @@ -4630,8 +4731,19 @@ public final class ActivityManagerService extends ActivityManagerNative final int userId = mStartedUsers.keyAt(i); Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - broadcastIntentLocked(null, null, intent, - null, null, 0, null, null, + broadcastIntentLocked(null, null, intent, null, + new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, int sendingUser) { + synchronized (ActivityManagerService.this) { + requestPssAllProcsLocked(SystemClock.uptimeMillis(), + true); + } + } + }, + 0, null, null, android.Manifest.permission.RECEIVE_BOOT_COMPLETED, AppOpsManager.OP_NONE, false, false, MY_PID, Process.SYSTEM_UID, userId); @@ -10934,7 +11046,7 @@ public final class ActivityManagerService extends ActivityManagerNative long uptime = SystemClock.uptimeMillis(); long realtime = SystemClock.elapsedRealtime(); - if (procs.size() == 1 || isCheckinRequest) { + if (!brief && !oomOnly && (procs.size() == 1 || isCheckinRequest)) { dumpDetails = true; } @@ -10960,17 +11072,24 @@ public final class ActivityManagerService extends ActivityManagerNative long totalPss = 0; + Debug.MemoryInfo mi = null; for (int i = procs.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = procs.get(i); - if (r.thread != null) { + IApplicationThread thread; + int oomAdj; + synchronized (this) { + thread = r.thread; + oomAdj = r.setAdj; + } + if (thread != null) { if (!isCheckinRequest && dumpDetails) { pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **"); pw.flush(); } - Debug.MemoryInfo mi = null; if (dumpDetails) { try { - mi = r.thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs); + mi = null; + mi = thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs); } catch (RemoteException e) { if (!isCheckinRequest) { pw.println("Got RemoteException!"); @@ -10978,21 +11097,31 @@ public final class ActivityManagerService extends ActivityManagerNative } } } else { - mi = new Debug.MemoryInfo(); - Debug.getMemoryInfo(r.pid, mi); + if (mi == null) { + mi = new Debug.MemoryInfo(); + } + if (!brief && !oomOnly) { + Debug.getMemoryInfo(r.pid, mi); + } else { + mi.dalvikPss = (int)Debug.getPss(r.pid); + } + } + + final long myTotalPss = mi.getTotalPss(); + + synchronized (this) { + if (r.thread != null && oomAdj == r.setAdj) { + // Record this for posterity if the process has been stable. + r.baseProcessTracker.addPss(myTotalPss, true); + } } if (!isCheckinRequest && mi != null) { - long myTotalPss = mi.getTotalPss(); totalPss += myTotalPss; MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")", r.processName, myTotalPss, 0); procMems.add(pssItem); - synchronized (this) { - r.baseProcessTracker.addPss(myTotalPss); - } - nativePss += mi.nativePss; dalvikPss += mi.dalvikPss; otherPss += mi.otherPss; @@ -11070,7 +11199,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } for (int j=0; j<miCat.subitems.size(); j++) { - MemItem mi = miCat.subitems.get(j); + MemItem memi = miCat.subitems.get(j); if (j > 0) { if (outTag != null) { outTag.append(" "); @@ -11080,10 +11209,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) { - appendMemBucket(outTag, mi.pss, mi.shortLabel, false); + appendMemBucket(outTag, memi.pss, memi.shortLabel, false); } if (outStack != null) { - appendMemBucket(outStack, mi.pss, mi.shortLabel, true); + appendMemBucket(outStack, memi.pss, memi.shortLabel, true); } } if (outStack != null && miCat.id >= ProcessList.FOREGROUND_APP_ADJ) { @@ -11109,7 +11238,7 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println("Total PSS by OOM adjustment:"); dumpMemItems(pw, " ", oomMems, false); - if (!oomOnly) { + if (!brief && !oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; out.println(); out.println("Total PSS by category:"); @@ -11117,29 +11246,31 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(); pw.print("Total PSS: "); pw.print(totalPss); pw.println(" kB"); - final int[] SINGLE_LONG_FORMAT = new int[] { - Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG - }; - long[] longOut = new long[1]; - Process.readProcFile("/sys/kernel/mm/ksm/pages_shared", - SINGLE_LONG_FORMAT, null, longOut, null); - long shared = longOut[0] * ProcessList.PAGE_SIZE / 1024; - longOut[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing", - SINGLE_LONG_FORMAT, null, longOut, null); - long sharing = longOut[0] * ProcessList.PAGE_SIZE / 1024; - longOut[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared", - SINGLE_LONG_FORMAT, null, longOut, null); - long unshared = longOut[0] * ProcessList.PAGE_SIZE / 1024; - longOut[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile", - SINGLE_LONG_FORMAT, null, longOut, null); - long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024; - pw.print(" KSM: "); pw.print(sharing); pw.print(" kB saved from shared "); - pw.print(shared); pw.println(" kB"); - pw.print(" "); pw.print(unshared); pw.print(" kB unshared; "); - pw.print(voltile); pw.println(" kB volatile"); + if (!brief) { + final int[] SINGLE_LONG_FORMAT = new int[] { + Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG + }; + long[] longOut = new long[1]; + Process.readProcFile("/sys/kernel/mm/ksm/pages_shared", + SINGLE_LONG_FORMAT, null, longOut, null); + long shared = longOut[0] * ProcessList.PAGE_SIZE / 1024; + longOut[0] = 0; + Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing", + SINGLE_LONG_FORMAT, null, longOut, null); + long sharing = longOut[0] * ProcessList.PAGE_SIZE / 1024; + longOut[0] = 0; + Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared", + SINGLE_LONG_FORMAT, null, longOut, null); + long unshared = longOut[0] * ProcessList.PAGE_SIZE / 1024; + longOut[0] = 0; + Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile", + SINGLE_LONG_FORMAT, null, longOut, null); + long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024; + pw.print(" KSM: "); pw.print(sharing); pw.print(" kB saved from shared "); + pw.print(shared); pw.println(" kB"); + pw.print(" "); pw.print(unshared); pw.print(" kB unshared; "); + pw.print(voltile); pw.println(" kB volatile"); + } } } @@ -11236,6 +11367,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mProcessesToGc.remove(app); + mPendingPssProcesses.remove(app); // Dismiss any open dialogs. if (app.crashDialog != null && !app.forceCrashReport) { @@ -11254,7 +11386,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.crashing = false; app.notResponding = false; - app.resetPackageList(); + app.resetPackageList(mProcessTracker); app.unlinkDeathRecipient(); app.thread = null; app.forcingToForeground = null; @@ -13749,6 +13881,41 @@ public final class ActivityManagerService extends ActivityManagerNative } /** + * Schedule PSS collection of a process. + */ + void requestPssLocked(ProcessRecord proc, long now, boolean always) { + if (!always && now < (proc.lastPssTime+PSS_MIN_INTERVAL)) { + return; + } + if (mPendingPssProcesses.contains(proc)) { + return; + } + proc.lastPssTime = now; + if (mPendingPssProcesses.size() == 0) { + mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG); + } + mPendingPssProcesses.add(proc); + } + + /** + * Schedule PSS collection of all processes. + */ + void requestPssAllProcsLocked(long now, boolean always) { + if (!always && now < (mLastFullPssTime+FULL_PSS_MIN_INTERVAL)) { + return; + } + mLastFullPssTime = now; + mPendingPssProcesses.ensureCapacity(mLruProcesses.size()); + mPendingPssProcesses.clear(); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + app.lastPssTime = now; + mPendingPssProcesses.add(app); + } + mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG); + } + + /** * Ask a given process to GC right now. */ final void performAppGcLocked(ProcessRecord app) { @@ -13959,6 +14126,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " during " + realtimeSince); EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "excessive wake lock"); + app.baseProcessTracker.reportExcessiveWake(app.pkgList); Process.killProcessQuiet(app.pid); } else if (doCpuKills && uptimeSince > 0 && ((cputimeUsed*100)/uptimeSince) >= 50) { @@ -13971,6 +14139,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " during " + uptimeSince); EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "excessive cpu"); + app.baseProcessTracker.reportExcessiveCpu(app.pkgList); Process.killProcessQuiet(app.pid); } else { app.lastWakeTime = wtime; @@ -14012,11 +14181,23 @@ public final class ActivityManagerService extends ActivityManagerNative app.setRawAdj = app.curRawAdj; } + if (app == TOP_APP && now > (app.lastPssTime+PSS_TOP_INTERVAL)) { + requestPssLocked(app, now, true); + } + if (app.curAdj != app.setAdj) { if (Process.setOomAdj(app.pid, app.curAdj)) { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( TAG, "Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": " + app.adjType); + if (app.setAdj == ProcessList.SERVICE_ADJ + && app.curAdj == ProcessList.SERVICE_B_ADJ) { + // If a service is dropping to the B list, it has been running for + // a while, take a PSS snapshot. + requestPssLocked(app, now, false); + } else if (now > (app.lastPssTime+PSS_MAX_INTERVAL)) { + requestPssLocked(app, now, true); + } app.setAdj = app.curAdj; app.setAdjChanged = true; if (!doingAll) { @@ -14406,6 +14587,9 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + if (allChanged) { + requestPssAllProcsLocked(now, false); + } if (DEBUG_OOM_ADJ) { Slog.d(TAG, "Did OOM ADJ in " + (SystemClock.uptimeMillis()-now) + "ms"); diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 561dd0f2d3f2..44b61d0daab6 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -222,7 +222,7 @@ final class ActivityRecord { if (lastLaunchTime == 0) pw.print("0"); else TimeUtils.formatDuration(lastLaunchTime, now, pw); pw.println(); - pw.print(prefix); pw.print(" haveState="); pw.print(haveState); + pw.print(prefix); pw.print("haveState="); pw.print(haveState); pw.print(" icicle="); pw.println(icicle); pw.print(prefix); pw.print("state="); pw.print(state); pw.print(" stopped="); pw.print(stopped); diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 8d981097370e..92a15231c2c5 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -1707,6 +1707,7 @@ public final class ActivityStackSupervisor { r.idle = true; if (allResumedActivitiesIdle()) { mService.scheduleAppGcsLocked(); + mService.requestPssLocked(r.app, SystemClock.uptimeMillis(), false); } if (r.thumbnailNeeded && r.app != null && r.app.thread != null) { sendThumbnail = r.app.thread; diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index e72656f8e3f1..365009ddf842 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -62,6 +62,7 @@ final class ProcessRecord { boolean starting; // True if the process is being started long lastActivityTime; // For managing the LRU list long lruWeight; // Weight for ordering in LRU list + long lastPssTime; // Last time we requested PSS data int maxAdj; // Maximum OOM adjustment for this process int cachedAdj; // If cached, this is the adjustment to use int clientCachedAdj; // If empty but cached client, this is the adjustment to use @@ -112,7 +113,6 @@ final class ProcessRecord { boolean reportLowMemory; // Set to true when waiting to report low mem boolean empty; // Is this an empty background process? boolean cached; // Is this a cached process? - int lastPss; // Last pss size reported by app. String adjType; // Debugging: primary thing impacting oom_adj. int adjTypeCode; // Debugging: adj code to report to app. Object adjSource; // Debugging: option dependent object. @@ -180,7 +180,12 @@ final class ProcessRecord { pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir); pw.print(" publicDir="); pw.print(info.publicSourceDir); pw.print(" data="); pw.println(info.dataDir); - pw.print(prefix); pw.print("packageList="); pw.println(pkgList); + pw.print(prefix); pw.print("packageList={"); + for (int i=0; i<pkgList.size(); i++) { + if (i > 0) pw.print(", "); + pw.print(pkgList.keyAt(i)); + } + pw.println("}"); pw.print(prefix); pw.print("compat="); pw.println(compat); if (instrumentationClass != null || instrumentationProfileFile != null || instrumentationArguments != null) { @@ -198,7 +203,7 @@ final class ProcessRecord { } pw.print(prefix); pw.print("thread="); pw.println(thread); pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting="); - pw.print(starting); pw.print(" lastPss="); pw.println(lastPss); + pw.println(starting); pw.print(prefix); pw.print("lastActivityTime="); TimeUtils.formatDuration(lastActivityTime, now, pw); pw.print(" lruWeight="); pw.print(lruWeight); @@ -220,7 +225,8 @@ final class ProcessRecord { pw.print(" systemNoUi="); pw.print(systemNoUi); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq); - pw.print(" lruSeq="); pw.println(lruSeq); + pw.print(" lruSeq="); pw.print(lruSeq); + pw.print(" lastPssTime="); pw.println(lastPssTime); if (hasShownUi || pendingUiClean || hasAboveClient) { pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi); pw.print(" pendingUiClean="); pw.print(pendingUiClean); @@ -345,6 +351,7 @@ final class ProcessRecord { curAdj = setAdj = -100; persistent = false; removed = false; + lastPssTime = SystemClock.uptimeMillis(); } public void setPid(int _pid) { @@ -451,21 +458,20 @@ final class ProcessRecord { ProcessList plist) { int state = this == TOP_APP ? ProcessTracker.STATE_TOP : plist.adjToTrackedState(setAdj); - if (pkgList.size() > 0) { - pkgList.valueAt(0).setState(state, memFactor, now, pkgList); - } + baseProcessTracker.setState(state, memFactor, now, pkgList); } /* * Delete all packages from list except the package indicated in info */ - public void resetPackageList() { + public void resetPackageList(ProcessTracker tracker) { long now = SystemClock.uptimeMillis(); - if (pkgList.size() > 0) { - pkgList.valueAt(0).setState(ProcessTracker.STATE_NOTHING, 0, now, pkgList); + baseProcessTracker.setState(ProcessTracker.STATE_NOTHING, 0, now, pkgList); + if (pkgList.size() != 1) { + pkgList.clear(); + pkgList.put(info.packageName, tracker.getProcessStateLocked( + info.packageName, info.uid, processName)); } - pkgList.clear(); - pkgList.put(info.packageName, baseProcessTracker); } public String[] getPackageList() { diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java index 30470f1b69c6..82b2158d2e84 100644 --- a/services/java/com/android/server/am/ProcessTracker.java +++ b/services/java/com/android/server/am/ProcessTracker.java @@ -48,6 +48,11 @@ public final class ProcessTracker { public static final int STATE_CACHED = 9; public static final int STATE_COUNT = STATE_CACHED+1; + static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, STATE_TOP, + STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE, STATE_BACKUP, + STATE_SERVICE, STATE_HOME, STATE_PREVIOUS, STATE_CACHED + }; + public static final int PSS_SAMPLE_COUNT = 0; public static final int PSS_MINIMUM = 1; public static final int PSS_AVERAGE = 2; @@ -65,6 +70,10 @@ 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 final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; + static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE, + ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; + // 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 @@ -110,7 +119,7 @@ public final class ProcessTracker { static final String[] STATE_TAGS = new String[] { "p", "t", "f", "v", "t", - "b", "s", "h", "v", "c" + "b", "s", "h", "r", "c" }; static final String CSV_SEP = "\t"; @@ -132,9 +141,14 @@ public final class ProcessTracker { int mCurState = STATE_NOTHING; long mStartTime; + int mLastPssState = STATE_NOTHING; + long mLastPssTime; int[] mPssTable; int mPssTableSize; + int mNumExcessiveWake; + int mNumExcessiveCpu; + boolean mMultiPackage; long mTmpTotalTime; @@ -145,7 +159,7 @@ public final class ProcessTracker { */ public ProcessState(State state, String pkg, int uid, String name) { mState = state; - mCommonProcess = null; + mCommonProcess = this; mPackage = pkg; mUid = uid; mName = name; @@ -199,6 +213,8 @@ public final class ProcessTracker { pnew.mPssTableSize = mState.mFindTableSize; } */ + pnew.mNumExcessiveWake = mNumExcessiveWake; + pnew.mNumExcessiveCpu = mNumExcessiveCpu; return pnew; } @@ -208,30 +224,16 @@ public final class ProcessTracker { 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; - } + // First update the common process. + mCommonProcess.setState(state, now); + + // If the common process is not multi-package, there is nothing else to do. + if (!mCommonProcess.mMultiPackage) { + 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); + pullFixedProc(pkgList, ip).setState(state, now); } } @@ -258,7 +260,15 @@ public final class ProcessTracker { } } - public void addPss(long pss) { + public void addPss(long pss, boolean always) { + if (!always) { + if (mLastPssState == mCurState && SystemClock.uptimeMillis() + < (mLastPssTime+(30*1000))) { + return; + } + } + mLastPssState = mCurState; + mLastPssTime = SystemClock.uptimeMillis(); if (mCurState != STATE_NOTHING) { int idx = State.binarySearch(mPssTable, mPssTableSize, mCurState); int off; @@ -284,7 +294,8 @@ public final class ProcessTracker { if (longs[idx+PSS_MINIMUM] > pss) { longs[idx+PSS_MINIMUM] = pss; } - longs[idx+PSS_AVERAGE] = ((longs[idx+PSS_AVERAGE]*count)+pss)/(count+1); + longs[idx+PSS_AVERAGE] = (long)( ((longs[idx+PSS_AVERAGE]*(double)count)+pss) + / (count+1) ); if (longs[idx+PSS_MAXIMUM] < pss) { longs[idx+PSS_MAXIMUM] = pss; } @@ -292,6 +303,45 @@ public final class ProcessTracker { } } + public void reportExcessiveWake(ArrayMap<String, ProcessTracker.ProcessState> pkgList) { + mCommonProcess.mNumExcessiveWake++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveWake++; + } + } + + public void reportExcessiveCpu(ArrayMap<String, ProcessTracker.ProcessState> pkgList) { + mCommonProcess.mNumExcessiveCpu++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveCpu++; + } + } + + private ProcessState pullFixedProc(ArrayMap<String, ProcessTracker.ProcessState> pkgList, + int index) { + ProcessState proc = pkgList.valueAt(index); + 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(index), + proc.mUid).mProcesses.get(proc.mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process"); + } + pkgList.setValueAt(index, proc); + } + return proc; + } + long getDuration(int state, long now) { int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, state); long time = idx >= 0 ? mState.getLong(mDurationsTable[idx], 0) : 0; @@ -685,7 +735,7 @@ public final class ProcessTracker { } } - static void dumpSingleTimeCsv(PrintWriter pw, String sep, long[] durations, + static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations, int curState, long curStartTime, long now) { for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { @@ -694,8 +744,9 @@ public final class ProcessTracker { if (curState == state) { time += now - curStartTime; } - pw.print(sep); - pw.print(time); + if (time != 0) { + printAdjTagAndValue(pw, state, time); + } } } } @@ -714,11 +765,11 @@ public final class ProcessTracker { pw.print(","); pw.print(serviceName); pw.print(opCount); - dumpSingleTimeCsv(pw, ",", durations, curState, curStartTime, now); + dumpAdjTimesCheckin(pw, ",", durations, curState, curStartTime, now); pw.println(); } - long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, + static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, int[] procStates, long now) { long totalTime = 0; /* @@ -756,10 +807,7 @@ public final class ProcessTracker { PackageState state = procs.valueAt(iu); for (int iproc=0; iproc<state.mProcesses.size(); iproc++) { ProcessState proc = state.mProcesses.valueAt(iproc); - if (proc.mCommonProcess != null) { - proc = proc.mCommonProcess; - } - foundProcs.add(proc); + foundProcs.add(proc.mCommonProcess); } } } @@ -785,8 +833,8 @@ public final class ProcessTracker { return outProcs; } - void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates, - int[] memStates, int[] procStates, long now) { + static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, + int[] screenStates, int[] memStates, int[] procStates, long now) { long totalTime = 0; int printedScreen = -1; for (int is=0; is<screenStates.length; is++) { @@ -833,7 +881,7 @@ public final class ProcessTracker { } } - void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates, + static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates, int[] memStates, int[] procStates) { boolean printedHeader = false; int printedScreen = -1; @@ -877,9 +925,17 @@ public final class ProcessTracker { } } } + if (proc.mNumExcessiveWake != 0) { + pw.print(prefix); pw.print("Killed for excessive wake locks: "); + pw.print(proc.mNumExcessiveWake); pw.println(" times"); + } + if (proc.mNumExcessiveCpu != 0) { + pw.print(prefix); pw.print("Killed for excessive CPU use: "); + pw.print(proc.mNumExcessiveCpu); pw.println(" times"); + } } - void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, + static 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; @@ -911,7 +967,7 @@ public final class ProcessTracker { } } - void dumpProcessStateCsv(PrintWriter pw, ProcessState proc, + static 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; @@ -946,7 +1002,7 @@ public final class ProcessTracker { } } - void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, + static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates, long now) { String innerPrefix = prefix + " "; for (int i=procs.size()-1; i>=0; i--) { @@ -966,7 +1022,7 @@ public final class ProcessTracker { } } - void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, + static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now) { pw.print("process"); @@ -1014,44 +1070,6 @@ public final class ProcessTracker { return false; } - void dumpAllProcessState(PrintWriter pw, String prefix, ProcessState proc, long now) { - long totalTime = 0; - int printedScreen = -1; - for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { - int printedMem = -1; - for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { - for (int is=0; is<STATE_NAMES.length; is++) { - int bucket = is+(STATE_COUNT*(imem+iscreen)); - long time = proc.getDuration(bucket, now); - String running = ""; - if (proc.mCurState == bucket) { - running = " (running)"; - } - if (time != 0) { - pw.print(prefix); - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - printMemLabel(pw, printedMem != imem - ? imem : STATE_NOTHING); - printedMem = imem; - pw.print(STATE_NAMES[is]); pw.print(": "); - TimeUtils.formatDuration(time, pw); pw.println(running); - totalTime += time; - } - } - } - } - if (totalTime != 0) { - pw.print(prefix); - printScreenLabel(pw, STATE_NOTHING); - printMemLabel(pw, STATE_NOTHING); - pw.print("TOTAL : "); - TimeUtils.formatDuration(totalTime, pw); - pw.println(); - } - } - static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) { int index = value/mod; if (index >= 0 && index < array.length) { @@ -1062,20 +1080,32 @@ public final class ProcessTracker { return value - index*mod; } - void printProcStateTag(PrintWriter pw, int state) { + static void printProcStateTag(PrintWriter pw, int state) { state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT); state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT); printArrayEntry(pw, STATE_TAGS, state, 1); } - void printProcStateTagAndValue(PrintWriter pw, int state, long value) { + static void printAdjTag(PrintWriter pw, int state) { + state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); + printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); + } + + static void printProcStateTagAndValue(PrintWriter pw, int state, long value) { pw.print(','); printProcStateTag(pw, state); pw.print(':'); pw.print(value); } - void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) { + static void printAdjTagAndValue(PrintWriter pw, int state, long value) { + pw.print(','); + printAdjTag(pw, state); + pw.print(':'); + pw.print(value); + } + + static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) { boolean didCurState = false; for (int i=0; i<proc.mDurationsTableSize; i++) { int off = proc.mDurationsTable[i]; @@ -1092,7 +1122,7 @@ public final class ProcessTracker { } } - void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc, long now) { + static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) { for (int i=0; i<proc.mPssTableSize; i++) { int off = proc.mPssTable[i]; int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; @@ -1154,7 +1184,7 @@ public final class ProcessTracker { return finalRes; } - private void dumpHelp(PrintWriter pw) { + static private void dumpHelp(PrintWriter pw) { pw.println("Process stats (procstats) dump options:"); pw.println(" [--checkin|--csv] [csv-screen] [csv-proc] [csv-mem]"); pw.println(" [--reset] [-h] [<package.name>]"); @@ -1165,6 +1195,7 @@ public final class ProcessTracker { 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(" -a: print everything."); pw.println(" -h: print this help text."); pw.println(" <package.name>: optional name of package to filter output by."); } @@ -1174,6 +1205,7 @@ public final class ProcessTracker { boolean isCheckin = false; boolean isCsv = false; + boolean dumpAll = false; String reqPackage = null; boolean csvSepScreenStats = false; int[] csvScreenStats = new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}; @@ -1248,7 +1280,7 @@ public final class ProcessTracker { dumpHelp(pw); return; } else if ("-a".equals(arg)) { - // ignore + dumpAll = true; } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); dumpHelp(pw); @@ -1332,8 +1364,6 @@ public final class ProcessTracker { if (NPROCS > 0 || NSRVS > 0) { if (!printedHeader) { pw.println("Per-Package Process Stats:"); - pw.print(" Num long arrays: "); pw.println(mState.mLongs.size()); - pw.print(" Next long entry: "); pw.println(mState.mNextLong); printedHeader = true; } pw.print(" * "); pw.print(pkgName); pw.print(" / "); @@ -1349,7 +1379,10 @@ public final class ProcessTracker { pw.print(proc.mDurationsTableSize); pw.print(" entries)"); pw.println(":"); - dumpAllProcessState(pw, " ", proc, now); + dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); } else { pw.print("pkgproc,"); pw.print(pkgName); @@ -1359,6 +1392,29 @@ public final class ProcessTracker { pw.print(state.mProcesses.keyAt(iproc)); dumpAllProcessStateCheckin(pw, proc, now); pw.println(); + if (proc.mPssTableSize > 0) { + pw.print("pkgpss,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(state.mProcesses.keyAt(iproc)); + dumpAllProcessPssCheckin(pw, proc); + pw.println(); + } + if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) { + pw.print("pkgkills,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(state.mProcesses.keyAt(iproc)); + pw.print(","); + pw.print(proc.mNumExcessiveWake); + pw.print(","); + pw.print(proc.mNumExcessiveCpu); + pw.println(); + } } } for (int isvc=0; isvc<NSRVS; isvc++) { @@ -1431,6 +1487,12 @@ public final class ProcessTracker { pw.println("Run time Stats:"); dumpSingleTime(pw, " ", mState.mMemFactorDurations, mState.mMemFactor, mState.mStartTime, now); + if (dumpAll) { + pw.println(); + pw.println("Internal state:"); + pw.print(" Num long arrays: "); pw.println(mState.mLongs.size()); + pw.print(" Next long entry: "); pw.println(mState.mNextLong); + } } else { ArrayMap<String, SparseArray<ProcessState>> procMap = mState.mProcesses.getMap(); for (int ip=0; ip<procMap.size(); ip++) { @@ -1452,13 +1514,24 @@ public final class ProcessTracker { pw.print(procName); pw.print(","); pw.print(uid); - dumpAllProcessPssCheckin(pw, state, now); + dumpAllProcessPssCheckin(pw, state); + pw.println(); + } + if (state.mNumExcessiveWake > 0 || state.mNumExcessiveCpu > 0) { + pw.print("kills,"); + pw.print(uid); + pw.print(","); + pw.print(procName); + pw.print(","); + pw.print(state.mNumExcessiveWake); + pw.print(","); + pw.print(state.mNumExcessiveCpu); pw.println(); } } } pw.print("total"); - dumpSingleTimeCsv(pw, ",", mState.mMemFactorDurations, mState.mMemFactor, + dumpAdjTimesCheckin(pw, ",", mState.mMemFactorDurations, mState.mMemFactor, mState.mStartTime, now); pw.println(); } |