Skip to content

Phase 6.1: Rust HeapProfile::write_pprof + hand-rolled pprof proto encoder - #18

Merged
jayakasadev merged 1 commit into
mainfrom
feature/phase-6-1-rust-pprof
Jun 11, 2026
Merged

jayakasadev merged 1 commit into
mainfrom
feature/phase-6-1-rust-pprof

Conversation

@jayakasadev

Copy link
Copy Markdown
Owner

Summary

Adds pprof output format to snmalloc-rs. Consumable by go tool pprof, Pyroscope, Polar Signals, Speedscope (via pprof importer).

  • HeapProfile::write_pprof<W>(w, weight) — emits a Profile proto with sample_type=[("alloc_objects","count"),("alloc_space","bytes")], one sample per BtSample, unique location + function per PC
  • Hand-rolled protobuf encoder (pub(crate) mod pprof) — zero new deps. Rationale: the Profile message is small (~10 fields, two wire types); pulling prost + prost-build + syn + quote for one message wasn't worth it. ~250 LOC encoder + ~250 LOC tests.
  • Output is uncompressed protobuf (first byte = 0x0a, NOT gzip magic 0x1f). Callers wrap in flate2::GzEncoder for .pb.gz if desired.
  • Unsymbolicated path: function name = "0x{hex}", file empty, line 0. Symbolicated path comes from Phase 4.4's symbolicate feature.

Test plan

  • cargo build (no features)
  • cargo build --features profiling
  • cargo build --features 'profiling symbolicate'
  • cargo test --features profiling: 3 new tests in tests/profile_pprof.rs — write_pprof_smoke / write_pprof_empty_snapshot / pprof_total_weight_matches_total_allocated_bytes
  • 6 new pprof::tests::* unit tests (round-trip decode via minimal in-test decoder, no prost dev-dep)
  • All 12 C++ profile ctests unaffected

- New pub(crate) module snmalloc-rs/src/pprof.rs hand-rolls the
  protobuf3 wire format (varint + length-delimited) for the subset
  of Google's pprof Profile schema needed for snmalloc heap
  snapshots; no prost/flate2 dependencies added.
- HeapProfile::write_pprof emits two sample_type axes
  (alloc_objects/count, alloc_space/bytes) plus per-stack
  location/function chains; output is uncompressed (callers can
  wrap in GzEncoder if they want .pb.gz).
- Unsymbolicated frames render function name as 0x..hex.. with
  empty filename/line, mirroring write_flamegraph; symbolicated
  frames use names from HeapProfile::symbolize when available.
- Tests: 6 unit tests in src/pprof.rs (varint, empty profile,
  alloc_space-axis invariance under both Weight projections,
  function/location dedup, string-table slot-0 contract) +
  3 integration tests in tests/profile_pprof.rs gated on
  --features profiling (smoke, empty snapshot, total_weight ==
  total_allocated_bytes).
@jayakasadev
jayakasadev merged commit 3a9fa3f into main Jun 11, 2026
154 of 202 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant