diff options
| author | 2024-04-05 11:16:44 +0000 | |
|---|---|---|
| committer | 2024-04-05 11:16:44 +0000 | |
| commit | 55812da62268a74fd62f3d77ef8c22630cec9c76 (patch) | |
| tree | 4bcc7d5d035bf3f9752c1bfdd769777bcbcf514a | |
| parent | eaf775bcef15e443e8465e22da8086e62ecd0455 (diff) | |
| parent | 30cc25ec34d17a514247c425273e0aa657be3c31 (diff) | |
Merge "Add compressed bitmaps to be included in `am dumpheap`" into main am: 063ac7b8fd am: 30cc25ec34
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/3015073
Change-Id: Ifc1f2d3ad36aaa7c6d5c6f0e0c1014511541124a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
7 files changed, 124 insertions, 10 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e53bd395f7fe..3575545e202d 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1082,6 +1082,8 @@ public final class ActivityThread extends ClientTransactionHandler public boolean managed; public boolean mallocInfo; public boolean runGc; + // compression format to dump bitmaps, null if no bitmaps to be dumped + public String dumpBitmaps; String path; ParcelFileDescriptor fd; RemoteCallback finishCallback; @@ -1486,11 +1488,12 @@ public final class ActivityThread extends ClientTransactionHandler } @Override - public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path, - ParcelFileDescriptor fd, RemoteCallback finishCallback) { + public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String dumpBitmaps, + String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) { DumpHeapData dhd = new DumpHeapData(); dhd.managed = managed; dhd.mallocInfo = mallocInfo; + dhd.dumpBitmaps = dumpBitmaps; dhd.runGc = runGc; dhd.path = path; try { @@ -6859,6 +6862,9 @@ public final class ActivityThread extends ClientTransactionHandler System.runFinalization(); System.gc(); } + if (dhd.dumpBitmaps != null) { + Bitmap.dumpAll(dhd.dumpBitmaps); + } try (ParcelFileDescriptor fd = dhd.fd) { if (dhd.managed) { Debug.dumpHprofData(dhd.path, fd.getFileDescriptor()); @@ -6886,6 +6892,9 @@ public final class ActivityThread extends ClientTransactionHandler if (dhd.finishCallback != null) { dhd.finishCallback.sendResult(null); } + if (dhd.dumpBitmaps != null) { + Bitmap.dumpAll(null); // clear dump + } } final void handleDispatchPackageBroadcast(int cmd, String[] packages) { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 85611e8303bf..ffecd679d928 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -394,7 +394,7 @@ interface IActivityManager { oneway void getMimeTypeFilterAsync(in Uri uri, int userId, in RemoteCallback resultCallback); // Cause the specified process to dump the specified heap. boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo, - boolean runGc, in String path, in ParcelFileDescriptor fd, + boolean runGc, in String dumpBitmaps, in String path, in ParcelFileDescriptor fd, in RemoteCallback finishCallback); @UnsupportedAppUsage boolean isUserRunning(int userid, int flags); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 251e4e8ad834..a64261a77a29 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -119,7 +119,8 @@ oneway interface IApplicationThread { void scheduleSuicide(); void dispatchPackageBroadcast(int cmd, in String[] packages); void scheduleCrash(in String msg, int typeId, in Bundle extras); - void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path, + void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, + in String dumpBitmaps, in String path, in ParcelFileDescriptor fd, in RemoteCallback finishCallback); void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix, in String[] args); diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 250362b1e1e3..319f115d7427 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -41,12 +41,15 @@ import dalvik.annotation.optimization.CriticalNative; import libcore.util.NativeAllocationRegistry; import java.io.IOException; +import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.WeakHashMap; public final class Bitmap implements Parcelable { private static final String TAG = "Bitmap"; @@ -120,6 +123,11 @@ public final class Bitmap implements Parcelable { } /** + * @hide + */ + private static final WeakHashMap<Bitmap, Void> sAllBitmaps = new WeakHashMap<>(); + + /** * Private constructor that must receive an already allocated native bitmap * int (pointer). */ @@ -162,6 +170,9 @@ public final class Bitmap implements Parcelable { Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount); } registry.registerNativeAllocation(this, nativeBitmap); + synchronized (Bitmap.class) { + sAllBitmaps.put(this, null); + } } /** @@ -1510,6 +1521,86 @@ public final class Bitmap implements Parcelable { } /** + * @hide + */ + private static final class DumpData { + private int count; + private int format; + private long[] natives; + private byte[][] buffers; + private int max; + + public DumpData(@NonNull CompressFormat format, int max) { + this.max = max; + this.format = format.nativeInt; + this.natives = new long[max]; + this.buffers = new byte[max][]; + this.count = 0; + } + + public void add(long nativePtr, byte[] buffer) { + natives[count] = nativePtr; + buffers[count] = buffer; + count = (count >= max) ? max : count + 1; + } + + public int size() { + return count; + } + } + + /** + * @hide + */ + private static DumpData dumpData = null; + + + /** + * @hide + * + * Dump all the bitmaps with their contents compressed into dumpData + * + * @param format format of the compressed image, null to clear dump data + */ + public static void dumpAll(@Nullable String format) { + if (format == null) { + /* release the dump data */ + dumpData = null; + return; + } + final CompressFormat fmt; + if (format.equals("jpg") || format.equals("jpeg")) { + fmt = CompressFormat.JPEG; + } else if (format.equals("png")) { + fmt = CompressFormat.PNG; + } else if (format.equals("webp")) { + fmt = CompressFormat.WEBP_LOSSLESS; + } else { + Log.w(TAG, "No bitmaps dumped: unrecognized format " + format); + return; + } + + final ArrayList<Bitmap> allBitmaps; + synchronized (Bitmap.class) { + allBitmaps = new ArrayList<>(sAllBitmaps.size()); + for (Bitmap bitmap : sAllBitmaps.keySet()) { + if (bitmap != null && !bitmap.isRecycled()) { + allBitmaps.add(bitmap); + } + } + } + + dumpData = new DumpData(fmt, allBitmaps.size()); + for (Bitmap bitmap : allBitmaps) { + ByteArrayOutputStream bas = new ByteArrayOutputStream(); + if (bitmap.compress(fmt, 90, bas)) { + dumpData.add(bitmap.getNativeInstance(), bas.toByteArray()); + } + } + Log.i(TAG, dumpData.size() + "/" + allBitmaps.size() + " bitmaps dumped"); + } + + /** * Number of bytes of temp storage we use for communicating between the * native compressor and the java OutputStream. */ diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d49749be75bf..458bd9473bab 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17624,7 +17624,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo, - boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) { + boolean runGc, String dumpBitmaps, + String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) { try { // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to // its own permission (same as profileControl). @@ -17658,7 +17659,8 @@ public class ActivityManagerService extends IActivityManager.Stub } }, null); - thread.dumpHeap(managed, mallocInfo, runGc, path, fd, intermediateCallback); + thread.dumpHeap(managed, mallocInfo, runGc, dumpBitmaps, + path, fd, intermediateCallback); 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 5a97e87f53f7..755631cce728 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -1239,6 +1239,7 @@ final class ActivityManagerShellCommand extends ShellCommand { final PrintWriter err = getErrPrintWriter(); boolean managed = true; boolean mallocInfo = false; + String dumpBitmaps = null; int userId = UserHandle.USER_CURRENT; boolean runGc = false; @@ -1257,6 +1258,11 @@ final class ActivityManagerShellCommand extends ShellCommand { } else if (opt.equals("-m")) { managed = false; mallocInfo = true; + } else if (opt.equals("-b")) { + dumpBitmaps = getNextArg(); + if (dumpBitmaps == null) { + dumpBitmaps = "png"; // default to PNG in dumping bitmaps + } } else { err.println("Error: Unknown option: " + opt); return -1; @@ -1288,8 +1294,8 @@ final class ActivityManagerShellCommand extends ShellCommand { } }, null); - if (!mInterface.dumpHeap(process, userId, managed, mallocInfo, runGc, heapFile, fd, - finishCallback)) { + if (!mInterface.dumpHeap(process, userId, managed, mallocInfo, runGc, dumpBitmaps, + heapFile, fd, finishCallback)) { err.println("HEAP DUMP FAILED on process " + process); return -1; } @@ -4284,11 +4290,14 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" --user <USER_ID> | current: When supplying a process name,"); pw.println(" specify user of process to profile; uses current user if not"); pw.println(" specified."); - pw.println(" dumpheap [--user <USER_ID> current] [-n] [-g] <PROCESS> <FILE>"); + pw.println(" dumpheap [--user <USER_ID> current] [-n] [-g] [-b <format>] "); + pw.println(" <PROCESS> <FILE>"); pw.println(" Dump the heap of a process. The given <PROCESS> argument may"); pw.println(" be either a process name or pid. Options are:"); pw.println(" -n: dump native heap instead of managed heap"); pw.println(" -g: force GC before dumping the heap"); + pw.println(" -b <format>: dump contents of bitmaps in the format specified,"); + pw.println(" which can be \"png\", \"jpg\" or \"webp\"."); pw.println(" --user <USER_ID> | current: When supplying a process name,"); pw.println(" specify user of process to dump; uses current user if not specified."); pw.println(" set-debug-app [-w] [--persistent] <PACKAGE>"); diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index 51aae771542e..6c16fba048bf 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1051,7 +1051,9 @@ public class AppProfiler { + mProfile.mApp + " to " + mDumpUri.getPath()); } thread.dumpHeap(/* managed= */ true, - /* mallocInfo= */ false, /* runGc= */ false, + /* mallocInfo= */ false, + /* runGc= */ false, + /* dumpbitmaps= */ null, mDumpUri.getPath(), fd, /* finishCallback= */ null); } catch (RemoteException e) { |