From 8d652f8f7e48370b92314bbd547ddf2d66a1ed4b Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 11 Apr 2017 16:29:18 -0700 Subject: Implement dumpheap -m. Adds an option to dumpheap that calls mallocInfo and write the xml produced to a file. Bug: 36871013 Test: Run am dumpheap -m , am dumpheap -n , am dumpheap Test: and verified that they produce the correct data. Change-Id: Ifb9e878ea621c7cacc3aa6e3a889c414681dee2d --- core/java/android/app/ActivityThread.java | 17 +++++-- core/java/android/app/IActivityManager.aidl | 4 +- core/java/android/app/IApplicationThread.aidl | 3 +- core/java/android/os/Debug.java | 7 +++ core/jni/android_os_Debug.cpp | 54 +++++++++++++++++----- .../android/server/am/ActivityManagerService.java | 9 ++-- .../server/am/ActivityManagerShellCommand.java | 6 ++- 7 files changed, 75 insertions(+), 25 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c994e135f408..3d2e0619af92 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -658,6 +658,8 @@ public final class ActivityThread { } static final class DumpHeapData { + public boolean managed; + public boolean mallocInfo; public boolean runGc; String path; ParcelFileDescriptor fd; @@ -1025,12 +1027,15 @@ public final class ActivityThread { } @Override - public void dumpHeap(boolean managed, boolean runGc, String path, ParcelFileDescriptor fd) { + public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path, + ParcelFileDescriptor fd) { DumpHeapData dhd = new DumpHeapData(); + dhd.managed = managed; + dhd.mallocInfo = mallocInfo; dhd.runGc = runGc; dhd.path = path; dhd.fd = fd; - sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/); + sendMessage(H.DUMP_HEAP, dhd, 0, 0, true /*async*/); } public void attachAgent(String agent) { @@ -1762,7 +1767,7 @@ public final class ActivityThread { case SCHEDULE_CRASH: throw new RemoteServiceException((String)msg.obj); case DUMP_HEAP: - handleDumpHeap(msg.arg1 != 0, (DumpHeapData)msg.obj); + handleDumpHeap((DumpHeapData) msg.obj); break; case DUMP_ACTIVITY: handleDumpActivity((DumpComponentInfo)msg.obj); @@ -5173,13 +5178,13 @@ public final class ActivityThread { } } - static final void handleDumpHeap(boolean managed, DumpHeapData dhd) { + static void handleDumpHeap(DumpHeapData dhd) { if (dhd.runGc) { System.gc(); System.runFinalization(); System.gc(); } - if (managed) { + if (dhd.managed) { try { Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor()); } catch (IOException e) { @@ -5192,6 +5197,8 @@ public final class ActivityThread { Slog.w(TAG, "Failure closing profile fd", e); } } + } else if (dhd.mallocInfo) { + Debug.dumpNativeMallocInfo(dhd.fd.getFileDescriptor()); } else { Debug.dumpNativeHeap(dhd.fd.getFileDescriptor()); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 3be6f97504bd..df1a412d464e 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -277,8 +277,8 @@ interface IActivityManager { int checkGrantUriPermission(int callingUid, in String targetPkg, in Uri uri, int modeFlags, int userId); // Cause the specified process to dump the specified heap. - boolean dumpHeap(in String process, int userId, boolean managed, boolean runGc, in String path, - in ParcelFileDescriptor fd); + boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo, + boolean runGc, in String path, in ParcelFileDescriptor fd); int startActivities(in IApplicationThread caller, in String callingPackage, in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo, in Bundle options, int userId); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 7191fc471077..aeed7e1287d2 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -117,7 +117,8 @@ oneway interface IApplicationThread { void scheduleSuicide(); void dispatchPackageBroadcast(int cmd, in String[] packages); void scheduleCrash(in String msg); - void dumpHeap(boolean managed, boolean runGc, in String path, in ParcelFileDescriptor fd); + void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path, + in ParcelFileDescriptor fd); void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix, in String[] args); void clearDnsCache(); diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index f243f377cb56..de5974216f71 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1813,6 +1813,13 @@ public final class Debug */ public static native void dumpNativeHeap(FileDescriptor fd); + /** + * Writes malloc info data to the specified file descriptor. + * + * @hide + */ + public static native void dumpNativeMallocInfo(FileDescriptor fd); + /** * Returns a count of the extant instances of a class. * diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 76140c046611..16a923098a27 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -48,10 +48,14 @@ namespace android { -using UniqueFile = std::unique_ptr; +static void safeFclose(FILE* fp) { + if (fp) fclose(fp); +} + +using UniqueFile = std::unique_ptr; static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) { - return UniqueFile(fopen(path, mode), fclose); + return UniqueFile(fopen(path, mode), safeFclose); } enum { @@ -972,21 +976,16 @@ static void dumpNativeHeap(FILE* fp) fprintf(fp, "END\n"); } -/* - * Dump the native heap, writing human-readable output to the specified - * file descriptor. - */ -static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, - jobject fileDescriptor) +static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp) { if (fileDescriptor == NULL) { jniThrowNullPointerException(env, "fd == null"); - return; + return false; } int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (origFd < 0) { jniThrowRuntimeException(env, "Invalid file descriptor"); - return; + return false; } /* dup() the descriptor so we don't close the original with fclose() */ @@ -994,14 +993,28 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, if (fd < 0) { ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); jniThrowRuntimeException(env, "dup() failed"); - return; + return false; } - UniqueFile fp(fdopen(fd, "w"), fclose); + fp.reset(fdopen(fd, "w")); if (fp == nullptr) { ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); close(fd); jniThrowRuntimeException(env, "fdopen() failed"); + return false; + } + return true; +} + +/* + * Dump the native heap, writing human-readable output to the specified + * file descriptor. + */ +static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject, + jobject fileDescriptor) +{ + UniqueFile fp(nullptr, safeFclose); + if (!openFile(env, fileDescriptor, fp)) { return; } @@ -1010,6 +1023,21 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, ALOGD("Native heap dump complete.\n"); } +/* + * Dump the native malloc info, writing xml output to the specified + * file descriptor. + */ +static void android_os_Debug_dumpNativeMallocInfo(JNIEnv* env, jobject, + jobject fileDescriptor) +{ + UniqueFile fp(nullptr, safeFclose); + if (!openFile(env, fileDescriptor, fp)) { + return; + } + + malloc_info(0, fp.get()); +} + static bool dumpTraces(JNIEnv* env, jint pid, jstring fileName, jint timeoutSecs, DebuggerdDumpType dumpType) { const ScopedUtfChars fileNameChars(env, fileName); @@ -1070,6 +1098,8 @@ static const JNINativeMethod gMethods[] = { (void*) android_os_Debug_getMemInfo }, { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", (void*) android_os_Debug_dumpNativeHeap }, + { "dumpNativeMallocInfo", "(Ljava/io/FileDescriptor;)V", + (void*) android_os_Debug_dumpNativeMallocInfo }, { "getBinderSentTransactions", "()I", (void*) android_os_Debug_getBinderSentTransactions }, { "getBinderReceivedTransactions", "()I", diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 04df10442aa6..a5f1476cd93c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -21631,7 +21631,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting dump heap from " + myProc + " to " + heapdumpFile); - thread.dumpHeap(/* managed=*/ true, /* runGc= */ false, + thread.dumpHeap(/* managed= */ true, + /* mallocInfo= */ false, /* runGc= */ false, heapdumpFile.toString(), fd); } catch (RemoteException e) { } @@ -23394,8 +23395,8 @@ public class ActivityManagerService extends IActivityManager.Stub return proc; } - public boolean dumpHeap(String process, int userId, boolean managed, boolean runGc, - String path, ParcelFileDescriptor fd) throws RemoteException { + public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo, + boolean runGc, String path, ParcelFileDescriptor fd) throws RemoteException { try { synchronized (this) { @@ -23423,7 +23424,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - proc.thread.dumpHeap(managed, runGc, path, fd); + proc.thread.dumpHeap(managed, mallocInfo, runGc, path, fd); fd = null; return true; } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 5d242963dfb4..83eea9840043 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -784,6 +784,7 @@ final class ActivityManagerShellCommand extends ShellCommand { int runDumpHeap(PrintWriter pw) throws RemoteException { final PrintWriter err = getErrPrintWriter(); boolean managed = true; + boolean mallocInfo = false; int userId = UserHandle.USER_CURRENT; boolean runGc = false; @@ -799,6 +800,9 @@ final class ActivityManagerShellCommand extends ShellCommand { managed = false; } else if (opt.equals("-g")) { runGc = true; + } else if (opt.equals("-m")) { + managed = false; + mallocInfo = true; } else { err.println("Error: Unknown option: " + opt); return -1; @@ -814,7 +818,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - if (!mInterface.dumpHeap(process, userId, managed, runGc, heapFile, fd)) { + if (!mInterface.dumpHeap(process, userId, managed, mallocInfo, runGc, heapFile, fd)) { err.println("HEAP DUMP FAILED on process " + process); return -1; } -- cgit v1.2.3-59-g8ed1b