Skip to content

Fix kperf hardware-counter sampling on Apple Silicon#5

Open
StefanoD wants to merge 1 commit into
D-Berg:mainfrom
StefanoD:fix-kperf-apple-silicon-sampling
Open

Fix kperf hardware-counter sampling on Apple Silicon#5
StefanoD wants to merge 1 commit into
D-Berg:mainfrom
StefanoD:fix-kperf-apple-silicon-sampling

Conversation

@StefanoD

@StefanoD StefanoD commented Jul 12, 2026

Copy link
Copy Markdown

Fixes #4.

Summary

sudo crap panicked on Apple Silicon (M2) before printing any hardware counters. Two independent bugs in the kperf path, plus tests. (#4 reports the first panic on macOS 26; the second surfaces once that's fixed.)

1. EventNotFound — event lookup used the wrong name

startSampling looked events up by the KpepEventAlias enum tag ("Cycles", "Instructions") instead of the real PMC event names. The kpep database indexes events as FIXED_CYCLES, FIXED_INSTRUCTIONS, … — the exact names already listed in KpepEventAlias.getNames(), which was defined but never called. The fix consults getNames() and tries each candidate name.

thread panic: unable to start kperf sampling: EventNotFound
src/kperf.zig:366  return xnu.Error.EventNotFound

2. integer overflow — non-monotonic per-thread counters

Summing per-thread deltas did counters_1 - counters_0 as a plain u64 subtraction. Per-thread PMC snapshots are normally monotonic, but on Apple Silicon's asymmetric P/E cores a thread's accumulated counter can read lower on a later sample (counter virtualization across core types), underflowing the subtraction — a panic in Debug, a garbage wrap in ReleaseFast. Extracted addSaturatingDiffs(), which saturates so such a thread contributes 0.

thread panic: integer overflow
src/kperf.zig:286  counter_diffs[i] += t_data.counters_1[i] - t_data.counters_0[i]

Tests

Adds a zig build test step and unit tests for both fixes:

  • addSaturatingDiffs saturates a backwards counter instead of underflowing.
  • KpepEventAlias.getNames() yields the real PMC event names (FIXED_CYCLES, …), guarding the data the lookup now depends on.

The kperf code is macOS-only, so the tests are referenced from main.zig behind a comptime macOS guard — discovered on the macOS target and pruned on Linux/Windows (the executable still cross-compiles unchanged). CI currently runs zig build release only; on the macos-latest runner a zig build test step would exercise these.

Verified

M2 (Darwin 25.5). Before: panic (both, sequentially). After: sudo crap reports counters — e.g. an AES-256-GCM 1200 B seal+open micro-benchmark, RustCrypto software vs ring:

Benchmark 1  rustcrypto:  cpu_cycles 268M    instructions 147M
Benchmark 3  ring:        cpu_cycles 9.50M   instructions 5.94M   ⚡ -96.5%
  • zig build test → 3 pass (verified failing when an assertion is broken).
  • zig build and zig build -Dtarget=x86_64-linux unchanged.

🤖 Generated with Claude Code

Fixes D-Berg#4.

`sudo crap` panicked on Apple Silicon (M2) before reporting any hardware
counters. Two independent bugs in the kperf path:

1. Event lookup used the wrong name. The lookup passed the KpepEventAlias
   enum tag ("Cycles"/"Instructions") to kpep_db_event(), but the kpep
   database indexes events by their PMC names ("FIXED_CYCLES",
   "FIXED_INSTRUCTIONS", ...). The correct names were already listed in the
   never-called KpepEventAlias.getNames(); consult it and try each candidate.

       thread panic: unable to start kperf sampling: EventNotFound
       kperf.zig:366 ... return xnu.Error.EventNotFound

2. Counter-diff underflow. Summing per-thread deltas computed
   counters_1 - counters_0 as a plain u64 subtraction. Per-thread PMC
   snapshots are normally monotonic, but on asymmetric P/E cores a thread's
   accumulated counter can read lower on a later sample (counter
   virtualization), underflowing the subtraction. Extract addSaturatingDiffs()
   and saturate so such a thread contributes 0.

       thread panic: integer overflow
       kperf.zig:286 ... counter_diffs[i] += t_data.counters_1[i] - t_data.counters_0[i]

Add a `zig build test` step and unit tests for both fixes (the saturating diff
helper, and that getNames() yields real PMC event names). The kperf code is
macOS-only, so the tests are discovered on the macOS target and pruned
elsewhere; the executable still cross-compiles to Linux/Windows unchanged.

Tested on an M2 (Darwin 25.5): `sudo crap` now reports cpu_cycles /
instructions instead of panicking.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@StefanoD StefanoD force-pushed the fix-kperf-apple-silicon-sampling branch from 0c5263b to 14fa9ed Compare July 12, 2026 13:50
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.

Crap fails to run under sudo on macOS 26

1 participant