Contributor/agent notes. User-facing docs live in README.md; this is the
"how it's built and where the sharp edges are" companion.
A VGI worker (Rust, compiled binary) that decodes pprof
profiles (the gzip-wrapped profile.proto emitted by Go, gperftools, Parca,
Pyroscope, py-spy, …) into SQL rows over Arrow IPC. Built on the vgi crate
(crates.io), modeled on vgi-fixedformat / vgi-units. Catalog name pprof
(single main schema). It exists so SRE/perf teams can bulk-diff profiles in
SQL — go tool pprof is interactive and single-file.
Distinct format/library from vgi-perf (Linux perf.data) — do not merge
them; they ship in one observability/CI-regression bundle but decode different
things.
Cargo.toml workspace; pins vgi = "0.18.0" (→ vgi-rpc 0.11, arrow 59), prost 0.14
crates/pprof-core/ PURE decode/flatten engine (no Arrow/VGI)
proto/profile.proto VENDORED Google pprof schema (Apache-2.0)
build.rs prost-build codegen; protoc via protoc-bin-vendored (hermetic)
src/lib.rs gunzip + prost decode + flatten into row structs + unit tests
tests/golden.rs golden fixtures (one per producer) + proptest no-panic fuzzing
crates/pprof-worker/
src/main.rs Worker::new(); registers tables; catalog metadata (incl. implementation_version) + the VALUES-backed sample_type_guide view
src/source.rs resolve overloaded `src` (path/glob/list/BLOB) + per-file error capture
src/arrow_build.rs row structs -> Arrow columns (LIST/STRUCT/MAP type defs shared with on_bind)
src/table/{stacks,samples,functions,locations,mappings,meta,mod}.rs thin table-fn adapters
src/meta.rs vgi-lint metadata tag helpers (shared)
data/generate_fixtures.go builds the golden .pb.gz fixtures with google/pprof
data/*.pb.gz committed fixtures (+ empty.pb.gz / bad.pb.gz for error tests)
test/sql/*.test haybarn-unittest sqllogictest — authoritative E2E
ci/ run-integration.sh (transport matrix) + check-version.sh + preprocess-require.awk
run_tests.sh local E2E convenience wrapper
Pattern: keep decode/flatten in pprof-core (pure, golden + proptest tested),
keep Arrow marshalling in pprof-worker (thin). A nested column's Arrow type is
defined once in arrow_build.rs and reused by both output_schema() (what
on_bind returns) and the column builder, so the declared schema and the built
RecordBatch can't drift.
profile.proto is a string-table-indexed graph: samples reference location ids
(leaf first), locations belong to a mapping and carry inlined Lines (innermost
first), each line names a function, and all the human strings live in
string_table (index 0 is always ""). pprof-core resolves those indices once
and pre-joins the graph:
stacks(the headline) — one row per sample withvalue(LIST(BIGINT) aligned tometa.sample_types),labels(MAP), andframe(LIST(STRUCT), leaf first, inlined frames expanded). An unsymbolized location yields one address-only frame (function/filename NULL, address kept).samples/functions/locations/mappings— the raw graph with the profile's original ids passed through verbatim so they join (and somappings.build_idflows tovgi-symbols).meta— one row of sample types / period / duration.
- Per-file error capture, not abort. Every table appends
file+errorcolumns. A missing/zero-byte/malformed profile becomes a single error row (data columns NULL); a glob mixing good and bad files still returns the good rows. Always filterWHERE error IS NULLin aggregations. srcoverloads on type. VARCHAR → path (may glob); LIST(VARCHAR) → many paths; BLOB → inline bytes (fileis NULL). Seesource.rs.read_textis a DuckDB table function and can't be nested as a scalar arg — for inline bytes use a BLOB literal (from_base64('…')) orread_blob(...)in a FROM clause.- gzip is sniffed, not assumed. On-disk pprof is gzip-wrapped, but some
producers/tests emit raw protobuf;
Decoded::from_byteschecks the1f 8bmagic and inflates only when present. - MAP labels need unique keys. DuckDB's MAP rejects duplicate keys, so
corede-duplicates label keys (first wins). pprof itself discourages multi-value label keys. Numeric labels render as the number (+ unit suffix). - value indexing is 1-based and type-ordered. For a Go CPU profile
value[1]issamples/countandvalue[2]iscpu/nanoseconds— readpprof.meta(src).sample_typesto know which slot is which. haybarn-unittestskipsrequire vgi—.testfiles use explicitstatement ok+LOAD vgi;. AfterSET search_path = 'pprof.main'thepprofcatalog is the default database, so do notDETACH pprofat end-of-file (it errors). Fixtures are referenced by absolute path via${VGI_PPROF_DATA}because the runner cd's into a staging dir.- prost-build needs
protoc— supplied hermetically byprotoc-bin-vendoredinbuild.rs, so neither CI nor a dev box needs a systemprotoc. An explicitPROTOCenv still wins if set.
cargo build --release · cargo clippy --all-targets -- -D warnings ·
cargo fmt --check · cargo test · cargo doc (RUSTDOCFLAGS=-D warnings) ·
vgi-lint … --fail-on info (100/100) · haybarn SQLLogic E2E on
subprocess/http/unix.
MIT (fleet convention). The vendored profile.proto is Apache-2.0 (Google).
Copyright 2026 Query Farm LLC — https://query.farm