Loading core/java/android/app/ActivityThread.java +59 −39 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(','); Loading Loading @@ -850,25 +849,39 @@ 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", 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); int otherPss = memInfo.otherPss; int otherSharedDirty = memInfo.otherSharedDirty; int otherPrivateDirty = memInfo.otherPrivateDirty; for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { printRow(pw, HEAP_COLUMN, memInfo.getOtherLabel(i), memInfo.getOtherPss(i), memInfo.getOtherSharedDirty(i), memInfo.getOtherPrivateDirty(i), "", "", ""); otherPss -= memInfo.getOtherPss(i); otherSharedDirty -= memInfo.getOtherSharedDirty(i); otherPrivateDirty -= memInfo.getOtherPrivateDirty(i); } printRow(pw, HEAP_COLUMN, "Unknown", otherPss, otherSharedDirty, otherPrivateDirty, "", "", ""); printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), nativeMax+dalvikMax, nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); printRow(pw, HEAP_COLUMN, "(Pss):", memInfo.nativePss, memInfo.dalvikPss, memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); printRow(pw, HEAP_COLUMN, "(shared dirty):", nativeShared, dalvikShared, otherShared, nativeShared + dalvikShared + otherShared); printRow(pw, HEAP_COLUMN, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate, nativePrivate + dalvikPrivate + otherPrivate); pw.println(" "); pw.println(" Objects"); printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewAncestors:", Loading Loading @@ -916,6 +929,13 @@ public final class ActivityThread { pw.println(" Asset Allocations"); pw.print(assetAlloc); } return memInfo; } @Override public void dumpGfxInfo(FileDescriptor fd, String[] args) { dumpGraphicsInfo(fd); } private void printRow(PrintWriter pw, String format, Object...objs) { Loading core/java/android/app/ApplicationThreadNative.java +66 −0 Original line number Diff line number Diff line Loading @@ -485,6 +485,48 @@ public abstract class ApplicationThreadNative extends Binder scheduleTrimMemory(level); return true; } case DUMP_MEM_INFO_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); ParcelFileDescriptor fd = data.readFileDescriptor(); String[] args = data.readStringArray(); Debug.MemoryInfo mi = null; if (fd != null) { try { mi = dumpMemInfo(fd.getFileDescriptor(), args); } finally { try { fd.close(); } catch (IOException e) { // swallowed, not propagated back to the caller } } } reply.writeNoException(); mi.writeToParcel(reply, 0); return true; } case DUMP_GFX_INFO_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); ParcelFileDescriptor fd = data.readFileDescriptor(); String[] args = data.readStringArray(); if (fd != null) { try { dumpGfxInfo(fd.getFileDescriptor(), args); } finally { try { fd.close(); } catch (IOException e) { // swallowed, not propagated back to the caller } } } reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); Loading Loading @@ -1004,4 +1046,28 @@ class ApplicationThreadProxy implements IApplicationThread { mRemote.transact(SCHEDULE_TRIM_MEMORY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); } public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeFileDescriptor(fd); data.writeStringArray(args); mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0); reply.readException(); Debug.MemoryInfo info = new Debug.MemoryInfo(); info.readFromParcel(reply); data.recycle(); reply.recycle(); return info; } public void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeFileDescriptor(fd); data.writeStringArray(args); mRemote.transact(DUMP_GFX_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } } core/java/android/app/IApplicationThread.java +4 −0 Original line number Diff line number Diff line Loading @@ -120,6 +120,8 @@ public interface IApplicationThread extends IInterface { void setCoreSettings(Bundle coreSettings) throws RemoteException; void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException; void scheduleTrimMemory(int level) throws RemoteException; Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException; String descriptor = "android.app.IApplicationThread"; Loading Loading @@ -164,4 +166,6 @@ public interface IApplicationThread extends IInterface { int SET_CORE_SETTINGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39; int UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40; int SCHEDULE_TRIM_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41; int DUMP_MEM_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+42; int DUMP_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43; } core/java/android/os/Debug.java +39 −0 Original line number Diff line number Diff line Loading @@ -129,6 +129,11 @@ public final class Debug /** The shared dirty pages used by everything else. */ public int otherSharedDirty; /** @hide */ public static final int NUM_OTHER_STATS = 9; private int[] otherStats = new int[NUM_OTHER_STATS*3]; public MemoryInfo() { } Loading @@ -153,6 +158,38 @@ public final class Debug return dalvikSharedDirty + nativeSharedDirty + otherSharedDirty; } /* @hide */ public int getOtherPss(int which) { return otherStats[which*3]; } /* @hide */ public int getOtherPrivateDirty(int which) { return otherStats[which*3 + 1]; } /* @hide */ public int getOtherSharedDirty(int which) { return otherStats[which*3 + 2]; } /* @hide */ public static String getOtherLabel(int which) { switch (which) { case 0: return "Cursor"; case 1: return "Ashmem"; case 2: return "Other dev"; case 3: return ".so mmap"; case 4: return ".jar mmap"; case 5: return ".apk mmap"; case 6: return ".ttf mmap"; case 7: return ".dex mmap"; case 8: return "Other mmap"; default: return "????"; } } public int describeContents() { return 0; } Loading @@ -167,6 +204,7 @@ public final class Debug dest.writeInt(otherPss); dest.writeInt(otherPrivateDirty); dest.writeInt(otherSharedDirty); dest.writeIntArray(otherStats); } public void readFromParcel(Parcel source) { Loading @@ -179,6 +217,7 @@ public final class Debug otherPss = source.readInt(); otherPrivateDirty = source.readInt(); otherSharedDirty = source.readInt(); otherStats = source.createIntArray(); } public static final Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() { Loading core/jni/android_os_Debug.cpp +134 −85 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <sys/time.h> #include <errno.h> #include <assert.h> #include <ctype.h> #ifdef HAVE_MALLOC_H #include <malloc.h> Loading @@ -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 stats_t { int dalvikPss; int dalvikPrivateDirty; int dalvikSharedDirty; struct stat_fields { jfieldID pss_field; jfieldID privateDirty_field; jfieldID sharedDirty_field; }; int nativePss; int nativePrivateDirty; int nativeSharedDirty; struct stat_field_names { const char* pss_name; const char* privateDirty_name; const char* sharedDirty_name; }; int otherPss; int otherPrivateDirty; int otherSharedDirty; 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 pss; int privateDirty; int sharedDirty; }; #define BINDER_STATS "/proc/binder/stats" Loading Loading @@ -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; int whichHeap = HEAP_UNKNOWN; int prevHeap = HEAP_UNKNOWN; if(fgets(line, 1024, fp) == 0) return; 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; 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 (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); 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, Loading Loading @@ -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; } } } Loading @@ -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; } env->SetIntField(object, nativePss_field, stats.nativePss); env->SetIntField(object, nativePrivateDirty_field, stats.nativePrivateDirty); env->SetIntField(object, nativeSharedDirty_field, stats.nativeSharedDirty); 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); } 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) Loading Loading @@ -488,17 +538,16 @@ 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"); 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"); } otherPss_field = env->GetFieldID(clazz, "otherPss", "I"); otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I"); otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I"); otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); } Loading Loading
core/java/android/app/ActivityThread.java +59 −39 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(','); Loading Loading @@ -850,25 +849,39 @@ 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", 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); int otherPss = memInfo.otherPss; int otherSharedDirty = memInfo.otherSharedDirty; int otherPrivateDirty = memInfo.otherPrivateDirty; for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { printRow(pw, HEAP_COLUMN, memInfo.getOtherLabel(i), memInfo.getOtherPss(i), memInfo.getOtherSharedDirty(i), memInfo.getOtherPrivateDirty(i), "", "", ""); otherPss -= memInfo.getOtherPss(i); otherSharedDirty -= memInfo.getOtherSharedDirty(i); otherPrivateDirty -= memInfo.getOtherPrivateDirty(i); } printRow(pw, HEAP_COLUMN, "Unknown", otherPss, otherSharedDirty, otherPrivateDirty, "", "", ""); printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), nativeMax+dalvikMax, nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); printRow(pw, HEAP_COLUMN, "(Pss):", memInfo.nativePss, memInfo.dalvikPss, memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); printRow(pw, HEAP_COLUMN, "(shared dirty):", nativeShared, dalvikShared, otherShared, nativeShared + dalvikShared + otherShared); printRow(pw, HEAP_COLUMN, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate, nativePrivate + dalvikPrivate + otherPrivate); pw.println(" "); pw.println(" Objects"); printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewAncestors:", Loading Loading @@ -916,6 +929,13 @@ public final class ActivityThread { pw.println(" Asset Allocations"); pw.print(assetAlloc); } return memInfo; } @Override public void dumpGfxInfo(FileDescriptor fd, String[] args) { dumpGraphicsInfo(fd); } private void printRow(PrintWriter pw, String format, Object...objs) { Loading
core/java/android/app/ApplicationThreadNative.java +66 −0 Original line number Diff line number Diff line Loading @@ -485,6 +485,48 @@ public abstract class ApplicationThreadNative extends Binder scheduleTrimMemory(level); return true; } case DUMP_MEM_INFO_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); ParcelFileDescriptor fd = data.readFileDescriptor(); String[] args = data.readStringArray(); Debug.MemoryInfo mi = null; if (fd != null) { try { mi = dumpMemInfo(fd.getFileDescriptor(), args); } finally { try { fd.close(); } catch (IOException e) { // swallowed, not propagated back to the caller } } } reply.writeNoException(); mi.writeToParcel(reply, 0); return true; } case DUMP_GFX_INFO_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); ParcelFileDescriptor fd = data.readFileDescriptor(); String[] args = data.readStringArray(); if (fd != null) { try { dumpGfxInfo(fd.getFileDescriptor(), args); } finally { try { fd.close(); } catch (IOException e) { // swallowed, not propagated back to the caller } } } reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); Loading Loading @@ -1004,4 +1046,28 @@ class ApplicationThreadProxy implements IApplicationThread { mRemote.transact(SCHEDULE_TRIM_MEMORY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); } public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeFileDescriptor(fd); data.writeStringArray(args); mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0); reply.readException(); Debug.MemoryInfo info = new Debug.MemoryInfo(); info.readFromParcel(reply); data.recycle(); reply.recycle(); return info; } public void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeFileDescriptor(fd); data.writeStringArray(args); mRemote.transact(DUMP_GFX_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } }
core/java/android/app/IApplicationThread.java +4 −0 Original line number Diff line number Diff line Loading @@ -120,6 +120,8 @@ public interface IApplicationThread extends IInterface { void setCoreSettings(Bundle coreSettings) throws RemoteException; void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException; void scheduleTrimMemory(int level) throws RemoteException; Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException; String descriptor = "android.app.IApplicationThread"; Loading Loading @@ -164,4 +166,6 @@ public interface IApplicationThread extends IInterface { int SET_CORE_SETTINGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39; int UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40; int SCHEDULE_TRIM_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41; int DUMP_MEM_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+42; int DUMP_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43; }
core/java/android/os/Debug.java +39 −0 Original line number Diff line number Diff line Loading @@ -129,6 +129,11 @@ public final class Debug /** The shared dirty pages used by everything else. */ public int otherSharedDirty; /** @hide */ public static final int NUM_OTHER_STATS = 9; private int[] otherStats = new int[NUM_OTHER_STATS*3]; public MemoryInfo() { } Loading @@ -153,6 +158,38 @@ public final class Debug return dalvikSharedDirty + nativeSharedDirty + otherSharedDirty; } /* @hide */ public int getOtherPss(int which) { return otherStats[which*3]; } /* @hide */ public int getOtherPrivateDirty(int which) { return otherStats[which*3 + 1]; } /* @hide */ public int getOtherSharedDirty(int which) { return otherStats[which*3 + 2]; } /* @hide */ public static String getOtherLabel(int which) { switch (which) { case 0: return "Cursor"; case 1: return "Ashmem"; case 2: return "Other dev"; case 3: return ".so mmap"; case 4: return ".jar mmap"; case 5: return ".apk mmap"; case 6: return ".ttf mmap"; case 7: return ".dex mmap"; case 8: return "Other mmap"; default: return "????"; } } public int describeContents() { return 0; } Loading @@ -167,6 +204,7 @@ public final class Debug dest.writeInt(otherPss); dest.writeInt(otherPrivateDirty); dest.writeInt(otherSharedDirty); dest.writeIntArray(otherStats); } public void readFromParcel(Parcel source) { Loading @@ -179,6 +217,7 @@ public final class Debug otherPss = source.readInt(); otherPrivateDirty = source.readInt(); otherSharedDirty = source.readInt(); otherStats = source.createIntArray(); } public static final Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() { Loading
core/jni/android_os_Debug.cpp +134 −85 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <sys/time.h> #include <errno.h> #include <assert.h> #include <ctype.h> #ifdef HAVE_MALLOC_H #include <malloc.h> Loading @@ -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 stats_t { int dalvikPss; int dalvikPrivateDirty; int dalvikSharedDirty; struct stat_fields { jfieldID pss_field; jfieldID privateDirty_field; jfieldID sharedDirty_field; }; int nativePss; int nativePrivateDirty; int nativeSharedDirty; struct stat_field_names { const char* pss_name; const char* privateDirty_name; const char* sharedDirty_name; }; int otherPss; int otherPrivateDirty; int otherSharedDirty; 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 pss; int privateDirty; int sharedDirty; }; #define BINDER_STATS "/proc/binder/stats" Loading Loading @@ -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; int whichHeap = HEAP_UNKNOWN; int prevHeap = HEAP_UNKNOWN; if(fgets(line, 1024, fp) == 0) return; 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; 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 (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); 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, Loading Loading @@ -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; } } } Loading @@ -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; } env->SetIntField(object, nativePss_field, stats.nativePss); env->SetIntField(object, nativePrivateDirty_field, stats.nativePrivateDirty); env->SetIntField(object, nativeSharedDirty_field, stats.nativeSharedDirty); 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); } 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) Loading Loading @@ -488,17 +538,16 @@ 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"); 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"); } otherPss_field = env->GetFieldID(clazz, "otherPss", "I"); otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I"); otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I"); otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); } Loading