blob: bdfa92b2e2b86f63113af7d542312317cf537b2d [file] [view]
# Android Hprof Dump Data Source
## Overview
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.
## What it does
1. Receives trace config with target process pid/cmdline
2. Calls `AMS.dumpHeap(process, managed=true, dumpBitmaps, path, fd, cb)`
3. Target process runs `Debug.dumpHprofData()` `.hprof` file
4. Optionally target process runs `Bitmap.dumpAll("png")` PNG files
5. On completion callback: reads the files, writes trace packets
6. Cleans up temp files
## Proto
### Config (new, in Perfetto repo)
```proto
// 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"
}
```
### Trace output (new, in Perfetto repo)
```proto
// 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:
```proto
HprofDump hprof_dump = <next_field>;
```
For bitmaps, reuse the `VideoFrame` proto or add:
```proto
message HprofBitmap {
optional int32 pid = 1;
optional string filename = 2; // e.g., "bitmap_0.png"
optional bytes png_image = 3;
}
```
## Trace processor changes
### Option A: Route raw bytes to ArtHprofParser
Add a module that handles `TracePacket.hprof_dump`:
1. Extracts the `hprof_data` bytes
2. Feeds them to `ArtHprofParser::Parse()` as `TraceBlobView` chunks
3. `ArtHprofParser` populates the existing `heap_graph_*` tables
This reuses all existing hprof parsing -- no new tables needed.
### Option B: Store raw bytes as BLOB
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.
## System server data source (AOSP)
### `HprofDumpDataSource.java`
```java
public 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
}
}
```
### Splitting large hprof 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:
```java
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();
}
```
### Shmem buffer sizing
Hprof dumps are large. Set `shmem_size_hint_kb = 8192` (8MB).
Use `PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT`.
### Threading
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()`).
## Trace config example
```
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
```
## Testing
On device:
```sh
# 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.
## Perfetto repo changes needed
1. Config proto: `HprofDumpConfig`
2. Trace proto: `HprofDump` (raw bytes), `HprofBitmap` (PNG bytes)
3. Trace processor module: routes `hprof_dump` bytes to `ArtHprofParser`
4. Optional: BLOB storage + SQL function for raw hprof download
5. Optional: UI plugin for bitmap gallery view