* commit '28eeb42012018bfa3cffc77e9a970e8f5c13f70b': Implement #10749688: Improve low memory reporting
This commit is contained in:
@ -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
|
||||||
|
@ -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];
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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) {
|
||||||
|
@ -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:");
|
||||||
|
@ -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
|
||||||
|
37
services/java/com/android/server/am/ProcessMemInfo.java
Normal file
37
services/java/com/android/server/am/ProcessMemInfo.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user