DWARF Perf Corpus
July 28, 2026 ยท View on GitHub
This directory holds reproducible programs used for DWARF parser performance baselines.
The corpus is intentionally separate from e2e-tests/tests/fixtures:
- correctness fixtures stay small and easy to reason about
- perf fixtures can grow in size and DWARF density without making routine e2e work slower
Corpus Layout
src/query-hotspot/query_hotspot.c- hand-written single-binary sample for single-address query baselines
- contains a unique
DWARF_PERF_QUERY_HOTSPOTsource marker that the build step records in the manifest
src/parse-stress/- documentation for the generated multi-translation-unit corpus
- sources are emitted by
scripts/dwarf-perf/generate_parse_stress.pyduring the build
src/rust-parse-stress/- documentation for the generated Rust large-symbol corpus
- sources are emitted by
scripts/dwarf-perf/generate_rust_parse_stress.pyduring the build
src/cpp-template-stress/- documentation for the generated C++ template-heavy corpus
- sources are emitted by
scripts/dwarf-perf/generate_cpp_template_stress.pyduring the build
src/rust-generic-stress/- documentation for the generated Rust generic-heavy corpus
- sources are emitted by
scripts/dwarf-perf/generate_rust_generic_stress.pyduring the build
src/cpp-deep-namespace/- documentation for the generated C++ deep-namespace corpus
- sources are emitted by
scripts/dwarf-perf/generate_cpp_deep_namespace.pyduring the build
Build Flow
By default the build uses the image pinned in
scripts/dwarf-perf/builder_image_ref.txt, produced by the DWARF perf image workflow:
./scripts/dwarf-perf/build_corpus.sh
Current locked image:
ghcr.io/swananan/ghostscope-dwarf-perf-builder@sha256:ed99b20a96a4fb2abc4845ca55e7e3780e97752d3281da441b8aa335b56b9ecb
To override the pinned image temporarily:
DWARF_PERF_BUILDER_IMAGE=ghcr.io/swananan/ghostscope-dwarf-perf-builder:ubuntu24.04-llvm18-rust1.88 \
./scripts/dwarf-perf/build_corpus.sh
The build writes artifacts under scripts/dwarf-perf/corpus/out/:
query-hotspot/query_hotspotparse-stress/parse_stressrust-parse-stress/rust_parse_stresscpp-template-stress/cpp_template_stressrust-generic-stress/rust_generic_stresscpp-deep-namespace/cpp_deep_namespacemanifest.json
These generated outputs are ignored by Git through the repository
/.gitignore.
End-to-end baseline runner:
./scripts/dwarf-perf/run_baseline.sh
This will:
- build the corpus unless
--skip-buildis passed - run the parse benchmark through
dwarf-tool benchmarkfor every built-in fast-parse corpus by default - run the query benchmark through
dwarf-tool benchmark-source-line - write a JSON result under
perf-results/
To run only one parse corpus:
./scripts/dwarf-perf/run_baseline.sh --parse-target rust-parse-stress
Other built-in parse targets include:
parse-stressrust-parse-stresscpp-template-stressrust-generic-stresscpp-deep-namespace
perf-results/ is also ignored by Git and is intended for local or CI
benchmark result snapshots.
For long-term main-branch history, see
.github/workflows/dwarf-perf-history.yml. That workflow publishes the
baseline history site through GitHub Pages custom workflow deployment and
keeps the generated site state in the dwarf-perf-history-data branch.
The script prints three separate sections:
Fast parse benchmarkSource-line query benchmarkQuery result snapshot
Fast parse benchmark is the analyzer load path, which reflects the initial
DWARF fast-parse and index-build cost. It reports average_ms, p50_ms,
p95_ms, min_ms, and max_ms.
The source-line benchmark keeps one analyzer instance warm, performs 10
unmeasured warmup queries by default, and then records first_run_ms,
average_ms, p50_ms, p95_ms, min_ms, and max_ms from the measured
queries. Use --query-warmup-runs to override the baseline warmup count. Its
latency covers the full source-line query path: source-line lookup, matched
address resolution, and variable collection for those addresses.
Tuning
The generated parse corpus is deterministic and can be scaled without editing sources:
PARSE_STRESS_PRESETPARSE_STRESS_UNITSPARSE_STRESS_TYPES_PER_UNITPARSE_STRESS_FUNCTIONS_PER_UNITPARSE_STRESS_HISTORY_LENRUST_PARSE_STRESS_PRESETRUST_PARSE_STRESS_MODULESRUST_PARSE_STRESS_TYPES_PER_MODULERUST_PARSE_STRESS_FUNCTIONS_PER_MODULECPP_TEMPLATE_STRESS_PRESETCPP_TEMPLATE_STRESS_UNITSCPP_TEMPLATE_STRESS_FAMILIES_PER_UNITCPP_TEMPLATE_STRESS_INSTANTIATIONS_PER_FAMILYCPP_TEMPLATE_STRESS_METHODS_PER_FAMILYRUST_GENERIC_STRESS_PRESETRUST_GENERIC_STRESS_MODULESRUST_GENERIC_STRESS_FAMILIES_PER_MODULERUST_GENERIC_STRESS_MONOMORPHS_PER_FAMILYCPP_DEEP_NAMESPACE_PRESETCPP_DEEP_NAMESPACE_UNITSCPP_DEEP_NAMESPACE_NAMESPACE_DEPTHCPP_DEEP_NAMESPACE_BRANCHES_PER_UNITCPP_DEEP_NAMESPACE_FUNCTIONS_PER_BRANCH
Preset defaults:
medium:16units,12types/unit,32helper functions/unit,8history entrieslarge:48units,24types/unit,72helper functions/unit,16history entriesxlarge:96units,40types/unit,128helper functions/unit,24history entries
Rust large-symbol preset defaults:
medium:24modules,16types/module,48worker functions/modulelarge:64modules,24types/module,96worker functions/modulexlarge:96modules,32types/module,160worker functions/module
C++ template preset defaults:
medium:8units,10families/unit,24instantiations/family,4methods/familylarge:16units,16families/unit,48instantiations/family,4methods/familyxlarge:20units,18families/unit,56instantiations/family,4methods/family
Rust generic preset defaults:
medium:8modules,10families/module,18monomorphs/familylarge:16modules,16families/module,28monomorphs/familyxlarge:20modules,18families/module,34monomorphs/family
C++ deep-namespace preset defaults:
medium:8units, namespace depth6,12branches/unit,16functions/branchlarge:16units, namespace depth8,16branches/unit,24functions/branchxlarge:20units, namespace depth10,18branches/unit,28functions/branch
Current default:
PARSE_STRESS_PRESET=largeRUST_PARSE_STRESS_PRESET=largeCPP_TEMPLATE_STRESS_PRESET=largeRUST_GENERIC_STRESS_PRESET=largeCPP_DEEP_NAMESPACE_PRESET=large
The generator now emits:
- a shared type header
- a shared inline-helper header
- one header and one source per unit
- cross-unit type references and callback edges
- a
generation_config.jsonfile consumed by the build manifest
The compiler and DWARF flavor are also configurable:
DWARF_PERF_CCDWARF_PERF_CXXDWARF_PERF_DWARF_VERSIONDWARF_PERF_CFLAGSDWARF_PERF_CXXFLAGSDWARF_PERF_LDFLAGSDWARF_PERF_RUSTCDWARF_PERF_RUSTFLAGS
Compiler default:
DWARF_PERF_CC=gccDWARF_PERF_CXX=g++DWARF_PERF_RUSTC=rustc
gcc is the default because the current dwarf-tool query flows resolve the query-hotspot
sample more reliably with GCC-generated DWARF than with Clang-generated DWARF.
The Rust parse corpus is intentionally tuned for DWARF density rather than runtime
performance. It keeps debug info in the binary, references a broad slice of std,
and compiles with -C link-dead-code=yes so it is useful for fast-index pressure
tests.
The new comparison groups focus on mangled-symbol coverage from three angles:
cpp-template-stress: larger C++ template instantiation sets, roughly50K-100Kmangled symbols on the default presetrust-generic-stress: Rust generic monomorphization, roughly20K-50Kmangled symbols on the default presetcpp-deep-namespace: long hierarchical namespace names, roughly10K-30Kmangled symbols on the default preset