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
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index a4432c3..3a3f07e 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -27,6 +27,7 @@
 #include <sys/time.h>
 #include <errno.h>
 #include <assert.h>
+#include <ctype.h>
 
 #ifdef HAVE_MALLOC_H
 #include <malloc.h>
@@ -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 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 @@
         }
 
         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 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 @@
 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));
 }