diff options
author | Dianne Hackborn <hackbod@google.com> | 2013-09-10 19:06:15 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2013-09-13 16:02:01 -0700 |
commit | 8e69257a9c7e9c1781e1f53d8856358ada38921d (patch) | |
tree | 69c9e07b9ed81d9ef8fb03769370b60d03e75f70 /services/java/com/android/server/am | |
parent | 9210bc85545f31973c957b5179e6a82d05f473c6 (diff) | |
download | frameworks_base-8e69257a9c7e9c1781e1f53d8856358ada38921d.zip frameworks_base-8e69257a9c7e9c1781e1f53d8856358ada38921d.tar.gz frameworks_base-8e69257a9c7e9c1781e1f53d8856358ada38921d.tar.bz2 |
Implement #10749688: Improve low memory reporting
This significantly reworks the logging we do when
all cached processes are killed:
- We now collect the list of processes in-place so we
have a snapshot of exactly when the low memory situation
happened.
- In that snapshot we include the key process state: oom
adj, proc state, adj reasons.
- The report then asynchronously collects pss information
for those processes.
- The ultimate data printed to the log looks like a mix
between the "dumpsys meminfo" and "dumpsys activity"
output. This code no longer uses "dumpsys meminfo"
itself, so some of that data is no longer included,
in particular pss organized by allocation type.
In doing this, I realized that the existing code that is
supposed to run "procstats" is not currently working. And
at that point I realized, really, when we are collecting
this pss data we'd really like to include all those native
processes using ghod-only-knows how much RAM. And guess
what, we have a list of processes available in
ProcessCpuTracker.
So we now also collect and print information for native
processes, and we also do this for "dumpsys meminfo" which
really seems like a good thing when we are printing summaries
of all pss and such.
I also improved the code for reading /proc/meminfo to be
able to load all the interesting fields from there, and
am now printing that as well.
Change-Id: I9e7d13e9c07a8249c7a7e12e5433973b2c0fdc11
Diffstat (limited to 'services/java/com/android/server/am')
5 files changed, 425 insertions, 237 deletions
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index 4521037..b4d9da0 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -328,8 +328,17 @@ public final class ActiveServices { addToStarting = true; if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Not delaying, but counting as bg: " + r); } else if (DEBUG_DELAYED_STATS) { - Slog.v(TAG, "Not potential delay (state=" + proc.curProcState - + " " + proc.makeAdjReason() + "): " + r); + StringBuilder sb = new StringBuilder(128); + sb.append("Not potential delay (state=").append(proc.curProcState) + .append(' ').append(proc.adjType); + String reason = proc.makeAdjReason(); + if (reason != null) { + sb.append(' '); + sb.append(reason); + } + sb.append("): "); + sb.append(r.toString()); + Slog.v(TAG, sb.toString()); } } else if (DEBUG_DELAYED_STATS) { if (callerFg) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 96b7030..5f2265c 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1370,64 +1370,167 @@ public final class ActivityManagerService extends ActivityManagerNative break; } case REPORT_MEM_USAGE: { - boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); - if (!isDebuggable) { - return; - } - synchronized (ActivityManagerService.this) { - long now = SystemClock.uptimeMillis(); - if (now < (mLastMemUsageReportTime+5*60*1000)) { - // Don't report more than every 5 minutes to somewhat - // avoid spamming. - return; - } - mLastMemUsageReportTime = now; - } + final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj; Thread thread = new Thread() { @Override public void run() { - StringBuilder dropBuilder = new StringBuilder(1024); + final SparseArray<ProcessMemInfo> infoMap + = new SparseArray<ProcessMemInfo>(memInfos.size()); + for (int i=0, N=memInfos.size(); i<N; i++) { + ProcessMemInfo mi = memInfos.get(i); + infoMap.put(mi.pid, mi); + } + updateCpuStatsNow(); + synchronized (mProcessCpuThread) { + final int N = mProcessCpuTracker.countStats(); + for (int i=0; i<N; i++) { + ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); + if (st.vsize > 0) { + long pss = Debug.getPss(st.pid, null); + if (pss > 0) { + if (infoMap.indexOfKey(st.pid) < 0) { + ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid, + ProcessList.NATIVE_ADJ, -1, "native", null); + mi.pss = pss; + memInfos.add(mi); + } + } + } + } + } + + long totalPss = 0; + for (int i=0, N=memInfos.size(); i<N; i++) { + ProcessMemInfo mi = memInfos.get(i); + if (mi.pss == 0) { + mi.pss = Debug.getPss(mi.pid, null); + } + totalPss += mi.pss; + } + Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { + @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { + if (lhs.oomAdj != rhs.oomAdj) { + return lhs.oomAdj < rhs.oomAdj ? -1 : 1; + } + if (lhs.pss != rhs.pss) { + return lhs.pss < rhs.pss ? 1 : -1; + } + return 0; + } + }); + + StringBuilder tag = new StringBuilder(128); + StringBuilder stack = new StringBuilder(128); + tag.append("Low on memory -- "); + appendMemBucket(tag, totalPss, "total", false); + appendMemBucket(stack, totalPss, "total", true); + StringBuilder logBuilder = new StringBuilder(1024); + logBuilder.append("Low on memory:\n"); + + boolean firstLine = true; + int lastOomAdj = Integer.MIN_VALUE; + for (int i=0, N=memInfos.size(); i<N; i++) { + ProcessMemInfo mi = memInfos.get(i); + + if (mi.oomAdj != ProcessList.NATIVE_ADJ + && (mi.oomAdj < ProcessList.SERVICE_ADJ + || mi.oomAdj == ProcessList.HOME_APP_ADJ + || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) { + if (lastOomAdj != mi.oomAdj) { + lastOomAdj = mi.oomAdj; + if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { + tag.append(" / "); + } + if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) { + if (firstLine) { + stack.append(":"); + firstLine = false; + } + stack.append("\n\t at "); + } else { + stack.append("$"); + } + } else { + tag.append(" "); + stack.append("$"); + } + if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { + appendMemBucket(tag, mi.pss, mi.name, false); + } + appendMemBucket(stack, mi.pss, mi.name, true); + if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ + && ((i+1) >= N || memInfos.get(i+1).oomAdj != lastOomAdj)) { + stack.append("("); + for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) { + if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) { + stack.append(DUMP_MEM_OOM_LABEL[k]); + stack.append(":"); + stack.append(DUMP_MEM_OOM_ADJ[k]); + } + } + stack.append(")"); + } + } + + logBuilder.append(" "); + logBuilder.append(ProcessList.makeOomAdjString(mi.oomAdj)); + logBuilder.append(' '); + logBuilder.append(ProcessList.makeProcStateString(mi.procState)); + logBuilder.append(' '); + ProcessList.appendRamKb(logBuilder, mi.pss); + logBuilder.append(" kB: "); + logBuilder.append(mi.name); + logBuilder.append(" ("); + logBuilder.append(mi.pid); + logBuilder.append(") "); + logBuilder.append(mi.adjType); + logBuilder.append('\n'); + if (mi.adjReason != null) { + logBuilder.append(" "); + logBuilder.append(mi.adjReason); + logBuilder.append('\n'); + } + } + + logBuilder.append(" "); + ProcessList.appendRamKb(logBuilder, totalPss); + logBuilder.append(" kB: TOTAL\n"); + + long[] infos = new long[Debug.MEMINFO_COUNT]; + Debug.getMemInfo(infos); + logBuilder.append(" MemInfo: "); + logBuilder.append(infos[Debug.MEMINFO_SLAB]).append(" kB slab, "); + logBuilder.append(infos[Debug.MEMINFO_SHMEM]).append(" kB shmem, "); + logBuilder.append(infos[Debug.MEMINFO_BUFFERS]).append(" kB buffers, "); + logBuilder.append(infos[Debug.MEMINFO_CACHED]).append(" kB cached, "); + logBuilder.append(infos[Debug.MEMINFO_FREE]).append(" kB free\n"); + + Slog.i(TAG, logBuilder.toString()); + + StringBuilder dropBuilder = new StringBuilder(1024); + /* StringWriter oomSw = new StringWriter(); PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256); StringWriter catSw = new StringWriter(); PrintWriter catPw = new FastPrintWriter(catSw, false, 256); String[] emptyArgs = new String[] { }; - StringBuilder tag = new StringBuilder(128); - StringBuilder stack = new StringBuilder(128); - tag.append("Low on memory -- "); - dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw, - tag, stack); + dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw); + oomPw.flush(); + String oomString = oomSw.toString(); + */ dropBuilder.append(stack); dropBuilder.append('\n'); dropBuilder.append('\n'); - oomPw.flush(); - String oomString = oomSw.toString(); + dropBuilder.append(logBuilder); + dropBuilder.append('\n'); + /* dropBuilder.append(oomString); dropBuilder.append('\n'); - logBuilder.append(oomString); - try { - java.lang.Process proc = Runtime.getRuntime().exec(new String[] { - "procrank", }); - final InputStreamReader converter = new InputStreamReader( - proc.getInputStream()); - BufferedReader in = new BufferedReader(converter); - String line; - while (true) { - line = in.readLine(); - if (line == null) { - break; - } - if (line.length() > 0) { - logBuilder.append(line); - logBuilder.append('\n'); - } - dropBuilder.append(line); - dropBuilder.append('\n'); - } - converter.close(); - } catch (IOException e) { - } + */ + StringWriter catSw = new StringWriter(); synchronized (ActivityManagerService.this) { + PrintWriter catPw = new FastPrintWriter(catSw, false, 256); + String[] emptyArgs = new String[] { }; catPw.println(); dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); catPw.println(); @@ -1435,12 +1538,13 @@ public final class ActivityManagerService extends ActivityManagerNative false, false, null); catPw.println(); dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); + catPw.flush(); } - catPw.flush(); dropBuilder.append(catSw.toString()); addErrorToDropBox("lowmem", null, "system_server", null, null, tag.toString(), dropBuilder.toString(), null, null); - Slog.i(TAG, logBuilder.toString()); + //Slog.i(TAG, "Sent to dropbox:"); + //Slog.i(TAG, dropBuilder.toString()); synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); if (mLastMemUsageReportTime < now) { @@ -1691,8 +1795,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, - false, null, null, null); + mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); } } @@ -3271,6 +3374,66 @@ public final class ActivityManagerService extends ActivityManagerNative return appIndex >= 0 ? mLruProcesses.get(appIndex) : null; } + final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) { + // If there are no longer any background processes running, + // and the app that died was not running instrumentation, + // then tell everyone we are now low on memory. + boolean haveBg = false; + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); + if (rec.thread != null + && rec.setProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { + haveBg = true; + break; + } + } + + if (!haveBg) { + boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + if (doReport) { + long now = SystemClock.uptimeMillis(); + if (now < (mLastMemUsageReportTime+5*60*1000)) { + doReport = false; + } else { + mLastMemUsageReportTime = now; + } + } + final ArrayList<ProcessMemInfo> memInfos + = doReport ? new ArrayList<ProcessMemInfo>(mLruProcesses.size()) : null; + EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size()); + long now = SystemClock.uptimeMillis(); + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord rec = mLruProcesses.get(i); + if (rec == dyingProc || rec.thread == null) { + continue; + } + if (doReport) { + memInfos.add(new ProcessMemInfo(rec.processName, rec.pid, rec.setAdj, + rec.setProcState, rec.adjType, rec.makeAdjReason())); + } + if ((rec.lastLowMemory+GC_MIN_INTERVAL) <= now) { + // The low memory report is overriding any current + // state for a GC request. Make sure to do + // heavy/important/visible/foreground processes first. + if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) { + rec.lastRequestedGc = 0; + } else { + rec.lastRequestedGc = rec.lastLowMemory; + } + rec.reportLowMemory = true; + rec.lastLowMemory = now; + mProcessesToGc.remove(rec); + addProcessToGcListLocked(rec); + } + } + if (doReport) { + Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE, memInfos); + mHandler.sendMessage(msg); + } + scheduleAppGcsLocked(); + } + } + final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) { @@ -3296,42 +3459,7 @@ public final class ActivityManagerService extends ActivityManagerNative handleAppDiedLocked(app, false, true); if (doLowMem) { - // If there are no longer any background processes running, - // and the app that died was not running instrumentation, - // then tell everyone we are now low on memory. - boolean haveBg = false; - for (int i=mLruProcesses.size()-1; i>=0; i--) { - ProcessRecord rec = mLruProcesses.get(i); - if (rec.thread != null && rec.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) { - haveBg = true; - break; - } - } - - if (!haveBg) { - EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size()); - long now = SystemClock.uptimeMillis(); - for (int i=mLruProcesses.size()-1; i>=0; i--) { - ProcessRecord rec = mLruProcesses.get(i); - if (rec != app && rec.thread != null && - (rec.lastLowMemory+GC_MIN_INTERVAL) <= now) { - // The low memory report is overriding any current - // state for a GC request. Make sure to do - // heavy/important/visible/foreground processes first. - if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) { - rec.lastRequestedGc = 0; - } else { - rec.lastRequestedGc = rec.lastLowMemory; - } - rec.reportLowMemory = true; - rec.lastLowMemory = now; - mProcessesToGc.remove(rec); - addProcessToGcListLocked(rec); - } - } - mHandler.sendEmptyMessage(REPORT_MEM_USAGE); - scheduleAppGcsLocked(); - } + doLowMemReportIfNeededLocked(app); } } else if (app.pid != pid) { // A new process has already been started. @@ -3850,6 +3978,8 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), false, true, "kill all background"); } + updateOomAdjLocked(); + doLowMemReportIfNeededLocked(null); } } finally { Binder.restoreCallingIdentity(callingId); @@ -4159,6 +4289,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); } + updateOomAdjLocked(); return N > 0; } @@ -10791,14 +10922,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private static String buildOomTag(String prefix, String space, int val, int base) { - if (val == base) { - if (space == null) return prefix; - return prefix + " "; - } - return prefix + "+" + Integer.toString(val-base); - } - private static final int dumpProcessList(PrintWriter pw, ActivityManagerService service, List list, String prefix, String normalLabel, String persistentLabel, @@ -10870,34 +10993,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=list.size()-1; i>=0; i--) { ProcessRecord r = list.get(i).first; - String oomAdj; - if (r.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) { - oomAdj = buildOomTag("cch", " ", r.setAdj, ProcessList.CACHED_APP_MIN_ADJ); - } else if (r.setAdj >= ProcessList.SERVICE_B_ADJ) { - oomAdj = buildOomTag("svcb ", null, r.setAdj, ProcessList.SERVICE_B_ADJ); - } else if (r.setAdj >= ProcessList.PREVIOUS_APP_ADJ) { - oomAdj = buildOomTag("prev ", null, r.setAdj, ProcessList.PREVIOUS_APP_ADJ); - } else if (r.setAdj >= ProcessList.HOME_APP_ADJ) { - oomAdj = buildOomTag("home ", null, r.setAdj, ProcessList.HOME_APP_ADJ); - } else if (r.setAdj >= ProcessList.SERVICE_ADJ) { - oomAdj = buildOomTag("svc ", null, r.setAdj, ProcessList.SERVICE_ADJ); - } else if (r.setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { - oomAdj = buildOomTag("hvy ", null, r.setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ); - } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) { - oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ); - } else if (r.setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { - oomAdj = buildOomTag("prcp ", null, r.setAdj, ProcessList.PERCEPTIBLE_APP_ADJ); - } else if (r.setAdj >= ProcessList.VISIBLE_APP_ADJ) { - oomAdj = buildOomTag("vis ", null, r.setAdj, ProcessList.VISIBLE_APP_ADJ); - } else if (r.setAdj >= ProcessList.FOREGROUND_APP_ADJ) { - oomAdj = buildOomTag("fore ", null, r.setAdj, ProcessList.FOREGROUND_APP_ADJ); - } else if (r.setAdj >= ProcessList.PERSISTENT_PROC_ADJ) { - oomAdj = buildOomTag("pers ", null, r.setAdj, ProcessList.PERSISTENT_PROC_ADJ); - } else if (r.setAdj >= ProcessList.SYSTEM_ADJ) { - oomAdj = buildOomTag("sys ", null, r.setAdj, ProcessList.SYSTEM_ADJ); - } else { - oomAdj = Integer.toString(r.setAdj); - } + String oomAdj = ProcessList.makeOomAdjString(r.setAdj); char schedGroup; switch (r.setSchedGroup) { case Process.THREAD_GROUP_BG_NONINTERACTIVE: @@ -10918,54 +11014,7 @@ public final class ActivityManagerService extends ActivityManagerNative } else { foreground = ' '; } - String procState; - switch (r.curProcState) { - case ActivityManager.PROCESS_STATE_PERSISTENT: - procState = "P "; - break; - case ActivityManager.PROCESS_STATE_PERSISTENT_UI: - procState = "PU"; - break; - case ActivityManager.PROCESS_STATE_TOP: - procState = "T "; - break; - case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: - procState = "IF"; - break; - case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: - procState = "IB"; - break; - case ActivityManager.PROCESS_STATE_BACKUP: - procState = "BU"; - break; - case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: - procState = "HW"; - break; - case ActivityManager.PROCESS_STATE_SERVICE: - procState = "S "; - break; - case ActivityManager.PROCESS_STATE_RECEIVER: - procState = "R "; - break; - case ActivityManager.PROCESS_STATE_HOME: - procState = "HO"; - break; - case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: - procState = "LA"; - break; - case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: - procState = "CA"; - break; - case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: - procState = "Ca"; - break; - case ActivityManager.PROCESS_STATE_CACHED_EMPTY: - procState = "CE"; - break; - default: - procState = "??"; - break; - } + String procState = ProcessList.makeProcStateString(r.curProcState); pw.print(prefix); pw.print(r.persistent ? persistentLabel : normalLabel); pw.print(" #"); @@ -11253,6 +11302,7 @@ public final class ActivityManagerService extends ActivityManagerNative } static final int[] DUMP_MEM_OOM_ADJ = new int[] { + ProcessList.NATIVE_ADJ, ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ, ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, @@ -11260,6 +11310,7 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ }; static final String[] DUMP_MEM_OOM_LABEL = new String[] { + "Native", "System", "Persistent", "Foreground", "Visible", "Perceptible", "Heavy Weight", "Backup", @@ -11267,6 +11318,7 @@ public final class ActivityManagerService extends ActivityManagerNative "Previous", "B Services", "Cached" }; static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { + "native", "sys", "pers", "fore", "vis", "percept", "heavy", "backup", @@ -11275,8 +11327,7 @@ public final class ActivityManagerService extends ActivityManagerNative }; final void dumpApplicationMemoryUsage(FileDescriptor fd, - PrintWriter pw, String prefix, String[] args, boolean brief, - PrintWriter categoryPw, StringBuilder outTag, StringBuilder outStack) { + PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) { boolean dumpDetails = false; boolean dumpDalvik = false; boolean oomOnly = false; @@ -11337,6 +11388,7 @@ public final class ActivityManagerService extends ActivityManagerNative System.arraycopy(args, opti, innerArgs, 0, args.length-opti); ArrayList<MemItem> procMems = new ArrayList<MemItem>(); + final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>(); long nativePss=0, dalvikPss=0, otherPss=0; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; @@ -11404,6 +11456,7 @@ public final class ActivityManagerService extends ActivityManagerNative (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss, pid, hasActivities); procMems.add(pssItem); + procMemsMap.put(pid, pssItem); nativePss += mi.nativePss; dalvikPss += mi.dalvikPss; @@ -11434,6 +11487,48 @@ public final class ActivityManagerService extends ActivityManagerNative } if (!isCheckinRequest && procs.size() > 1) { + // If we are showing aggregations, also look for native processes to + // include so that our aggregations are more accurate. + updateCpuStatsNow(); + synchronized (mProcessCpuThread) { + final int N = mProcessCpuTracker.countStats(); + for (int i=0; i<N; i++) { + ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); + if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) { + if (mi == null) { + mi = new Debug.MemoryInfo(); + } + if (!brief && !oomOnly) { + Debug.getMemoryInfo(st.pid, mi); + } else { + mi.nativePss = (int)Debug.getPss(st.pid, tmpLong); + mi.nativePrivateDirty = (int)tmpLong[0]; + } + + final long myTotalPss = mi.getTotalPss(); + totalPss += myTotalPss; + + MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")", + st.name, myTotalPss, st.pid, false); + procMems.add(pssItem); + + nativePss += mi.nativePss; + dalvikPss += mi.dalvikPss; + otherPss += mi.otherPss; + for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { + long mem = mi.getOtherPss(j); + miscPss[j] += mem; + otherPss -= mem; + } + oomPss[0] += myTotalPss; + if (oomProcs[0] == null) { + oomProcs[0] = new ArrayList<MemItem>(); + } + oomProcs[0].add(pssItem); + } + } + } + ArrayList<MemItem> catMems = new ArrayList<MemItem>(); catMems.add(new MemItem("Native", "Native", nativePss, -1)); @@ -11456,68 +11551,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - if (outTag != null || outStack != null) { - if (outTag != null) { - appendMemBucket(outTag, totalPss, "total", false); - } - if (outStack != null) { - appendMemBucket(outStack, totalPss, "total", true); - } - boolean firstLine = true; - for (int i=0; i<oomMems.size(); i++) { - MemItem miCat = oomMems.get(i); - if (miCat.subitems == null || miCat.subitems.size() < 1) { - continue; - } - if (miCat.id < ProcessList.SERVICE_ADJ - || miCat.id == ProcessList.HOME_APP_ADJ - || miCat.id == ProcessList.PREVIOUS_APP_ADJ) { - if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) { - outTag.append(" / "); - } - if (outStack != null) { - if (miCat.id >= ProcessList.FOREGROUND_APP_ADJ) { - if (firstLine) { - outStack.append(":"); - firstLine = false; - } - outStack.append("\n\t at "); - } else { - outStack.append("$"); - } - } - for (int j=0; j<miCat.subitems.size(); j++) { - MemItem memi = miCat.subitems.get(j); - if (j > 0) { - if (outTag != null) { - outTag.append(" "); - } - if (outStack != null) { - outStack.append("$"); - } - } - if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) { - appendMemBucket(outTag, memi.pss, memi.shortLabel, false); - } - if (outStack != null) { - appendMemBucket(outStack, memi.pss, memi.shortLabel, true); - } - } - if (outStack != null && miCat.id >= ProcessList.FOREGROUND_APP_ADJ) { - outStack.append("("); - for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) { - if (DUMP_MEM_OOM_ADJ[k] == miCat.id) { - outStack.append(DUMP_MEM_OOM_LABEL[k]); - outStack.append(":"); - outStack.append(DUMP_MEM_OOM_ADJ[k]); - } - } - outStack.append(")"); - } - } - } - } - if (!brief && !oomOnly && !isCompact) { pw.println(); pw.println("Total PSS by process:"); diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index fb81b3a..54593aa 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -19,6 +19,7 @@ package com.android.server.am; import java.io.FileOutputStream; import java.io.IOException; +import android.app.ActivityManager; import com.android.internal.util.MemInfoReader; import com.android.server.wm.WindowManagerService; @@ -98,6 +99,10 @@ final class ProcessList { // The system process runs at the default adjustment. static final int SYSTEM_ADJ = -16; + // Special code for native processes that are not being managed by the system (so + // don't have an oom adj assigned by the system). + static final int NATIVE_ADJ = -17; + // Memory pages are 4K. static final int PAGE_SIZE = 4*1024; @@ -278,6 +283,46 @@ final class ProcessList { return (totalProcessLimit*2)/3; } + private static String buildOomTag(String prefix, String space, int val, int base) { + if (val == base) { + if (space == null) return prefix; + return prefix + " "; + } + return prefix + "+" + Integer.toString(val-base); + } + + public static String makeOomAdjString(int setAdj) { + if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) { + return buildOomTag("cch", " ", setAdj, ProcessList.CACHED_APP_MIN_ADJ); + } else if (setAdj >= ProcessList.SERVICE_B_ADJ) { + return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ); + } else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) { + return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ); + } else if (setAdj >= ProcessList.HOME_APP_ADJ) { + return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ); + } else if (setAdj >= ProcessList.SERVICE_ADJ) { + return buildOomTag("svc ", null, setAdj, ProcessList.SERVICE_ADJ); + } else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { + return buildOomTag("hvy ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ); + } else if (setAdj >= ProcessList.BACKUP_APP_ADJ) { + return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ); + } else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { + return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ); + } else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) { + return buildOomTag("vis ", null, setAdj, ProcessList.VISIBLE_APP_ADJ); + } else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) { + return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ); + } else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) { + return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ); + } else if (setAdj >= ProcessList.SYSTEM_ADJ) { + return buildOomTag("sys ", null, setAdj, ProcessList.SYSTEM_ADJ); + } else if (setAdj >= ProcessList.NATIVE_ADJ) { + return buildOomTag("ntv ", null, setAdj, ProcessList.NATIVE_ADJ); + } else { + return Integer.toString(setAdj); + } + } + // The minimum amount of time after a state change it is safe ro collect PSS. public static final int PSS_MIN_TIME_FROM_STATE_CHANGE = 15*1000; @@ -366,6 +411,70 @@ final class ProcessList { return sProcStateToProcMem[procState1] != sProcStateToProcMem[procState2]; } + public static String makeProcStateString(int curProcState) { + String procState; + switch (curProcState) { + case -1: + procState = "N "; + break; + case ActivityManager.PROCESS_STATE_PERSISTENT: + procState = "P "; + break; + case ActivityManager.PROCESS_STATE_PERSISTENT_UI: + procState = "PU"; + break; + case ActivityManager.PROCESS_STATE_TOP: + procState = "T "; + break; + case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: + procState = "IF"; + break; + case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: + procState = "IB"; + break; + case ActivityManager.PROCESS_STATE_BACKUP: + procState = "BU"; + break; + case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: + procState = "HW"; + break; + case ActivityManager.PROCESS_STATE_SERVICE: + procState = "S "; + break; + case ActivityManager.PROCESS_STATE_RECEIVER: + procState = "R "; + break; + case ActivityManager.PROCESS_STATE_HOME: + procState = "HO"; + break; + case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: + procState = "LA"; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: + procState = "CA"; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + procState = "Ca"; + break; + case ActivityManager.PROCESS_STATE_CACHED_EMPTY: + procState = "CE"; + break; + default: + procState = "??"; + break; + } + return procState; + } + + public static void appendRamKb(StringBuilder sb, long ramKb) { + for (int j=0, fact=10; j<6; j++, fact*=10) { + if (ramKb < fact) { + sb.append(' '); + } + } + sb.append(ramKb); + } + public static long computeNextPssTime(int procState, boolean first, boolean sleeping, long now) { final long[] table = sleeping diff --git a/services/java/com/android/server/am/ProcessMemInfo.java b/services/java/com/android/server/am/ProcessMemInfo.java new file mode 100644 index 0000000..c94694e --- /dev/null +++ b/services/java/com/android/server/am/ProcessMemInfo.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +public class ProcessMemInfo { + final String name; + final int pid; + final int oomAdj; + final int procState; + final String adjType; + final String adjReason; + long pss; + + public ProcessMemInfo(String _name, int _pid, int _oomAdj, int _procState, + String _adjType, String _adjReason) { + name = _name; + pid = _pid; + oomAdj = _oomAdj; + procState = _procState; + adjType = _adjType; + adjReason = _adjReason; + } +} diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 892271f..35e06b6 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -529,9 +529,8 @@ final class ProcessRecord { } public String makeAdjReason() { - StringBuilder sb = new StringBuilder(128); - sb.append('(').append(adjType).append(')'); if (adjSource != null || adjTarget != null) { + StringBuilder sb = new StringBuilder(128); sb.append(' '); if (adjTarget instanceof ComponentName) { sb.append(((ComponentName)adjTarget).flattenToShortString()); @@ -550,8 +549,9 @@ final class ProcessRecord { } else { sb.append("{null}"); } + return sb.toString(); } - return sb.toString(); + return null; } /* |