A Perfetto data source that triggers a Java heap dump (.hprof) on a target Android process and embeds the raw hprof binary in the trace as a packet. Optionally also extracts bitmap images as PNGs.
This runs in system_server (AOSP frameworks/base) and uses the existing ActivityManagerService.dumpHeap() mechanism.
AMS.dumpHeap(process, managed=true, dumpBitmaps, path, fd, cb)Debug.dumpHprofData() → .hprof fileBitmap.dumpAll("png") → PNG files// protos/perfetto/config/profiling/hprof_dump_config.proto message HprofDumpConfig { optional uint64 pid = 1; optional string process_cmdline = 2; optional bool run_gc = 3; // GC before dump, default true optional bool dump_bitmaps = 4; // also extract bitmaps as PNG optional string bitmap_format = 5; // "png" (default) or "webp" }
// protos/perfetto/trace/profiling/hprof_dump.proto message HprofDump { // Process ID of the dumped process. optional int32 pid = 1; // Raw .hprof binary data. Can be split across multiple packets // if the dump is large (use continued flag on TracePacket). optional bytes hprof_data = 2; }
Add to TracePacket:
HprofDump hprof_dump = <next_field>;
For bitmaps, reuse the VideoFrame proto or add:
message HprofBitmap { optional int32 pid = 1; optional string filename = 2; // e.g., "bitmap_0.png" optional bytes png_image = 3; }
Add a module that handles TracePacket.hprof_dump:
hprof_data bytesArtHprofParser::Parse() as TraceBlobView chunksArtHprofParser populates the existing heap_graph_* tablesThis reuses all existing hprof parsing -- no new tables needed.
Store the raw hprof bytes in a BLOB vector (like VideoFrame) and expose via hprof_dump_data(id) SQL function. This lets the UI download the raw .hprof file.
Recommended: both. Parse into heap_graph tables AND store raw bytes so the user can also download the original file.
HprofDumpDataSource.javapublic class HprofDumpDataSource extends PerfettoDataSource { static { INSTANCE.register("android.hprof_dump"); } @Override protected void onStart(int instanceIndex, byte[] config) { // 1. Parse HprofDumpConfig from config bytes // 2. Find target process via AMS // 3. Create temp dir: /data/local/tmp/hprof_<sessionId>/ // 4. Create ParcelFileDescriptor for output // 5. Call AMS.dumpHeap(process, managed=true, // dumpBitmaps=config.dump_bitmaps ? "png" : null, // path, fd, finishCallback) // 6. In finishCallback (runs when dump complete): // a. Read .hprof file bytes // b. Write as TracePacket { hprof_dump { pid, hprof_data } } // Split into multiple packets if >4MB // c. If bitmap dump enabled: // Read each PNG file // Write as TracePacket { hprof_bitmap { pid, filename, png } } // d. commitPacket() // e. Delete temp files } }
A heap dump can be 50-200MB. This won't fit in a single trace packet. Use the continued flag on TracePacket to split across multiple packets on the same sequence:
byte[] hprofBytes = Files.readAllBytes(hprofPath); int chunkSize = 4 * 1024 * 1024; // 4MB chunks for (int offset = 0; offset < hprofBytes.length; offset += chunkSize) { int len = Math.min(chunkSize, hprofBytes.length - offset); ProtoWriter w = ctx.getWriter(); // Write timestamp, sequence_id, etc. int dump = w.beginNested(HPROF_DUMP_FIELD); w.writeVarInt(1, pid); w.writeBytes(2, hprofBytes, offset, len); w.endNested(dump); ctx.commitPacket(); }
Hprof dumps are large. Set shmem_size_hint_kb = 8192 (8MB). Use PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT.
The dump is async -- AMS.dumpHeap() returns immediately, the target process does the work, then calls the finish callback. The data source should defer stop until the callback fires (like LayerDataSource's HandleStopAsynchronously()).
buffers { size_kb: 262144 } # 256MB for large hprof
data_sources {
config {
name: "android.hprof_dump"
hprof_dump_config {
process_cmdline: "com.example.app"
run_gc: true
dump_bitmaps: true
}
}
}
duration_ms: 60000
On device:
# Existing mechanism (works today): adb shell am dumpheap -b png com.example.app /data/local/tmp/dump.hprof # With Perfetto (after this data source is built): adb shell perfetto -c - --txt <<EOF buffers { size_kb: 262144 } data_sources { config { name: "android.hprof_dump" hprof_dump_config { process_cmdline: "com.example.app" run_gc: true } } } duration_ms: 60000 EOF
Load the trace in Perfetto UI → heap graph tables populated from the embedded hprof data, alongside any other concurrent trace data.
HprofDumpConfigHprofDump (raw bytes), HprofBitmap (PNG bytes)hprof_dump bytes to ArtHprofParser