diff options
author | Andy McFadden <fadden@android.com> | 2010-07-09 16:26:57 -0700 |
---|---|---|
committer | Andy McFadden <fadden@android.com> | 2010-07-12 13:35:14 -0700 |
commit | 824c510752fd6a30cdba5ed7324cb80a5043ce26 (patch) | |
tree | 701346031a5b93e71c73f4786f1a276e1f6053db /core/java | |
parent | 2707d6026240bcca6f0e35e2e1138958882e90ce (diff) | |
download | frameworks_base-824c510752fd6a30cdba5ed7324cb80a5043ce26.zip frameworks_base-824c510752fd6a30cdba5ed7324cb80a5043ce26.tar.gz frameworks_base-824c510752fd6a30cdba5ed7324cb80a5043ce26.tar.bz2 |
Allow "am" to initiate heap dumps.
This was mostly cloned from the "am profile" implementation. It's
intended to replace the old "kill -10" approach used by "runhat".
We could really use a native heap dump, so I pass a "managed"
flag through that indicates whether we want to dump the native or
managed heap. We don't currently have a native heap dump-to-file
function, so it currently just logs a warning.
(android.ddm.DdmHandleNativeHeap.getLeakInfo is a good start -- it
copies /proc/maps and then calls get_malloc_leak_info to get some
goodies. Needs some formatting to make it human-readable. I didn't
want to cram all that into this change.)
It would be useful if "am" didn't exit until the heap dump operation
completed, but I'm not sure how to do that.
Bug 2759474.
Change-Id: I46bc98067738d8c72ac0fc10002ca67bb4929271
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 37 | ||||
-rw-r--r-- | core/java/android/app/ActivityThread.java | 37 | ||||
-rw-r--r-- | core/java/android/app/ApplicationThreadNative.java | 28 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 7 | ||||
-rw-r--r-- | core/java/android/app/IApplicationThread.java | 3 | ||||
-rw-r--r-- | core/java/android/os/Debug.java | 19 |
6 files changed, 126 insertions, 5 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 1fe85e6..43a08b5 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1294,6 +1294,19 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case DUMP_HEAP_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String process = data.readString(); + boolean managed = data.readInt() != 0; + String path = data.readString(); + ParcelFileDescriptor fd = data.readInt() != 0 + ? data.readFileDescriptor() : null; + boolean res = dumpHeap(process, managed, path, fd); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -2874,6 +2887,28 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - + + public boolean dumpHeap(String process, boolean managed, + String path, ParcelFileDescriptor fd) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(process); + data.writeInt(managed ? 1 : 0); + data.writeString(path); + if (fd != null) { + data.writeInt(1); + fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + data.writeInt(0); + } + mRemote.transact(DUMP_HEAP_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + reply.recycle(); + data.recycle(); + return res; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d788be8..53883b1 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -356,6 +356,11 @@ public final class ActivityThread { ParcelFileDescriptor fd; } + private static final class DumpHeapData { + String path; + ParcelFileDescriptor 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"; @@ -623,6 +628,13 @@ public final class ActivityThread { queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0); } + public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) { + DumpHeapData dhd = new DumpHeapData(); + dhd.path = path; + dhd.fd = fd; + queueOrSendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0); + } + public void setSchedulingGroup(int group) { // Note: do this immediately, since going into the foreground // should happen regardless of what pending work we have to do @@ -874,6 +886,7 @@ public final class ActivityThread { public static final int ENABLE_JIT = 132; public static final int DISPATCH_PACKAGE_BROADCAST = 133; public static final int SCHEDULE_CRASH = 134; + public static final int DUMP_HEAP = 135; String codeToString(int code) { if (localLOGV) { switch (code) { @@ -912,6 +925,7 @@ public final class ActivityThread { case ENABLE_JIT: return "ENABLE_JIT"; case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST"; case SCHEDULE_CRASH: return "SCHEDULE_CRASH"; + case DUMP_HEAP: return "DUMP_HEAP"; } } return "(unknown)"; @@ -1037,6 +1051,9 @@ public final class ActivityThread { break; case SCHEDULE_CRASH: throw new RemoteServiceException((String)msg.obj); + case DUMP_HEAP: + handleDumpHeap(msg.arg1 != 0, (DumpHeapData)msg.obj); + break; } } @@ -3015,6 +3032,26 @@ public final class ActivityThread { } } + final void handleDumpHeap(boolean managed, DumpHeapData dhd) { + if (managed) { + try { + Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor()); + } catch (IOException e) { + Slog.w(TAG, "Managed heap dump failed on path " + dhd.path + + " -- can the process access this path?"); + } finally { + try { + dhd.fd.close(); + } catch (IOException e) { + Slog.w(TAG, "Failure closing profile fd", e); + } + } + } else { + // TODO + Slog.w(TAG, "Native heap dump not yet implemented"); + } + } + final void handleDispatchPackageBroadcast(int cmd, String[] packages) { boolean hasPkgInfo = false; if (packages != null) { diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 1c20062..dc2145f 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -403,6 +403,17 @@ public abstract class ApplicationThreadNative extends Binder scheduleCrash(msg); return true; } + + case DUMP_HEAP_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + boolean managed = data.readInt() != 0; + String path = data.readString(); + ParcelFileDescriptor fd = data.readInt() != 0 + ? data.readFileDescriptor() : null; + dumpHeap(managed, path, fd); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -829,5 +840,22 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + + public void dumpHeap(boolean managed, String path, + ParcelFileDescriptor fd) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeInt(managed ? 1 : 0); + data.writeString(path); + if (fd != null) { + data.writeInt(1); + fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + data.writeInt(0); + } + mRemote.transact(DUMP_HEAP_TRANSACTION, data, null, + IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 20c9a80..8ea59a7 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -316,7 +316,11 @@ public interface IActivityManager extends IInterface { public void crashApplication(int uid, int initialPid, String packageName, String message) throws RemoteException; - + + // Cause the specified process to dump the specified heap. + public boolean dumpHeap(String process, boolean managed, String path, + ParcelFileDescriptor fd) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -533,4 +537,5 @@ public interface IActivityManager extends IInterface { int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111; int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112; int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113; + int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index c8ef17f..039bcb9 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -97,6 +97,8 @@ public interface IApplicationThread extends IInterface { void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException; void profilerControl(boolean start, String path, ParcelFileDescriptor fd) throws RemoteException; + void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) + throws RemoteException; void setSchedulingGroup(int group) throws RemoteException; void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException; static final int PACKAGE_REMOVED = 0; @@ -140,4 +142,5 @@ public interface IApplicationThread extends IInterface { int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32; int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33; int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34; + int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35; } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 2e14667..d6b6d72 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -730,7 +730,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo } /** - * Dump "hprof" data to the specified file. This will cause a GC. + * Dump "hprof" data to the specified file. This may cause a GC. * * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof"). * @throws UnsupportedOperationException if the VM was built without @@ -742,11 +742,24 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo } /** - * Collect "hprof" and send it to DDMS. This will cause a GC. + * Like dumpHprofData(String), but takes an already-opened + * FileDescriptor to which the trace is written. The file name is also + * supplied simply for logging. Makes a dup of the file descriptor. + * + * Primarily for use by the "am" shell command. + * + * @hide + */ + public static void dumpHprofData(String fileName, FileDescriptor fd) + throws IOException { + VMDebug.dumpHprofData(fileName, fd); + } + + /** + * Collect "hprof" and send it to DDMS. This may cause a GC. * * @throws UnsupportedOperationException if the VM was built without * HPROF support. - * * @hide */ public static void dumpHprofDataDdms() { |