From 8e69257a9c7e9c1781e1f53d8856358ada38921d Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 10 Sep 2013 19:06:15 -0700 Subject: [PATCH] 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 --- core/java/android/os/Debug.java | 22 + .../internal/os/ProcessCpuTracker.java | 26 +- .../android/internal/util/MemInfoReader.java | 34 +- core/jni/android_os_Debug.cpp | 83 +++ .../com/android/server/am/ActiveServices.java | 13 +- .../server/am/ActivityManagerService.java | 497 ++++++++++-------- .../com/android/server/am/ProcessList.java | 109 ++++ .../com/android/server/am/ProcessMemInfo.java | 37 ++ .../com/android/server/am/ProcessRecord.java | 6 +- 9 files changed, 552 insertions(+), 275 deletions(-) create mode 100644 services/java/com/android/server/am/ProcessMemInfo.java diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 8f68fc1a3cbcd..eb91238451e7d 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1018,6 +1018,28 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo */ public static native long getPss(int pid, long[] outUss); + /** @hide */ + public static final int MEMINFO_TOTAL = 0; + /** @hide */ + public static final int MEMINFO_FREE = 1; + /** @hide */ + public static final int MEMINFO_BUFFERS = 2; + /** @hide */ + public static final int MEMINFO_CACHED = 3; + /** @hide */ + public static final int MEMINFO_SHMEM = 4; + /** @hide */ + public static final int MEMINFO_SLAB = 5; + /** @hide */ + public static final int MEMINFO_COUNT = 6; + + /** + * Retrieves /proc/meminfo. outSizes is filled with fields + * as defined by MEMINFO_* offsets. + * @hide + */ + public static native void getMemInfo(long[] outSizes); + /** * Establish an object allocation limit in the current thread. * This feature was never enabled in release builds. The diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 30ca73efb42b8..58cd60dc465e4 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -49,12 +49,12 @@ public class ProcessCpuTracker { PROC_SPACE_TERM, PROC_SPACE_TERM, PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_OUT_LONG, // 9: minor faults + PROC_SPACE_TERM|PROC_OUT_LONG, // 10: minor faults PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_OUT_LONG, // 11: major faults + PROC_SPACE_TERM|PROC_OUT_LONG, // 12: major faults PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime - PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime + PROC_SPACE_TERM|PROC_OUT_LONG, // 14: utime + PROC_SPACE_TERM|PROC_OUT_LONG, // 15: stime }; static final int PROCESS_STAT_MINOR_FAULTS = 0; @@ -69,7 +69,7 @@ public class ProcessCpuTracker { private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] { PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_PARENS|PROC_OUT_STRING, // 1: name + PROC_SPACE_TERM|PROC_PARENS|PROC_OUT_STRING, // 2: name PROC_SPACE_TERM, PROC_SPACE_TERM, PROC_SPACE_TERM, @@ -77,19 +77,20 @@ public class ProcessCpuTracker { PROC_SPACE_TERM, PROC_SPACE_TERM, PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_OUT_LONG, // 9: minor faults + PROC_SPACE_TERM|PROC_OUT_LONG, // 10: minor faults PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_OUT_LONG, // 11: major faults + PROC_SPACE_TERM|PROC_OUT_LONG, // 12: major faults PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime - PROC_SPACE_TERM|PROC_OUT_LONG, // 14: stime + PROC_SPACE_TERM|PROC_OUT_LONG, // 14: utime + PROC_SPACE_TERM|PROC_OUT_LONG, // 15: stime PROC_SPACE_TERM, PROC_SPACE_TERM, PROC_SPACE_TERM, PROC_SPACE_TERM, PROC_SPACE_TERM, PROC_SPACE_TERM, - PROC_SPACE_TERM|PROC_OUT_LONG, // 21: vsize + PROC_SPACE_TERM, + PROC_SPACE_TERM|PROC_OUT_LONG, // 23: vsize }; static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1; @@ -190,6 +191,10 @@ public class ProcessCpuTracker { public String name; public int nameWidth; + // vsize capture when process first detected; can be used to + // filter out kernel processes. + public long vsize; + public long base_uptime; public long rel_uptime; @@ -444,6 +449,7 @@ public class ProcessCpuTracker { // are actually kernel threads... do we want to? Some // of them do use CPU, but there can be a *lot* that are // not doing anything. + st.vsize = procStats[PROCESS_FULL_STAT_VSIZE]; if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) { st.interesting = true; st.baseName = procStatsString[0]; diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java index 850e1f0860f4f..ad6543305b599 100644 --- a/core/java/com/android/internal/util/MemInfoReader.java +++ b/core/java/com/android/internal/util/MemInfoReader.java @@ -18,6 +18,7 @@ package com.android.internal.util; import java.io.FileInputStream; +import android.os.Debug; import android.os.StrictMode; public class MemInfoReader { @@ -63,34 +64,11 @@ public class MemInfoReader { // /proc/ and /sys/ files perhaps? StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); try { - mTotalSize = 0; - mFreeSize = 0; - mCachedSize = 0; - FileInputStream is = new FileInputStream("/proc/meminfo"); - int len = is.read(mBuffer); - is.close(); - final int BUFLEN = mBuffer.length; - int count = 0; - for (int i=0; i= '0' && *p <= '9') p++; + if (*p != 0) { + *p = 0; + p++; + } + mem[i] = atoll(num); + numFound++; + break; + } + i++; + } + while (*p && *p != '\n') { + p++; + } + if (*p) p++; + } + + int maxNum = env->GetArrayLength(out); + jlong* outArray = env->GetLongArrayElements(out, 0); + if (outArray != NULL) { + for (int i=0; iReleaseLongArrayElements(out, outArray, 0); +} + static jint read_binder_stat(const char* stat) { FILE* fp = fopen(BINDER_STATS, "r"); @@ -790,6 +871,8 @@ static JNINativeMethod gMethods[] = { (void*) android_os_Debug_getPss }, { "getPss", "(I[J)J", (void*) android_os_Debug_getPssPid }, + { "getMemInfo", "([J)V", + (void*) android_os_Debug_getMemInfo }, { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", (void*) android_os_Debug_dumpNativeHeap }, { "getBinderSentTransactions", "()I", diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index 4521037e117ef..b4d9da012845d 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 96b7030dba4bd..5f2265c1b8b6b 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 memInfos = (ArrayList)msg.obj; Thread thread = new Thread() { @Override public void run() { - StringBuilder dropBuilder = new StringBuilder(1024); + final SparseArray infoMap + = new SparseArray(memInfos.size()); + for (int i=0, N=memInfos.size(); i 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() { + @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= 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 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 memInfos + = doReport ? new ArrayList(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 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 procMems = new ArrayList(); + final SparseArray procMemsMap = new SparseArray(); 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 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(); + } + oomProcs[0].add(pssItem); + } + } + } + ArrayList catMems = new ArrayList(); 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= ProcessList.FOREGROUND_APP_ADJ) { - if (firstLine) { - outStack.append(":"); - firstLine = false; - } - outStack.append("\n\t at "); - } else { - outStack.append("$"); - } - } - for (int j=0; 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= 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 0000000000000..c94694e7c4b1c --- /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 892271fb239d3..35e06b655d984 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; } /*