From 0e3328fbdd3845b0e2bec364e951498eaee6b079 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 17 Jul 2011 13:31:17 -0700 Subject: [PATCH] Rework and fix "adb shell dumpsys meminfo" We now collect more detailed information splitting the maps into additional useful categories. Fixed some bugs in account, such as not correctly handling all of the current dalvik allocations. The activity manager now prints a final summary of all pss organized by the apps and the categories. Change-Id: Iafc5f27c998095812b1483c6803b8e0f0587aeae --- core/java/android/app/ActivityThread.java | 94 +++++--- .../android/app/ApplicationThreadNative.java | 66 ++++++ core/java/android/app/IApplicationThread.java | 4 + core/java/android/os/Debug.java | 39 ++++ core/jni/android_os_Debug.cpp | 217 +++++++++++------- .../server/am/ActivityManagerService.java | 84 ++++++- 6 files changed, 375 insertions(+), 129 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8994b1761eceb..c6a746b237f11 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -413,10 +413,10 @@ public final class ActivityThread { native private void dumpGraphicsInfo(FileDescriptor fd); private final class ApplicationThread extends ApplicationThreadNative { - private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s"; - private static final String ONE_COUNT_COLUMN = "%17s %8d"; - private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d"; - private static final String TWO_COUNT_COLUMNS_DB = "%20s %8d %20s %8d"; + private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s"; + private static final String ONE_COUNT_COLUMN = "%21s %8d"; + private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; + private static final String TWO_COUNT_COLUMNS_DB = "%21s %8d %21s %8d"; private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; // Formatting for checkin service - update version if row format changes @@ -729,12 +729,17 @@ public final class ActivityThread { } @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (args != null && args.length == 1 && args[0].equals("graphics")) { + public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) { + FileOutputStream fout = new FileOutputStream(fd); + PrintWriter pw = new PrintWriter(fout); + try { + return dumpMemInfo(fd, pw, args); + } finally { pw.flush(); - dumpGraphicsInfo(fd); - return; } + } + + private Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, PrintWriter pw, String[] args) { long nativeMax = Debug.getNativeHeapSize() / 1024; long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; long nativeFree = Debug.getNativeHeapFreeSize() / 1024; @@ -742,14 +747,6 @@ public final class ActivityThread { Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); Debug.getMemoryInfo(memInfo); - final int nativeShared = memInfo.nativeSharedDirty; - final int dalvikShared = memInfo.dalvikSharedDirty; - final int otherShared = memInfo.otherSharedDirty; - - final int nativePrivate = memInfo.nativePrivateDirty; - final int dalvikPrivate = memInfo.dalvikPrivateDirty; - final int otherPrivate = memInfo.otherPrivateDirty; - Runtime runtime = Runtime.getRuntime(); long dalvikMax = runtime.totalMemory() / 1024; @@ -813,16 +810,18 @@ public final class ActivityThread { pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(','); // Heap info - shared - pw.print(nativeShared); pw.print(','); - pw.print(dalvikShared); pw.print(','); - pw.print(otherShared); pw.print(','); - pw.print(nativeShared + dalvikShared + otherShared); pw.print(','); + pw.print(memInfo.nativeSharedDirty); pw.print(','); + pw.print(memInfo.dalvikSharedDirty); pw.print(','); + pw.print(memInfo.otherSharedDirty); pw.print(','); + pw.print(memInfo.nativeSharedDirty + memInfo.dalvikSharedDirty + + memInfo.otherSharedDirty); pw.print(','); // Heap info - private - pw.print(nativePrivate); pw.print(','); - pw.print(dalvikPrivate); pw.print(','); - pw.print(otherPrivate); pw.print(','); - pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(','); + pw.print(memInfo.nativePrivateDirty); pw.print(','); + pw.print(memInfo.dalvikPrivateDirty); pw.print(','); + pw.print(memInfo.otherPrivateDirty); pw.print(','); + pw.print(memInfo.nativePrivateDirty + memInfo.dalvikPrivateDirty + + memInfo.otherPrivateDirty); pw.print(','); // Object counts pw.print(viewInstanceCount); pw.print(','); @@ -850,24 +849,38 @@ public final class ActivityThread { pw.print(','); } - return; + return memInfo; } // otherwise, show human-readable format - printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total"); - printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax); - printRow(pw, HEAP_COLUMN, "allocated:", nativeAllocated, dalvikAllocated, "N/A", - nativeAllocated + dalvikAllocated); - printRow(pw, HEAP_COLUMN, "free:", nativeFree, dalvikFree, "N/A", - nativeFree + dalvikFree); + printRow(pw, HEAP_COLUMN, "", "", "Shared", "Private", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_COLUMN, "", "Pss", "Dirty", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_COLUMN, "", "------", "------", "------", "------", "------", + "------"); + printRow(pw, HEAP_COLUMN, "Native", memInfo.nativePss, memInfo.nativeSharedDirty, + memInfo.nativePrivateDirty, nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_COLUMN, "Dalvik", memInfo.dalvikPss, memInfo.dalvikSharedDirty, + memInfo.dalvikPrivateDirty, dalvikMax, dalvikAllocated, dalvikFree); - printRow(pw, HEAP_COLUMN, "(Pss):", memInfo.nativePss, memInfo.dalvikPss, - memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); + int otherPss = memInfo.otherPss; + int otherSharedDirty = memInfo.otherSharedDirty; + int otherPrivateDirty = memInfo.otherPrivateDirty; - printRow(pw, HEAP_COLUMN, "(shared dirty):", nativeShared, dalvikShared, otherShared, - nativeShared + dalvikShared + otherShared); - printRow(pw, HEAP_COLUMN, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate, - nativePrivate + dalvikPrivate + otherPrivate); + for (int i=0; i CREATOR = new Creator() { diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index a4432c38c32a0..3a3f07e0dacf3 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef HAVE_MALLOC_H #include @@ -35,28 +36,50 @@ namespace android { -static jfieldID dalvikPss_field; -static jfieldID dalvikPrivateDirty_field; -static jfieldID dalvikSharedDirty_field; -static jfieldID nativePss_field; -static jfieldID nativePrivateDirty_field; -static jfieldID nativeSharedDirty_field; -static jfieldID otherPss_field; -static jfieldID otherPrivateDirty_field; -static jfieldID otherSharedDirty_field; +enum { + HEAP_UNKNOWN, + HEAP_DALVIK, + HEAP_NATIVE, + HEAP_CURSOR, + HEAP_ASHMEM, + HEAP_UNKNOWN_DEV, + HEAP_SO, + HEAP_JAR, + HEAP_APK, + HEAP_TTF, + HEAP_DEX, + HEAP_UNKNOWN_MAP, + + _NUM_HEAP, + _NUM_CORE_HEAP = HEAP_NATIVE+1 +}; + +struct stat_fields { + jfieldID pss_field; + jfieldID privateDirty_field; + jfieldID sharedDirty_field; +}; + +struct stat_field_names { + const char* pss_name; + const char* privateDirty_name; + const char* sharedDirty_name; +}; + +static stat_fields stat_fields[_NUM_CORE_HEAP]; + +static stat_field_names stat_field_names[_NUM_CORE_HEAP] = { + { "otherPss", "otherPrivateDirty", "otherSharedDirty" }, + { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" }, + { "nativePss", "nativePrivateDirty", "nativeSharedDirty" } +}; + +jfieldID otherStats_field; struct stats_t { - int dalvikPss; - int dalvikPrivateDirty; - int dalvikSharedDirty; - - int nativePss; - int nativePrivateDirty; - int nativeSharedDirty; - - int otherPss; - int otherPrivateDirty; - int otherSharedDirty; + int pss; + int privateDirty; + int sharedDirty; }; #define BINDER_STATS "/proc/binder/stats" @@ -94,48 +117,71 @@ static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz) static void read_mapinfo(FILE *fp, stats_t* stats) { char line[1024]; - int len; + int len, nameLen; bool skip, done = false; - unsigned start = 0, size = 0, resident = 0, pss = 0; + unsigned size = 0, resident = 0, pss = 0; unsigned shared_clean = 0, shared_dirty = 0; unsigned private_clean = 0, private_dirty = 0; unsigned referenced = 0; unsigned temp; - int isNativeHeap; - int isDalvikHeap; - int isSqliteHeap; + unsigned long int start; + unsigned long int end = 0; + unsigned long int prevEnd = 0; + char* name; + int name_pos; - if(fgets(line, 1024, fp) == 0) return; + int whichHeap = HEAP_UNKNOWN; + int prevHeap = HEAP_UNKNOWN; + + if(fgets(line, sizeof(line), fp) == 0) return; while (!done) { - isNativeHeap = 0; - isDalvikHeap = 0; - isSqliteHeap = 0; + prevHeap = whichHeap; + prevEnd = end; + whichHeap = HEAP_UNKNOWN; skip = false; len = strlen(line); if (len < 1) return; line[--len] = 0; - /* ignore guard pages */ - if (len > 18 && line[17] == '-') skip = true; + if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) { + skip = true; + } else { + while (isspace(line[name_pos])) { + name_pos += 1; + } + name = line + name_pos; + nameLen = strlen(name); - start = strtoul(line, 0, 16); - - if (strstr(line, "[heap]")) { - isNativeHeap = 1; - } else if (strstr(line, "/dalvik-LinearAlloc")) { - isDalvikHeap = 1; - } else if (strstr(line, "/mspace/dalvik-heap")) { - isDalvikHeap = 1; - } else if (strstr(line, "/dalvik-heap-bitmap/")) { - isDalvikHeap = 1; - } else if (strstr(line, "/data/dalvik-cache/")) { - isDalvikHeap = 1; - } else if (strstr(line, "/tmp/sqlite-heap")) { - isSqliteHeap = 1; + if (strstr(name, "[heap]") == name) { + whichHeap = HEAP_NATIVE; + } else if (strstr(name, "/dev/ashmem/dalvik-") == name) { + whichHeap = HEAP_DALVIK; + } else if (strstr(name, "/dev/ashmem/CursorWindow") == name) { + whichHeap = HEAP_CURSOR; + } else if (strstr(name, "/dev/ashmem/") == name) { + whichHeap = HEAP_ASHMEM; + } else if (strstr(name, "/dev/") == name) { + whichHeap = HEAP_UNKNOWN_DEV; + } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) { + whichHeap = HEAP_SO; + } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) { + whichHeap = HEAP_JAR; + } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) { + whichHeap = HEAP_APK; + } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) { + whichHeap = HEAP_TTF; + } else if (nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) { + whichHeap = HEAP_DEX; + } else if (nameLen > 0) { + whichHeap = HEAP_UNKNOWN_MAP; + } else if (start == prevEnd && prevHeap == HEAP_SO) { + // bss section of a shared library. + whichHeap = HEAP_SO; + } } //LOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap, @@ -171,21 +217,9 @@ static void read_mapinfo(FILE *fp, stats_t* stats) } if (!skip) { - if (isNativeHeap) { - stats->nativePss += pss; - stats->nativePrivateDirty += private_dirty; - stats->nativeSharedDirty += shared_dirty; - } else if (isDalvikHeap) { - stats->dalvikPss += pss; - stats->dalvikPrivateDirty += private_dirty; - stats->dalvikSharedDirty += shared_dirty; - } else if ( isSqliteHeap) { - // ignore - } else { - stats->otherPss += pss; - stats->otherPrivateDirty += private_dirty; - stats->otherSharedDirty += shared_dirty; - } + stats[whichHeap].pss += pss; + stats[whichHeap].privateDirty += private_dirty; + stats[whichHeap].sharedDirty += shared_dirty; } } } @@ -206,22 +240,38 @@ static void load_maps(int pid, stats_t* stats) static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, jint pid, jobject object) { - stats_t stats; - memset(&stats, 0, sizeof(stats_t)); + stats_t stats[_NUM_HEAP]; + memset(&stats, 0, sizeof(stats)); - load_maps(pid, &stats); + load_maps(pid, stats); - env->SetIntField(object, dalvikPss_field, stats.dalvikPss); - env->SetIntField(object, dalvikPrivateDirty_field, stats.dalvikPrivateDirty); - env->SetIntField(object, dalvikSharedDirty_field, stats.dalvikSharedDirty); + for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) { + stats[HEAP_UNKNOWN].pss += stats[i].pss; + stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty; + stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty; + } + + for (int i=0; i<_NUM_CORE_HEAP; i++) { + env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss); + env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty); + env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty); + } - env->SetIntField(object, nativePss_field, stats.nativePss); - env->SetIntField(object, nativePrivateDirty_field, stats.nativePrivateDirty); - env->SetIntField(object, nativeSharedDirty_field, stats.nativeSharedDirty); + jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field); - env->SetIntField(object, otherPss_field, stats.otherPss); - env->SetIntField(object, otherPrivateDirty_field, stats.otherPrivateDirty); - env->SetIntField(object, otherSharedDirty_field, stats.otherSharedDirty); + jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0); + if (otherArray == NULL) { + return; + } + + int j=0; + for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) { + otherArray[j++] = stats[i].pss; + otherArray[j++] = stats[i].privateDirty; + otherArray[j++] = stats[i].sharedDirty; + } + + env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0); } static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) @@ -487,19 +537,18 @@ static JNINativeMethod gMethods[] = { int register_android_os_Debug(JNIEnv *env) { jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); - - dalvikPss_field = env->GetFieldID(clazz, "dalvikPss", "I"); - dalvikPrivateDirty_field = env->GetFieldID(clazz, "dalvikPrivateDirty", "I"); - dalvikSharedDirty_field = env->GetFieldID(clazz, "dalvikSharedDirty", "I"); - nativePss_field = env->GetFieldID(clazz, "nativePss", "I"); - nativePrivateDirty_field = env->GetFieldID(clazz, "nativePrivateDirty", "I"); - nativeSharedDirty_field = env->GetFieldID(clazz, "nativeSharedDirty", "I"); - - otherPss_field = env->GetFieldID(clazz, "otherPss", "I"); - otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I"); - otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I"); - + for (int i=0; i<_NUM_CORE_HEAP; i++) { + stat_fields[i].pss_field = + env->GetFieldID(clazz, stat_field_names[i].pss_name, "I"); + stat_fields[i].privateDirty_field = + env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I"); + stat_fields[i].sharedDirty_field = + env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I"); + } + + otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); + return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index f546cf1fe402a..a8fb1ed656401 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -8885,25 +8885,59 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("Applications Graphics Acceleration Info:"); pw.println("Uptime: " + uptime + " Realtime: " + realtime); - String callArgs[] = {"graphics"}; for (int i = procs.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = procs.get(i); if (r.thread != null) { pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **"); pw.flush(); try { - TransferPipe.goDump(r.thread.asBinder(), fd, callArgs); + TransferPipe tp = new TransferPipe(); + try { + r.thread.dumpGfxInfo(tp.getWriteFd().getFileDescriptor(), args); + tp.go(fd); + } finally { + tp.kill(); + } } catch (IOException e) { - pw.println("Failure: " + e); + pw.println("Failure while dumping the app: " + r); pw.flush(); } catch (RemoteException e) { - pw.println("Got RemoteException!"); + pw.println("Got a RemoteException while dumping the app " + r); pw.flush(); } } } } + final static class MemItem { + final String label; + final long pss; + + public MemItem(String _label, long _pss) { + label = _label; + pss = _pss; + } + } + + final void dumpMemItems(PrintWriter pw, String prefix, ArrayList items) { + Collections.sort(items, new Comparator() { + @Override + public int compare(MemItem lhs, MemItem rhs) { + if (lhs.pss < rhs.pss) { + return 1; + } else if (lhs.pss > rhs.pss) { + return -1; + } + return 0; + } + }); + + for (int i=0; i procs = collectProcesses(pw, args); @@ -8923,6 +8957,11 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("Applications Memory Usage (kB):"); pw.println("Uptime: " + uptime + " Realtime: " + realtime); } + + ArrayList procMems = new ArrayList(); + long nativePss=0, dalvikPss=0, otherPss=0; + long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; + for (int i = procs.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = procs.get(i); if (r.thread != null) { @@ -8930,19 +8969,48 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **"); pw.flush(); } + Debug.MemoryInfo mi = null; try { - TransferPipe.goDump(r.thread.asBinder(), fd, args); - } catch (IOException e) { - pw.println("Failure: " + e); - pw.flush(); + mi = r.thread.dumpMemInfo(fd, args); } catch (RemoteException e) { if (!isCheckinRequest) { pw.println("Got RemoteException!"); pw.flush(); } } + if (!isCheckinRequest && mi != null) { + procMems.add(new MemItem(r.processName + " (pid " + r.pid + ")", + mi.getTotalPss())); + + nativePss += mi.nativePss; + dalvikPss += mi.dalvikPss; + otherPss += mi.otherPss; + for (int j=0; j 1) { + ArrayList catMems = new ArrayList(); + + catMems.add(new MemItem("Native", nativePss)); + catMems.add(new MemItem("Dalvik", dalvikPss)); + catMems.add(new MemItem("Unknown", otherPss)); + for (int j=0; j