From 6edb5c665dcb024ae7cfb95b9a92e30dcc5777c1 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 31 Oct 2014 10:35:15 -0700 Subject: [PATCH] Improve low on RAM reporting. - Don't print every little native process. - Print in different sections, so if one is too long we don't get the rest truncated in the log. - Include other info from meminfo -- ksm and free/used/lost summary. Change-Id: Iea4ec3860212667e195d2b60b3ded23bfec78436 --- .../android/internal/util/MemInfoReader.java | 4 + .../server/am/ActivityManagerService.java | 520 ++++++++++-------- 2 files changed, 305 insertions(+), 219 deletions(-) diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java index 1dd946494d4d5..b71fa0674b4ed 100644 --- a/core/java/com/android/internal/util/MemInfoReader.java +++ b/core/java/com/android/internal/util/MemInfoReader.java @@ -106,4 +106,8 @@ public final class MemInfoReader { public long getZramTotalSizeKb() { return mInfos[Debug.MEMINFO_ZRAM_TOTAL]; } + + public long[] getRawInfo() { + return mInfos; + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9f1ce0b44501f..9179cc494f359 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1606,197 +1606,7 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList memInfos = (ArrayList)msg.obj; Thread thread = new Thread() { @Override public void run() { - 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 memInfos) { + 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 fullNativeBuilder = new StringBuilder(1024); + StringBuilder shortNativeBuilder = new StringBuilder(1024); + StringBuilder fullJavaBuilder = new StringBuilder(1024); + + boolean firstLine = true; + int lastOomAdj = Integer.MIN_VALUE; + long extraNativeRam = 0; + long cachedPss = 0; + for (int i=0, N=memInfos.size(); i= ProcessList.CACHED_APP_MIN_ADJ) { + cachedPss += mi.pss; + } + + 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= 1MB. + if (mi.pss >= 1000) { + appendMemInfo(shortNativeBuilder, mi); + } else { + extraNativeRam += mi.pss; + } + } else { + // Short form has all other details, but if we have collected RAM + // from smaller native processes let's dump a summary of that. + if (extraNativeRam > 0) { + appendBasicMemEntry(shortNativeBuilder, ProcessList.NATIVE_ADJ, + -1, extraNativeRam, "(Other native)"); + shortNativeBuilder.append('\n'); + extraNativeRam = 0; + } + appendMemInfo(fullJavaBuilder, mi); + } + } + + fullJavaBuilder.append(" "); + ProcessList.appendRamKb(fullJavaBuilder, totalPss); + fullJavaBuilder.append(" kB: TOTAL\n"); + + MemInfoReader memInfo = new MemInfoReader(); + memInfo.readMemInfo(); + final long[] infos = memInfo.getRawInfo(); + + StringBuilder memInfoBuilder = new StringBuilder(1024); + Debug.getMemInfo(infos); + memInfoBuilder.append(" MemInfo: "); + memInfoBuilder.append(infos[Debug.MEMINFO_SLAB]).append(" kB slab, "); + memInfoBuilder.append(infos[Debug.MEMINFO_SHMEM]).append(" kB shmem, "); + memInfoBuilder.append(infos[Debug.MEMINFO_VM_ALLOC_USED]).append(" kB vm alloc, "); + memInfoBuilder.append(infos[Debug.MEMINFO_PAGE_TABLES]).append(" kB page tables "); + memInfoBuilder.append(infos[Debug.MEMINFO_KERNEL_STACK]).append(" kB kernel stack\n"); + memInfoBuilder.append(" "); + memInfoBuilder.append(infos[Debug.MEMINFO_BUFFERS]).append(" kB buffers, "); + memInfoBuilder.append(infos[Debug.MEMINFO_CACHED]).append(" kB cached, "); + memInfoBuilder.append(infos[Debug.MEMINFO_MAPPED]).append(" kB mapped, "); + memInfoBuilder.append(infos[Debug.MEMINFO_FREE]).append(" kB free\n"); + if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) { + memInfoBuilder.append(" ZRAM: "); + memInfoBuilder.append(infos[Debug.MEMINFO_ZRAM_TOTAL]); + memInfoBuilder.append(" kB RAM, "); + memInfoBuilder.append(infos[Debug.MEMINFO_SWAP_TOTAL]); + memInfoBuilder.append(" kB swap total, "); + memInfoBuilder.append(infos[Debug.MEMINFO_SWAP_FREE]); + memInfoBuilder.append(" kB swap free\n"); + } + final long[] ksm = getKsmInfo(); + if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 + || ksm[KSM_VOLATILE] != 0) { + memInfoBuilder.append(" KSM: "); memInfoBuilder.append(ksm[KSM_SHARING]); + memInfoBuilder.append(" kB saved from shared "); + memInfoBuilder.append(ksm[KSM_SHARED]); memInfoBuilder.append(" kB\n"); + memInfoBuilder.append(" "); memInfoBuilder.append(ksm[KSM_UNSHARED]); + memInfoBuilder.append(" kB unshared; "); + memInfoBuilder.append(ksm[KSM_VOLATILE]); memInfoBuilder.append(" kB volatile\n"); + } + memInfoBuilder.append(" Free RAM: "); + memInfoBuilder.append(cachedPss + memInfo.getCachedSizeKb() + + memInfo.getFreeSizeKb()); + memInfoBuilder.append(" kB\n"); + memInfoBuilder.append(" Used RAM: "); + memInfoBuilder.append(totalPss - cachedPss + memInfo.getKernelUsedSizeKb()); + memInfoBuilder.append(" kB\n"); + memInfoBuilder.append(" Lost RAM: "); + memInfoBuilder.append(memInfo.getTotalSizeKb() + - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() + - memInfo.getKernelUsedSizeKb()); + memInfoBuilder.append(" kB\n"); + Slog.i(TAG, "Low on memory:"); + Slog.i(TAG, shortNativeBuilder.toString()); + Slog.i(TAG, fullJavaBuilder.toString()); + Slog.i(TAG, memInfoBuilder.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[] { }; + dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw); + oomPw.flush(); + String oomString = oomSw.toString(); + */ + dropBuilder.append("Low on memory:"); + dropBuilder.append(stack); + dropBuilder.append('\n'); + dropBuilder.append(fullNativeBuilder); + dropBuilder.append(fullJavaBuilder); + dropBuilder.append('\n'); + dropBuilder.append(memInfoBuilder); + dropBuilder.append('\n'); + /* + dropBuilder.append(oomString); + dropBuilder.append('\n'); + */ + 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(); + mServices.dumpServicesLocked(null, catPw, emptyArgs, 0, + false, false, null); + catPw.println(); + dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); + catPw.flush(); + } + dropBuilder.append(catSw.toString()); + addErrorToDropBox("lowmem", null, "system_server", null, + null, tag.toString(), dropBuilder.toString(), null, null); + //Slog.i(TAG, "Sent to dropbox:"); + //Slog.i(TAG, dropBuilder.toString()); + synchronized (ActivityManagerService.this) { + long now = SystemClock.uptimeMillis(); + if (mLastMemUsageReportTime < now) { + mLastMemUsageReportTime = now; + } + } + } + /** * Searches array of arguments for the specified string * @param args array of argument strings