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
This commit is contained in:
Dianne Hackborn
2011-07-17 13:31:17 -07:00
parent 9cbf8e270d
commit 0e3328fbdd
6 changed files with 375 additions and 129 deletions

View File

@@ -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<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);
pw.println(" ");
pw.println(" Objects");
@@ -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) {

View File

@@ -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);
@@ -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();
}
}

View File

@@ -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";
@@ -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;
}

View File

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