am 28eeb420: Merge "Implement #10749688: Improve low memory reporting" into klp-dev

* commit '28eeb42012018bfa3cffc77e9a970e8f5c13f70b':
  Implement #10749688: Improve low memory reporting
This commit is contained in:
Dianne Hackborn
2013-09-13 17:15:23 -07:00
committed by Android Git Automerger
9 changed files with 552 additions and 275 deletions

View File

@ -1018,6 +1018,28 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
*/ */
public static native long getPss(int pid, long[] outUss); 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. * Establish an object allocation limit in the current thread.
* This feature was never enabled in release builds. The * This feature was never enabled in release builds. The

View File

@ -49,12 +49,12 @@ public class ProcessCpuTracker {
PROC_SPACE_TERM, PROC_SPACE_TERM,
PROC_SPACE_TERM, 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_SPACE_TERM|PROC_OUT_LONG, // 11: major faults PROC_SPACE_TERM|PROC_OUT_LONG, // 12: major faults
PROC_SPACE_TERM, PROC_SPACE_TERM,
PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime PROC_SPACE_TERM|PROC_OUT_LONG, // 14: utime
PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime PROC_SPACE_TERM|PROC_OUT_LONG, // 15: stime
}; };
static final int PROCESS_STAT_MINOR_FAULTS = 0; 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[] { private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
PROC_SPACE_TERM, 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, 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_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_SPACE_TERM|PROC_OUT_LONG, // 11: major faults PROC_SPACE_TERM|PROC_OUT_LONG, // 12: major faults
PROC_SPACE_TERM, PROC_SPACE_TERM,
PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime PROC_SPACE_TERM|PROC_OUT_LONG, // 14: utime
PROC_SPACE_TERM|PROC_OUT_LONG, // 14: stime 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_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; static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1;
@ -190,6 +191,10 @@ public class ProcessCpuTracker {
public String name; public String name;
public int nameWidth; 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 base_uptime;
public long rel_uptime; public long rel_uptime;
@ -444,6 +449,7 @@ public class ProcessCpuTracker {
// are actually kernel threads... do we want to? Some // are actually kernel threads... do we want to? Some
// of them do use CPU, but there can be a *lot* that are // of them do use CPU, but there can be a *lot* that are
// not doing anything. // not doing anything.
st.vsize = procStats[PROCESS_FULL_STAT_VSIZE];
if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) { if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
st.interesting = true; st.interesting = true;
st.baseName = procStatsString[0]; st.baseName = procStatsString[0];

View File

@ -18,6 +18,7 @@ package com.android.internal.util;
import java.io.FileInputStream; import java.io.FileInputStream;
import android.os.Debug;
import android.os.StrictMode; import android.os.StrictMode;
public class MemInfoReader { public class MemInfoReader {
@ -63,34 +64,11 @@ public class MemInfoReader {
// /proc/ and /sys/ files perhaps? // /proc/ and /sys/ files perhaps?
StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
try { try {
mTotalSize = 0; long[] infos = new long[Debug.MEMINFO_COUNT];
mFreeSize = 0; Debug.getMemInfo(infos);
mCachedSize = 0; mTotalSize = infos[Debug.MEMINFO_TOTAL] * 1024;
FileInputStream is = new FileInputStream("/proc/meminfo"); mFreeSize = infos[Debug.MEMINFO_FREE] * 1024;
int len = is.read(mBuffer); mCachedSize = infos[Debug.MEMINFO_CACHED] * 1024;
is.close();
final int BUFLEN = mBuffer.length;
int count = 0;
for (int i=0; i<len && count < 3; i++) {
if (matchText(mBuffer, i, "MemTotal")) {
i += 8;
mTotalSize = extractMemValue(mBuffer, i);
count++;
} else if (matchText(mBuffer, i, "MemFree")) {
i += 7;
mFreeSize = extractMemValue(mBuffer, i);
count++;
} else if (matchText(mBuffer, i, "Cached")) {
i += 6;
mCachedSize = extractMemValue(mBuffer, i);
count++;
}
while (i < BUFLEN && mBuffer[i] != '\n') {
i++;
}
}
} catch (java.io.FileNotFoundException e) {
} catch (java.io.IOException e) {
} finally { } finally {
StrictMode.setThreadPolicy(savedPolicy); StrictMode.setThreadPolicy(savedPolicy);
} }

View File

@ -516,6 +516,87 @@ static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
return android_os_Debug_getPssPid(env, clazz, getpid(), NULL); return android_os_Debug_getPssPid(env, clazz, getpid(), NULL);
} }
static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
{
char buffer[1024];
int numFound = 0;
if (out == NULL) {
jniThrowNullPointerException(env, "out == null");
return;
}
int fd = open("/proc/meminfo", O_RDONLY);
if (fd < 0) {
printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
return;
}
const int len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
if (len < 0) {
printf("Empty /proc/meminfo");
return;
}
buffer[len] = 0;
static const char* const tags[] = {
"MemTotal:",
"MemFree:",
"Buffers:",
"Cached:",
"Shmem:",
"Slab:",
NULL
};
static const int tagsLen[] = {
9,
8,
8,
7,
6,
5,
0
};
long mem[] = { 0, 0, 0, 0, 0, 0 };
char* p = buffer;
while (*p && numFound < 6) {
int i = 0;
while (tags[i]) {
if (strncmp(p, tags[i], tagsLen[i]) == 0) {
p += tagsLen[i];
while (*p == ' ') p++;
char* num = p;
while (*p >= '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; i<maxNum && tags[i]; i++) {
outArray[i] = mem[i];
}
}
env->ReleaseLongArrayElements(out, outArray, 0);
}
static jint read_binder_stat(const char* stat) static jint read_binder_stat(const char* stat)
{ {
FILE* fp = fopen(BINDER_STATS, "r"); FILE* fp = fopen(BINDER_STATS, "r");
@ -790,6 +871,8 @@ static JNINativeMethod gMethods[] = {
(void*) android_os_Debug_getPss }, (void*) android_os_Debug_getPss },
{ "getPss", "(I[J)J", { "getPss", "(I[J)J",
(void*) android_os_Debug_getPssPid }, (void*) android_os_Debug_getPssPid },
{ "getMemInfo", "([J)V",
(void*) android_os_Debug_getMemInfo },
{ "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V",
(void*) android_os_Debug_dumpNativeHeap }, (void*) android_os_Debug_dumpNativeHeap },
{ "getBinderSentTransactions", "()I", { "getBinderSentTransactions", "()I",

View File

@ -328,8 +328,17 @@ public final class ActiveServices {
addToStarting = true; addToStarting = true;
if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Not delaying, but counting as bg: " + r); if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Not delaying, but counting as bg: " + r);
} else if (DEBUG_DELAYED_STATS) { } else if (DEBUG_DELAYED_STATS) {
Slog.v(TAG, "Not potential delay (state=" + proc.curProcState StringBuilder sb = new StringBuilder(128);
+ " " + proc.makeAdjReason() + "): " + r); 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) { } else if (DEBUG_DELAYED_STATS) {
if (callerFg) { if (callerFg) {

View File

@ -1370,64 +1370,167 @@ public final class ActivityManagerService extends ActivityManagerNative
break; break;
} }
case REPORT_MEM_USAGE: { case REPORT_MEM_USAGE: {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj;
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;
}
Thread thread = new Thread() { Thread thread = new Thread() {
@Override public void run() { @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); 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(); StringWriter oomSw = new StringWriter();
PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256); PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256);
StringWriter catSw = new StringWriter(); StringWriter catSw = new StringWriter();
PrintWriter catPw = new FastPrintWriter(catSw, false, 256); PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
String[] emptyArgs = new String[] { }; String[] emptyArgs = new String[] { };
StringBuilder tag = new StringBuilder(128); dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw);
StringBuilder stack = new StringBuilder(128); oomPw.flush();
tag.append("Low on memory -- "); String oomString = oomSw.toString();
dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw, */
tag, stack);
dropBuilder.append(stack); dropBuilder.append(stack);
dropBuilder.append('\n'); dropBuilder.append('\n');
dropBuilder.append('\n'); dropBuilder.append('\n');
oomPw.flush(); dropBuilder.append(logBuilder);
String oomString = oomSw.toString(); dropBuilder.append('\n');
/*
dropBuilder.append(oomString); dropBuilder.append(oomString);
dropBuilder.append('\n'); dropBuilder.append('\n');
logBuilder.append(oomString); */
try { StringWriter catSw = new StringWriter();
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) {
}
synchronized (ActivityManagerService.this) { synchronized (ActivityManagerService.this) {
PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
String[] emptyArgs = new String[] { };
catPw.println(); catPw.println();
dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null);
catPw.println(); catPw.println();
@ -1435,12 +1538,13 @@ public final class ActivityManagerService extends ActivityManagerNative
false, false, null); false, false, null);
catPw.println(); catPw.println();
dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
catPw.flush();
} }
catPw.flush();
dropBuilder.append(catSw.toString()); dropBuilder.append(catSw.toString());
addErrorToDropBox("lowmem", null, "system_server", null, addErrorToDropBox("lowmem", null, "system_server", null,
null, tag.toString(), dropBuilder.toString(), null, 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) { synchronized (ActivityManagerService.this) {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
if (mLastMemUsageReportTime < now) { if (mLastMemUsageReportTime < now) {
@ -1691,8 +1795,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return; return;
} }
mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null);
false, null, null, null);
} }
} }
@ -3272,6 +3375,66 @@ public final class ActivityManagerService extends ActivityManagerNative
return appIndex >= 0 ? mLruProcesses.get(appIndex) : null; 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, final void appDiedLocked(ProcessRecord app, int pid,
IApplicationThread thread) { IApplicationThread thread) {
@ -3297,42 +3460,7 @@ public final class ActivityManagerService extends ActivityManagerNative
handleAppDiedLocked(app, false, true); handleAppDiedLocked(app, false, true);
if (doLowMem) { if (doLowMem) {
// If there are no longer any background processes running, doLowMemReportIfNeededLocked(app);
// 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();
}
} }
} else if (app.pid != pid) { } else if (app.pid != pid) {
// A new process has already been started. // A new process has already been started.
@ -3851,6 +3979,8 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), false, true, "kill all background"); removeProcessLocked(procs.get(i), false, true, "kill all background");
} }
updateOomAdjLocked();
doLowMemReportIfNeededLocked(null);
} }
} finally { } finally {
Binder.restoreCallingIdentity(callingId); Binder.restoreCallingIdentity(callingId);
@ -4160,6 +4290,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
} }
updateOomAdjLocked();
return N > 0; return N > 0;
} }
@ -10792,14 +10923,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, private static final int dumpProcessList(PrintWriter pw,
ActivityManagerService service, List list, ActivityManagerService service, List list,
String prefix, String normalLabel, String persistentLabel, String prefix, String normalLabel, String persistentLabel,
@ -10871,34 +10994,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=list.size()-1; i>=0; i--) { for (int i=list.size()-1; i>=0; i--) {
ProcessRecord r = list.get(i).first; ProcessRecord r = list.get(i).first;
String oomAdj; String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
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);
}
char schedGroup; char schedGroup;
switch (r.setSchedGroup) { switch (r.setSchedGroup) {
case Process.THREAD_GROUP_BG_NONINTERACTIVE: case Process.THREAD_GROUP_BG_NONINTERACTIVE:
@ -10919,54 +11015,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} else { } else {
foreground = ' '; foreground = ' ';
} }
String procState; String procState = ProcessList.makeProcStateString(r.curProcState);
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;
}
pw.print(prefix); pw.print(prefix);
pw.print(r.persistent ? persistentLabel : normalLabel); pw.print(r.persistent ? persistentLabel : normalLabel);
pw.print(" #"); pw.print(" #");
@ -11254,6 +11303,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
static final int[] DUMP_MEM_OOM_ADJ = new int[] { static final int[] DUMP_MEM_OOM_ADJ = new int[] {
ProcessList.NATIVE_ADJ,
ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ, ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ,
ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ,
ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
@ -11261,6 +11311,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ
}; };
static final String[] DUMP_MEM_OOM_LABEL = new String[] { static final String[] DUMP_MEM_OOM_LABEL = new String[] {
"Native",
"System", "Persistent", "Foreground", "System", "Persistent", "Foreground",
"Visible", "Perceptible", "Visible", "Perceptible",
"Heavy Weight", "Backup", "Heavy Weight", "Backup",
@ -11268,6 +11319,7 @@ public final class ActivityManagerService extends ActivityManagerNative
"Previous", "B Services", "Cached" "Previous", "B Services", "Cached"
}; };
static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
"native",
"sys", "pers", "fore", "sys", "pers", "fore",
"vis", "percept", "vis", "percept",
"heavy", "backup", "heavy", "backup",
@ -11276,8 +11328,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}; };
final void dumpApplicationMemoryUsage(FileDescriptor fd, final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) {
PrintWriter categoryPw, StringBuilder outTag, StringBuilder outStack) {
boolean dumpDetails = false; boolean dumpDetails = false;
boolean dumpDalvik = false; boolean dumpDalvik = false;
boolean oomOnly = false; boolean oomOnly = false;
@ -11338,6 +11389,7 @@ public final class ActivityManagerService extends ActivityManagerNative
System.arraycopy(args, opti, innerArgs, 0, args.length-opti); System.arraycopy(args, opti, innerArgs, 0, args.length-opti);
ArrayList<MemItem> procMems = new ArrayList<MemItem>(); ArrayList<MemItem> procMems = new ArrayList<MemItem>();
final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
long nativePss=0, dalvikPss=0, otherPss=0; long nativePss=0, dalvikPss=0, otherPss=0;
long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
@ -11405,6 +11457,7 @@ public final class ActivityManagerService extends ActivityManagerNative
(hasActivities ? " / activities)" : ")"), (hasActivities ? " / activities)" : ")"),
r.processName, myTotalPss, pid, hasActivities); r.processName, myTotalPss, pid, hasActivities);
procMems.add(pssItem); procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
nativePss += mi.nativePss; nativePss += mi.nativePss;
dalvikPss += mi.dalvikPss; dalvikPss += mi.dalvikPss;
@ -11435,6 +11488,48 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
if (!isCheckinRequest && procs.size() > 1) { 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>(); ArrayList<MemItem> catMems = new ArrayList<MemItem>();
catMems.add(new MemItem("Native", "Native", nativePss, -1)); catMems.add(new MemItem("Native", "Native", nativePss, -1));
@ -11457,68 +11552,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) { if (!brief && !oomOnly && !isCompact) {
pw.println(); pw.println();
pw.println("Total PSS by process:"); pw.println("Total PSS by process:");

View File

@ -19,6 +19,7 @@ package com.android.server.am;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import android.app.ActivityManager;
import com.android.internal.util.MemInfoReader; import com.android.internal.util.MemInfoReader;
import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowManagerService;
@ -98,6 +99,10 @@ final class ProcessList {
// The system process runs at the default adjustment. // The system process runs at the default adjustment.
static final int SYSTEM_ADJ = -16; 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. // Memory pages are 4K.
static final int PAGE_SIZE = 4*1024; static final int PAGE_SIZE = 4*1024;
@ -278,6 +283,46 @@ final class ProcessList {
return (totalProcessLimit*2)/3; 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. // 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; public static final int PSS_MIN_TIME_FROM_STATE_CHANGE = 15*1000;
@ -366,6 +411,70 @@ final class ProcessList {
return sProcStateToProcMem[procState1] != sProcStateToProcMem[procState2]; 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, public static long computeNextPssTime(int procState, boolean first, boolean sleeping,
long now) { long now) {
final long[] table = sleeping final long[] table = sleeping

View File

@ -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;
}
}

View File

@ -529,9 +529,8 @@ final class ProcessRecord {
} }
public String makeAdjReason() { public String makeAdjReason() {
StringBuilder sb = new StringBuilder(128);
sb.append('(').append(adjType).append(')');
if (adjSource != null || adjTarget != null) { if (adjSource != null || adjTarget != null) {
StringBuilder sb = new StringBuilder(128);
sb.append(' '); sb.append(' ');
if (adjTarget instanceof ComponentName) { if (adjTarget instanceof ComponentName) {
sb.append(((ComponentName)adjTarget).flattenToShortString()); sb.append(((ComponentName)adjTarget).flattenToShortString());
@ -550,8 +549,9 @@ final class ProcessRecord {
} else { } else {
sb.append("{null}"); sb.append("{null}");
} }
return sb.toString();
} }
return sb.toString(); return null;
} }
/* /*