Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 824c5107 authored by Andy McFadden's avatar Andy McFadden
Browse files

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
parent 2707d602
Loading
Loading
Loading
Loading
+26 −0
Original line number Original line Diff line number Diff line
@@ -98,6 +98,8 @@ public class Am {
            sendBroadcast();
            sendBroadcast();
        } else if (op.equals("profile")) {
        } else if (op.equals("profile")) {
            runProfile();
            runProfile();
        } else if (op.equals("dumpheap")) {
            runDumpHeap();
        } else {
        } else {
            throw new IllegalArgumentException("Unknown command: " + op);
            throw new IllegalArgumentException("Unknown command: " + op);
        }
        }
@@ -424,6 +426,28 @@ public class Am {
        }
        }
    }
    }


    private void runDumpHeap() throws Exception {
        boolean managed = !"-n".equals(nextOption());
        String process = nextArgRequired();
        String heapFile = nextArgRequired();
        ParcelFileDescriptor fd = null;

        try {
            fd = ParcelFileDescriptor.open(
                    new File(heapFile),
                    ParcelFileDescriptor.MODE_CREATE |
                    ParcelFileDescriptor.MODE_TRUNCATE |
                    ParcelFileDescriptor.MODE_READ_WRITE);
        } catch (FileNotFoundException e) {
            System.err.println("Error: Unable to open file: " + heapFile);
            return;
        }

        if (!mAm.dumpHeap(process, managed, heapFile, fd)) {
            throw new AndroidException("HEAP DUMP FAILED on process " + process);
        }
    }

    private class IntentReceiver extends IIntentReceiver.Stub {
    private class IntentReceiver extends IIntentReceiver.Stub {
        private boolean mFinished = false;
        private boolean mFinished = false;


@@ -593,6 +617,8 @@ public class Am {
                "\n" +
                "\n" +
                "    start profiling: am profile <PROCESS> start <FILE>\n" +
                "    start profiling: am profile <PROCESS> start <FILE>\n" +
                "    stop profiling: am profile <PROCESS> stop\n" +
                "    stop profiling: am profile <PROCESS> stop\n" +
                "    dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" +
                "        -n: dump native heap instead of managed heap\n" +
                "\n" +
                "\n" +
                "    <INTENT> specifications include these flags:\n" +
                "    <INTENT> specifications include these flags:\n" +
                "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
                "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
+36 −1
Original line number Original line Diff line number Diff line
@@ -1294,6 +1294,19 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            return true;
            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);
        return super.onTransact(code, data, reply, flags);
@@ -2875,5 +2888,27 @@ class ActivityManagerProxy implements IActivityManager
        reply.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;
    private IBinder mRemote;
}
}
+37 −0
Original line number Original line Diff line number Diff line
@@ -356,6 +356,11 @@ public final class ActivityThread {
        ParcelFileDescriptor fd;
        ParcelFileDescriptor fd;
    }
    }


    private static final class DumpHeapData {
        String path;
        ParcelFileDescriptor fd;
    }

    private final class ApplicationThread extends ApplicationThreadNative {
    private final class ApplicationThread extends ApplicationThreadNative {
        private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
        private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
        private static final String ONE_COUNT_COLUMN = "%17s %8d";
        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);
            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) {
        public void setSchedulingGroup(int group) {
            // Note: do this immediately, since going into the foreground
            // Note: do this immediately, since going into the foreground
            // should happen regardless of what pending work we have to do
            // 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 ENABLE_JIT              = 132;
        public static final int DISPATCH_PACKAGE_BROADCAST = 133;
        public static final int DISPATCH_PACKAGE_BROADCAST = 133;
        public static final int SCHEDULE_CRASH          = 134;
        public static final int SCHEDULE_CRASH          = 134;
        public static final int DUMP_HEAP               = 135;
        String codeToString(int code) {
        String codeToString(int code) {
            if (localLOGV) {
            if (localLOGV) {
                switch (code) {
                switch (code) {
@@ -912,6 +925,7 @@ public final class ActivityThread {
                    case ENABLE_JIT: return "ENABLE_JIT";
                    case ENABLE_JIT: return "ENABLE_JIT";
                    case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST";
                    case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST";
                    case SCHEDULE_CRASH: return "SCHEDULE_CRASH";
                    case SCHEDULE_CRASH: return "SCHEDULE_CRASH";
                    case DUMP_HEAP: return "DUMP_HEAP";
                }
                }
            }
            }
            return "(unknown)";
            return "(unknown)";
@@ -1037,6 +1051,9 @@ public final class ActivityThread {
                    break;
                    break;
                case SCHEDULE_CRASH:
                case SCHEDULE_CRASH:
                    throw new RemoteServiceException((String)msg.obj);
                    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) {
    final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
        boolean hasPkgInfo = false;
        boolean hasPkgInfo = false;
        if (packages != null) {
        if (packages != null) {
+28 −0
Original line number Original line Diff line number Diff line
@@ -403,6 +403,17 @@ public abstract class ApplicationThreadNative extends Binder
            scheduleCrash(msg);
            scheduleCrash(msg);
            return true;
            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);
        return super.onTransact(code, data, reply, flags);
@@ -829,5 +840,22 @@ class ApplicationThreadProxy implements IApplicationThread {
        data.recycle();
        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();
    }
}
}
+6 −1
Original line number Original line Diff line number Diff line
@@ -317,6 +317,10 @@ public interface IActivityManager extends IInterface {
    public void crashApplication(int uid, int initialPid, String packageName,
    public void crashApplication(int uid, int initialPid, String packageName,
            String message) throws RemoteException;
            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
     * Private non-Binder interfaces
     */
     */
@@ -533,4 +537,5 @@ public interface IActivityManager extends IInterface {
    int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
    int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
    int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
    int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
    int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113;
    int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113;
    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
}
}
Loading