Skip to content

AArch64 DWARF unwind + macOS os_signpost integration#176

Open
angerman wants to merge 2 commits intostable-ghc-9.14from
feat/aarch64-dwarf-signpost
Open

AArch64 DWARF unwind + macOS os_signpost integration#176
angerman wants to merge 2 commits intostable-ghc-9.14from
feat/aarch64-dwarf-signpost

Conversation

@angerman
Copy link

Summary

Two independent, complementary features that make GHC-compiled Haskell code visible to standard profiling tools on macOS:

Part 1: AArch64 DWARF Unwind Support (GHC #19913)

The AArch64 NCG was silently discarding CmmUnwind nodes (return nilOL), producing no DWARF unwind information. This made lldb bt, Instruments, and Samply unable to unwind through Haskell frames on Apple Silicon.

  • Add UNWIND pseudo-instruction to AArch64 Instr data type (mirrors X86)
  • Implement CmmUnwindUNWIND conversion in stmtToInstrs
  • Add addSpUnwindings to emit UNWIND after DELTA (tracks SP changes)
  • Add extractUnwindPoints and wire into NcgImpl (was const [])
  • Add UNWIND pretty-printing to Ppr.hs

Part 2: macOS os_signpost Integration

The RTS had no os_signpost support, making GC pauses, thread events, and user events invisible in Apple Instruments.

  • New rts/Signpost.{h,c} with os_signpost API wrappers
  • GC interval signposts (begin/end pairs, per-capability tracking)
  • Thread lifecycle signposts (create/run/stop)
  • User event forwarding (traceEvent#/traceMarker#)
  • Zero overhead when Instruments is not attached (os_signpost_enabled() gate)
  • Empty macros on non-Darwin (zero overhead)
  • Events appear in Instruments "Points of Interest" lane by default

Files Changed

Compiler (AArch64 DWARF):

  • compiler/GHC/CmmToAsm/AArch64/Instr.hs — UNWIND constructor + pattern matches
  • compiler/GHC/CmmToAsm/AArch64/CodeGen.hs — CmmUnwind handler, addSpUnwindings, extractUnwindPoints
  • compiler/GHC/CmmToAsm/AArch64/Ppr.hs — UNWIND pretty-printing
  • compiler/GHC/CmmToAsm/AArch64.hs — Wire extractUnwindPoints into NcgImpl

RTS (os_signpost):

  • rts/Signpost.h — Header with Darwin functions / non-Darwin empty macros
  • rts/Signpost.c — os_signpost implementation
  • rts/RtsStartup.c — initSignposts/freeSignposts lifecycle
  • rts/Stats.c — GC begin/end signpost calls
  • rts/Trace.h — Thread event signpost calls
  • rts/Trace.c — User event signpost calls
  • rts/rts.cabal — Add Signpost.c to build

Test plan

  • Build GHC with changes on AArch64 (Apple Silicon)
  • Compile test program with -g and verify dwarfdump --debug-frame shows FDE entries
  • Run under lldb and verify bt shows Haskell frames
  • Build GHC on macOS and run allocation-heavy program under Instruments
  • Verify GC intervals appear in "Points of Interest" lane
  • Verify traceEvent/traceMarker events appear as signpost events
  • Verify non-Darwin builds compile without warnings (empty macros)

The AArch64 native code generator was silently discarding CmmUnwind
nodes (`return nilOL`), making DWARF-based profiling and debugging
impossible on Apple Silicon and ARM64 Linux.

This commit adds full DWARF unwind support, mirroring the existing
X86 implementation:

  - Add UNWIND pseudo-instruction to AArch64.Instr
  - Convert CmmUnwind nodes to UNWIND instructions in CodeGen
  - Emit UNWIND after DELTA via addSpUnwindings for SP tracking
  - Wire extractUnwindPoints into the AArch64 NcgImpl record
  - Pretty-print UNWIND as a label + comment in Ppr

With this change, `ghc -g` on AArch64 produces .debug_frame entries,
enabling `lldb` backtraces, `dwarfdump --debug-frame`, and sampler-
based profilers (Instruments, Samply) to unwind through Haskell code.
GHC-compiled programs are invisible to Apple Instruments because the
RTS emits no os_signpost events. This makes it hard to correlate GC
pauses and thread scheduling with system-level activity on macOS.

Add a new Signpost.c/Signpost.h module that bridges RTS events to the
os_signpost API, using OS_LOG_CATEGORY_POINTS_OF_INTEREST so events
appear in Instruments by default without a custom .instrpkg:

  - GC intervals: begin/end pairs tracked per-capability with unique
    signpost IDs, emitting generation, bytes copied, and slop
  - Thread lifecycle: create/run/stop as point events with cap and tid
  - User events: traceEvent#/traceMarker# forwarded as signposts

All functions gate on os_signpost_enabled() so the overhead when
Instruments is not attached is near zero (a single branch on the
log handle's signpost-enabled flag).

On non-Darwin platforms, all functions compile to empty macros.

Integration points:
  - Stats.c: GC begin/end with full statistics
  - Trace.h/Trace.c: thread and user event forwarding
  - RtsStartup.c: init after initScheduler, free before endTracing
@angerman angerman force-pushed the feat/aarch64-dwarf-signpost branch from b203c9e to eeb6c01 Compare March 12, 2026 05:27
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.

1 participant