feat(aarch64): record why the gap scan refused an address, as intel does - #330
Conversation
The intel gap scan emits a LOGGER.debug on every path that declines a gap pointer, so a suppression can be read back from a run. The AArch64 loop emitted nothing at all, which left the two deliberately identical paths diverging on the one axis that matters when something is refused and nobody can see why. Twelve refusals now name the address they declined, using the intel wording wherever the path is the same one: already inside the code or data map, padding, a declared .pdata extent, a declared landing pad, a declared FDE range, an interior indirect-branch landing pad, a trap word, a pointer the scan already handed out, the code filter, a run that flows into a mapped function, and the accepted candidate. The sweep over bytes outside every executable section is deliberately not logged. It refuses no candidate - it steps over what is not code at all - it has no intel counterpart, and on the bundled aarch64_static fixture alone it would emit 156,758 identical lines, 91% of everything the loop would say. What remains is 15,709 calls on the heaviest corpus cell measured, costing 0.02% of that run at 0.068us per suppressed call. The bundled fixture reaches seven of the twelve, and those are asserted individually so a path that stops logging fails as itself. The repeated gap pointer needs a caller that asks again from the same address rather than from where the last candidate ended, so it is driven directly.
|
Reviewed here and good to go. Verified here rather than taken on trust: every message you reuse from intel is verbatim against One thing better than intel, worth saying since it is a deliberate difference rather than drift: intel logs One non-blocking gap, and #333 hands you the fixture that closes it. |
Item 3 of #322.
intel/FunctionCandidateManageremits aLOGGER.debugon every path in its gap scan that declines a gap pointer. The AArch64 loop emitted nothing at all — its only logging anywhere was one line in the.pdatacarver — so the two paths, which #312 made deliberately identical in every other respect, diverged on the one axis that matters when an address is refused and nobody can see why.What is logged
Twelve refusals now name the address they declined, reusing the intel wording wherever the path is the same one:
.pdataextentThe one path deliberately left silent
The sweep over bytes outside every executable section refuses no candidate — it steps over what is not code at all — and it has no intel counterpart, because the intel scan does not walk non-executable bytes word by word.
Logging it costs nothing measurable but makes the output useless: on the bundled
aarch64_staticfixture alone it emits 156,758 identical lines, 91% of everything the loop would say. What remains is 15,709 calls on the heaviest corpus cell measured (googletest_gcc-arm64_O2-static), at 0.068µs per suppressed call — 0.02% of that run.That figure is measured by counting calls rather than by wall clock. Paired interleaved timing on this machine put the delta at +14% to +20% with an 18–37% spread within each side, which is item 4 of this issue in miniature: the noise is larger than the effect, so the call count and the per-call cost are what the claim rests on.
Tests
tests/testAArch64GapScanLogging.py, three cases:Verified to fail when the logging is removed: dropping the FDE-range line alone fails the first case.
logging.disable()is called at import by 68 files in this suite, which suppresses records before any handler sees them and would leaveassertLogswith nothing, so each case saves and restores the global disable level.Gates
python -m pytest tests/→ 2,063 passed, 1 skipped, 2,593 subtestsruff check .andruff format --check .→ cleanmake typecheck→ exit 0, 261 diagnostics, identical to mastere9dfe5f→ 100% (13 lines, 0 missing)No behaviour changes and no fixture baseline moves; the only non-logging edit is the test file.
You marked these as yours in the issue, so this is offered rather than assumed — close it if you would rather take it.