Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4bb3249
Fix macOS package discovery and add Linux perf oracle harness
ixtli Jun 11, 2026
6540a9a
Make mmap ingestion incremental via per-pid interval index
ixtli Jun 11, 2026
5b1b41e
Make mmap ingestion incremental via per-pid interval index
ixtli Jun 11, 2026
1cf9ded
Read thread ids with read_dir and drop procfs
ixtli Jun 11, 2026
fea081c
Error on unsupported profile backends and inject test platforms
ixtli Jun 11, 2026
8fe1ec7
Read perf feature bitmap at the correct offset
ixtli Jun 11, 2026
ffc5949
Parse perf feature-section build-id records
ixtli Jun 11, 2026
c3254d0
Name events from HEADER_EVENT_DESC and match perf script's event-name…
ixtli Jun 11, 2026
07e4747
Add aarch64 reg decoding, ebl fallback, and HEADER_ARCH parsing
ixtli Jun 11, 2026
7bd582e
Default perf-script/fold to one symtab frame per callchain entry
ixtli Jun 11, 2026
bfc4357
Split oracle harness into record and fast compare steps
ixtli Jun 11, 2026
31062c2
Update tests for the inline-off default and event-name spacing
ixtli Jun 11, 2026
279650c
Seed the build-id cache in the oracle compare container
ixtli Jun 11, 2026
dc77743
Fold with --count-periods in the oracle compare
ixtli Jun 11, 2026
89d8958
Match perf script output byte-for-byte on the fp oracle
ixtli Jun 11, 2026
1c242da
Move header-arch test feature bit to the corrected bitmap offset
ixtli Jun 11, 2026
58db791
Resolve the main repo from the absolute git common dir
ixtli Jun 11, 2026
11453d2
Cache DWARF inline-frame indexes per object across fold rounds
ixtli Jun 11, 2026
53d78c5
Thread perf arch through the fold user-unwind path
ixtli Jun 11, 2026
b6340da
Add aarch64 dwarf user-unwind fold tests and ebl-fallback trigger
ixtli Jun 11, 2026
347bd11
Add --inline passthrough to the bench and dwarf oracle compare
ixtli Jun 11, 2026
85f5cf5
Unwind aarch64 DWARF user stacks in the fold path
ixtli Jun 11, 2026
5182ff2
Record dwarf parity status after aarch64 unwind merge
ixtli Jun 11, 2026
16314a2
Name DWARF inline frames from linkage names like perf
ixtli Jun 11, 2026
7119522
Pin current-IP-only fold tests to a synthetic x86_64 ELF
ixtli Jun 11, 2026
d743cae
Record green macOS suite in parity findings
ixtli Jun 11, 2026
8c8416a
Expand inline frames on the sampled-IP leaf with --inline
ixtli Jun 11, 2026
cdd1b30
Format inline script frames with offset and (inlined) like perf
ixtli Jun 11, 2026
9e32655
Resolve kernel kallsyms frames in the direct fold on the recording ma…
ixtli Jun 11, 2026
e5b8d6e
Match perf inline-frame symbolization on the dwarf oracle
ixtli Jun 11, 2026
8262afc
Record dwarf inline parity closure in findings and beads
ixtli Jun 11, 2026
251df97
Compare device with inode in file identity
ixtli Jun 11, 2026
146db22
Unify symbol source across mmap record forms
ixtli Jun 11, 2026
62e3d62
Emit the libdw scenario-D leaf and gate it before unwinding
ixtli Jun 11, 2026
c49bb8b
Test scenario-D leaf emission and the skip-gate predicate
ixtli Jun 11, 2026
503e141
Test the CFI-presence memo consistency and invalidation
ixtli Jun 11, 2026
25313b7
Close pyroclast-5gr and pyroclast-pkh as the leaf-only model lands
ixtli Jun 11, 2026
b0604fc
Emit libdw scenario-D leaves and gate unwinds that cannot advance
ixtli Jun 11, 2026
b940ddc
Record leaf-only model closure in parity findings
ixtli Jun 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .ace-aarch64-unwind-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Spec: aarch64 DWARF user unwind support

Priority directive from the user (2026-06-11): the dwarf call-graph path is the
parity priority — fp call graphs require frame-pointer builds of the whole world.
The local oracle (Docker on Apple Silicon) records arm64 perf.data, so aarch64
support is the critical path for oracle-driven dwarf work. Validate against
`target/oracle/dwarf.*` (re-record with `scripts/perf-oracle` after changes; the
recorded inputs are stable otherwise).

## Current state

- `src/perfdata/unwind.rs`: `PerfX86_64Regs { ip, sp, bp, registers: [u64;16] }`,
`FramehopUnwinder { unwinder: UnwinderX86_64<ModuleBytes>, cache: CacheX86_64 }`,
`unwind_x86_64_frame_pointer_stack_like_elfutils` (x86 ebl fallback),
`PerfUserMemoryReader` (arch-neutral), module loading via
`ExplicitModuleSectionInfo` (arch-neutral).
- `src/perfdata/fold.rs` threads `&PerfX86_64Regs` through ~21 sites; entry point is
`append_perf_user_unwind_frames` →
`PerfX86_64Regs::from_perf_masked_values(event.layout.sample_regs_user, &regs.values)`.
`perf_user_reg_value(mask, values, 8)` hardcodes PERF_REG_X86_IP=8 in
`parse_sample_for_summary` too.
- x86-only logic to gate by arch: `is_syscall_return_state` (rcx==ip && r11!=0),
`libdw_arch_fallback_after_empty_object_unwind` guard `regs.bp >= regs.sp`.

## Register numbering

- x86_64 (PERF_REG_X86_*): BP=6, SP=7, IP=8 (already implemented).
- aarch64 (PERF_REG_ARM64_*): x0..x28 = 0..28, X29/FP = 29, LR/x30 = 30, SP = 31,
PC = 32. perf records `--call-graph dwarf` on arm64 with mask covering x0-x30,
sp, pc (mask 0x1ffffffff).

## Arch detection

perf.data does not store arch per attr; perf uses the header HEADER_ARCH feature
(string from uname, "aarch64" / "x86_64"). Header feature parsing machinery exists
(see `header_build_ids_by_filename_from_file` / `src/perfdata/build_id.rs`,
`header.rs`). Add `header_arch_from_file` reading HEADER_ARCH (feature bit 5;
perf string format: u32 len + bytes, see perf util/header.c write_arch/read).
Comment on lines +37 to +38

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Correct the HEADER_ARCH feature-bit value in the spec.

Line 37 says HEADER_ARCH is feature bit 5, but the implementation uses bit 6; this mismatch will mislead future maintenance/debugging.

📝 Suggested doc fix
-`header.rs`). Add `header_arch_from_file` reading HEADER_ARCH (feature bit 5;
+`header.rs`). Add `header_arch_from_file` reading HEADER_ARCH (feature bit 6;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`header.rs`). Add `header_arch_from_file` reading HEADER_ARCH (feature bit 5;
perf string format: u32 len + bytes, see perf util/header.c write_arch/read).
`header.rs`). Add `header_arch_from_file` reading HEADER_ARCH (feature bit 6;
perf string format: u32 len + bytes, see perf util/header.c write_arch/read).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.ace-aarch64-unwind-spec.md around lines 37 - 38, The spec incorrectly
documents HEADER_ARCH as feature bit 5; update the documentation to state
HEADER_ARCH is feature bit 6 to match the implementation used by
header_arch_from_file and header.rs (leave the perf string format note intact:
u32 len + bytes, see perf util/header.c write_arch/read). Ensure any mention of
"feature bit 5" is replaced with "feature bit 6" so the doc and the code remain
consistent.

Default to x86_64 when absent. Plumb into `FoldAccumulator`/sink construction so
`append_perf_user_unwind_frames` can decode regs per arch.

## Design (project is unreleased — rename freely)

1. Replace `PerfX86_64Regs` with arch-neutral `PerfUserRegs { arch: PerfArch,
ip: u64, sp: u64, fp: u64, lr: Option<u64>, values: ... }` with
`from_perf_masked_values(arch, mask, values)`; keep per-arch accessors used by
the x86 syscall-return check (rcx = values[?]; preserve current behavior via
the existing masked-value lookup). Alternatively an enum — pick whichever keeps
the 21 fold.rs sites simplest; most only use ip/sp/bp.
2. `FramehopUnwinder` becomes an enum or holds per-arch unwinder+cache
(`framehop::aarch64::{UnwinderAarch64, CacheAarch64, UnwindRegsAarch64}`).
Module registration (`framehop::Module::new` + ExplicitModuleSectionInfo) is
shared; instantiate by arch at accumulator/PidUnwindState creation (arch comes
from the file header, one arch per perf.data).
Note framehop aarch64 `UnwindRegsAarch64::new(lr, sp, fp)`; `iter_frames` seeds
with pc. framehop strips PAC bits itself.
3. aarch64 ebl_unwind fallback, faithful to elfutils backends/aarch64_unwind.c
(fetched 2026-06-11, evverx/elfutils mirror):
- FP_REG=29 LR_REG=30 SP_REG=31; FP_OFFSET=0 LR_OFFSET=8 SP_OFFSET=16.
- prev pc = lr; fail only if lr unreadable or lr == 0.
- newLr = mem[fp+8] else 0; newFp = mem[fp+0] else 0; newSp = fp+16.
- success iff `fp == 0 || newSp > sp` (NO bp>=sp precondition like x86;
fp==0 still yields one lr-based caller).
- Iterate like the x86 `unwind_x86_64_frame_pointer_stack_like_elfutils` loop
(callback-per-frame semantics; subsequent pc gets the `!initial && !signal`
pc-1 adjustment in the consumer like the existing code).
4. Gate x86-only logic by arch: syscall-return truncation (no arm64 analogue),
`regs.bp >= regs.sp` fallback precondition is x86-only (aarch64 fallback has
its own conditions above).
5. Leaf-only / scenario-D predicate (see .ace-research-perf-unwind.md §3): on
aarch64 the no-CFI fallback succeeds whenever lr != 0, so "current-IP-only"
stacks are far rarer; the cheap skip-gate condition 6 becomes
`!has_unwind_info_for_ip(ip) && lr == 0` on aarch64.

## Tests

- Unit tests mirroring tests/perfdata_unwind.rs x86 cases for the aarch64
fallback (synthetic stacks: fp chain at fp+0/fp+8, the fp==0-with-lr case, the
newSp <= sp failure case).
- Reg decoding tests for the arm64 mask layout (pc=32, sp=31, fp=29, lr=30).
- End-to-end: `scripts/perf-oracle` → dwarf bench comparison should go from
pyroclast_folded_lines=2 vs inferno=16 to matching (after the script-parity
naming fixes are merged; residuals analyzed against
.ace-research-perf-unwind.md).
Loading