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);
/** @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

View File

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

View File

@ -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<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) {
long[] infos = new long[Debug.MEMINFO_COUNT];
Debug.getMemInfo(infos);
mTotalSize = infos[Debug.MEMINFO_TOTAL] * 1024;
mFreeSize = infos[Debug.MEMINFO_FREE] * 1024;
mCachedSize = infos[Debug.MEMINFO_CACHED] * 1024;
} finally {
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);
}
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)
{
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",

View File

@ -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) {

View File

@ -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);
}
}
@ -3272,6 +3375,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) {
@ -3297,42 +3460,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.
@ -3851,6 +3979,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);
@ -4160,6 +4290,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;
}
@ -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,
ActivityManagerService service, List list,
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--) {
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:
@ -10919,54 +11015,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(" #");
@ -11254,6 +11303,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,
@ -11261,6 +11311,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",
@ -11268,6 +11319,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",
@ -11276,8 +11328,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;
@ -11338,6 +11389,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];
@ -11405,6 +11457,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;
@ -11435,6 +11488,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));
@ -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) {
pw.println();
pw.println("Total PSS by process:");

View File

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

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() {
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;
}
/*