[Based on #2090] runtime,cl: preserve panic-site frames across defer and recover#2026
Open
cpunion wants to merge 18 commits into
Open
[Based on #2090] runtime,cl: preserve panic-site frames across defer and recover#2026cpunion wants to merge 18 commits into
cpunion wants to merge 18 commits into
Conversation
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 4, 2026 07:53
554625e to
9c33770
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 4, 2026 12:29
9c33770 to
157d48d
Compare
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
2 times, most recently
from
July 4, 2026 14:05
9a4beda to
f102328
Compare
cpunion
added a commit
to cpunion/llgo
that referenced
this pull request
Jul 4, 2026
…libunwind + FP chain) Hardware faults — SIGSEGV/SIGBUS and previously-fatal SIGFPE — now convert to ordinary recoverable Go panics, and the unrecovered traceback shows the fault-site chain: C frames down through the Go callers. - A SA_SIGINFO handler captures the interrupted context; the handler's own frame-pointer chain dead-ends at the signal trampoline, which is why the ucontext pc/fp is required. - The capture prefers a dynamically-resolved libunwind (dlopen/dlsym at install time only — no link-time -lunwind; LLGO_DYNUNWIND=0 disables): DWARF/compact-unwind stepping survives C frames compiled without frame pointers, and the nongnu flavor's unw_get_proc_name reads .symtab, naming static C symbols dladdr cannot see (they otherwise display under a neighboring Go function via nearest-below). Where unwind info runs out, the walk resumes along the FP chain from libunwind's final cursor. Flavors: darwin libSystem, linux nongnu (arch-prefixed symbols), linux LLVM (context translated). - Only man-page async-signal-safe unw_* calls run in the handler (resolution and a lazy-state warm-up happen at install). Fault-context walks probe page readability (msync) before dereferencing — an arithmetic-valid frame pointer can still point into an unmapped hole, and faulting inside the fault path would recurse; a re-entered handler restores the default disposition for one clean core. - The unrecovered dump goes through a new PanicTraceback hook (gc-style frames via the funcinfo tables; libunwind's name for dot-less C symbols); non-fault panics keep the existing clite dump. Verified: darwin/arm64 (libSystem flavor); linux/amd64 with a -fomit-frame-pointer C chain — the FP-only walk recovers 2 frames with a misattributed name, the dynamic path recovers the full chain with correct static names. Overlaps with the fault half of xgo-dev#2026 (panic-site snapshots); whichever lands second rebases to drop its duplicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 4, 2026 16:12
f102328 to
2de72d5
Compare
This was referenced Jul 6, 2026
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 8, 2026 05:27
2de72d5 to
f9e5617
Compare
cpunion
added a commit
to cpunion/llgo
that referenced
this pull request
Jul 8, 2026
…libunwind + FP chain) Hardware faults — SIGSEGV/SIGBUS and previously-fatal SIGFPE — now convert to ordinary recoverable Go panics, and the unrecovered traceback shows the fault-site chain: C frames down through the Go callers. - A SA_SIGINFO handler captures the interrupted context; the handler's own frame-pointer chain dead-ends at the signal trampoline, which is why the ucontext pc/fp is required. - The capture prefers a dynamically-resolved libunwind (dlopen/dlsym at install time only — no link-time -lunwind; LLGO_DYNUNWIND=0 disables): DWARF/compact-unwind stepping survives C frames compiled without frame pointers, and the nongnu flavor's unw_get_proc_name reads .symtab, naming static C symbols dladdr cannot see (they otherwise display under a neighboring Go function via nearest-below). Where unwind info runs out, the walk resumes along the FP chain from libunwind's final cursor. Flavors: darwin libSystem, linux nongnu (arch-prefixed symbols), linux LLVM (context translated). - Only man-page async-signal-safe unw_* calls run in the handler (resolution and a lazy-state warm-up happen at install). Fault-context walks probe page readability (msync) before dereferencing — an arithmetic-valid frame pointer can still point into an unmapped hole, and faulting inside the fault path would recurse; a re-entered handler restores the default disposition for one clean core. - The unrecovered dump goes through a new PanicTraceback hook (gc-style frames via the funcinfo tables; libunwind's name for dot-less C symbols); non-fault panics keep the existing clite dump. Verified: darwin/arm64 (libSystem flavor); linux/amd64 with a -fomit-frame-pointer C chain — the FP-only walk recovers 2 frames with a misattributed name, the dynamic path recovers the full chain with correct static names. Overlaps with the fault half of xgo-dev#2026 (panic-site snapshots); whichever lands second rebases to drop its duplicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 8, 2026 07:02
f9e5617 to
1c0db95
Compare
cpunion
added a commit
to cpunion/llgo
that referenced
this pull request
Jul 8, 2026
…libunwind + FP chain) Hardware faults — SIGSEGV/SIGBUS and previously-fatal SIGFPE — now convert to ordinary recoverable Go panics, and the unrecovered traceback shows the fault-site chain: C frames down through the Go callers. - A SA_SIGINFO handler captures the interrupted context; the handler's own frame-pointer chain dead-ends at the signal trampoline, which is why the ucontext pc/fp is required. - The capture prefers a dynamically-resolved libunwind (dlopen/dlsym at install time only — no link-time -lunwind; LLGO_DYNUNWIND=0 disables): DWARF/compact-unwind stepping survives C frames compiled without frame pointers, and the nongnu flavor's unw_get_proc_name reads .symtab, naming static C symbols dladdr cannot see (they otherwise display under a neighboring Go function via nearest-below). Where unwind info runs out, the walk resumes along the FP chain from libunwind's final cursor. Flavors: darwin libSystem, linux nongnu (arch-prefixed symbols), linux LLVM (context translated). - Only man-page async-signal-safe unw_* calls run in the handler (resolution and a lazy-state warm-up happen at install). Fault-context walks probe page readability (msync) before dereferencing — an arithmetic-valid frame pointer can still point into an unmapped hole, and faulting inside the fault path would recurse; a re-entered handler restores the default disposition for one clean core. - The unrecovered dump goes through a new PanicTraceback hook (gc-style frames via the funcinfo tables; libunwind's name for dot-less C symbols); non-fault panics keep the existing clite dump. Verified: darwin/arm64 (libSystem flavor); linux/amd64 with a -fomit-frame-pointer C chain — the FP-only walk recovers 2 frames with a misattributed name, the dynamic path recovers the full chain with correct static names. Overlaps with the fault half of xgo-dev#2026 (panic-site snapshots); whichever lands second rebases to drop its duplicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 8, 2026 11:16
1c0db95 to
63bbc3d
Compare
cpunion
added a commit
to cpunion/llgo
that referenced
this pull request
Jul 8, 2026
…libunwind + FP chain) Hardware faults — SIGSEGV/SIGBUS and previously-fatal SIGFPE — now convert to ordinary recoverable Go panics, and the unrecovered traceback shows the fault-site chain: C frames down through the Go callers. - A SA_SIGINFO handler captures the interrupted context; the handler's own frame-pointer chain dead-ends at the signal trampoline, which is why the ucontext pc/fp is required. - The capture prefers a dynamically-resolved libunwind (dlopen/dlsym at install time only — no link-time -lunwind; LLGO_DYNUNWIND=0 disables): DWARF/compact-unwind stepping survives C frames compiled without frame pointers, and the nongnu flavor's unw_get_proc_name reads .symtab, naming static C symbols dladdr cannot see (they otherwise display under a neighboring Go function via nearest-below). Where unwind info runs out, the walk resumes along the FP chain from libunwind's final cursor. Flavors: darwin libSystem, linux nongnu (arch-prefixed symbols), linux LLVM (context translated). - Only man-page async-signal-safe unw_* calls run in the handler (resolution and a lazy-state warm-up happen at install). Fault-context walks probe page readability (msync) before dereferencing — an arithmetic-valid frame pointer can still point into an unmapped hole, and faulting inside the fault path would recurse; a re-entered handler restores the default disposition for one clean core. - The unrecovered dump goes through a new PanicTraceback hook (gc-style frames via the funcinfo tables; libunwind's name for dot-less C symbols); non-fault panics keep the existing clite dump. Verified: darwin/arm64 (libSystem flavor); linux/amd64 with a -fomit-frame-pointer C chain — the FP-only walk recovers 2 frames with a misattributed name, the dynamic path recovers the full chain with correct static names. Overlaps with the fault half of xgo-dev#2026 (panic-site snapshots); whichever lands second rebases to drop its duplicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cpunion
added a commit
to cpunion/llgo
that referenced
this pull request
Jul 8, 2026
…libunwind + FP chain) Hardware faults — SIGSEGV/SIGBUS and previously-fatal SIGFPE — now convert to ordinary recoverable Go panics, and the unrecovered traceback shows the fault-site chain: C frames down through the Go callers. - A SA_SIGINFO handler captures the interrupted context; the handler's own frame-pointer chain dead-ends at the signal trampoline, which is why the ucontext pc/fp is required. - The capture prefers a dynamically-resolved libunwind (dlopen/dlsym at install time only — no link-time -lunwind; LLGO_DYNUNWIND=0 disables): DWARF/compact-unwind stepping survives C frames compiled without frame pointers, and the nongnu flavor's unw_get_proc_name reads .symtab, naming static C symbols dladdr cannot see (they otherwise display under a neighboring Go function via nearest-below). Where unwind info runs out, the walk resumes along the FP chain from libunwind's final cursor. Flavors: darwin libSystem, linux nongnu (arch-prefixed symbols), linux LLVM (context translated). - Only man-page async-signal-safe unw_* calls run in the handler (resolution and a lazy-state warm-up happen at install). Fault-context walks probe page readability (msync) before dereferencing — an arithmetic-valid frame pointer can still point into an unmapped hole, and faulting inside the fault path would recurse; a re-entered handler restores the default disposition for one clean core. - The unrecovered dump goes through a new PanicTraceback hook (gc-style frames via the funcinfo tables; libunwind's name for dot-less C symbols); non-fault panics keep the existing clite dump. Verified: darwin/arm64 (libSystem flavor); linux/amd64 with a -fomit-frame-pointer C chain — the FP-only walk recovers 2 frames with a misattributed name, the dynamic path recovers the full chain with correct static names. Overlaps with the fault half of xgo-dev#2026 (panic-site snapshots); whichever lands second rebases to drop its duplicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
2 times, most recently
from
July 9, 2026 05:30
78cd80e to
96a7100
Compare
cpunion
added a commit
to cpunion/llgo
that referenced
this pull request
Jul 9, 2026
…libunwind + FP chain) Hardware faults — SIGSEGV/SIGBUS and previously-fatal SIGFPE — now convert to ordinary recoverable Go panics, and the unrecovered traceback shows the fault-site chain: C frames down through the Go callers. - A SA_SIGINFO handler captures the interrupted context; the handler's own frame-pointer chain dead-ends at the signal trampoline, which is why the ucontext pc/fp is required. - The capture prefers a dynamically-resolved libunwind (dlopen/dlsym at install time only — no link-time -lunwind; LLGO_DYNUNWIND=0 disables): DWARF/compact-unwind stepping survives C frames compiled without frame pointers, and the nongnu flavor's unw_get_proc_name reads .symtab, naming static C symbols dladdr cannot see (they otherwise display under a neighboring Go function via nearest-below). Where unwind info runs out, the walk resumes along the FP chain from libunwind's final cursor. Flavors: darwin libSystem, linux nongnu (arch-prefixed symbols), linux LLVM (context translated). - Only man-page async-signal-safe unw_* calls run in the handler (resolution and a lazy-state warm-up happen at install). Fault-context walks probe page readability (msync) before dereferencing — an arithmetic-valid frame pointer can still point into an unmapped hole, and faulting inside the fault path would recurse; a re-entered handler restores the default disposition for one clean core. - The unrecovered dump goes through a new PanicTraceback hook (gc-style frames via the funcinfo tables; libunwind's name for dot-less C symbols); non-fault panics keep the existing clite dump. Verified: darwin/arm64 (libSystem flavor); linux/amd64 with a -fomit-frame-pointer C chain — the FP-only walk recovers 2 frames with a misattributed name, the dynamic path recovers the full chain with correct static names. Overlaps with the fault half of xgo-dev#2026 (panic-site snapshots); whichever lands second rebases to drop its duplicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 11, 2026 03:04
96a7100 to
05aafd4
Compare
8 tasks
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 12, 2026 12:13
05aafd4 to
4486503
Compare
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 15, 2026 06:06
65b284f to
65919ea
Compare
12 tasks
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 16, 2026 04:37
65919ea to
370694c
Compare
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
3 times, most recently
from
July 19, 2026 00:13
fcc22f8 to
566b0d7
Compare
Store each immutable Func as one atomic pointer and validate the queried PC from the Func itself. This prevents concurrent cache replacement from combining a PC and function from different writers. Apply the same publication rule to the prebuilt function cache and stress multiple PCs during concurrent first use.
Give each package-backed locality block an owner-local TLS cache so hot accesses are O(1) regardless of the number of touched packages. Keep the block list only for GC reachability and teardown. Require locality variables to be unexported and reject go:linkname aliases. Add codegen, lifecycle, and 2/4/8-package performance coverage.
…ks see the panic frames gc runs deferred functions on top of the panicked stack; LLGo's longjmp unwinding removes those frames physically, so runtime.Caller / CallersFrames / debug.Stack from a deferred function (before or after recover) could not see the panic site. Now: - Panic() captures the physical pc chain (the existing SavePanicCallerFrames hook, empty since the shadow stack left) into a per-thread snapshot; Recover() marks the recovering frame so the snapshot stays observable exactly while that frame is live. - Caller-info walks splice the snapshot below the live deferred frames at the defer-owner junction, keeping one panic-machinery frame where gc has runtime.gopanic (fixed Caller depths count it). - Hardware faults (SIGSEGV/SIGBUS and previously-fatal SIGFPE) install a SA_SIGINFO handler that captures from the interrupted ucontext pc/fp — the handler's own chain dead-ends at the signal trampoline — so fault tracebacks start at the fault site, through C frames into the Go callers. C is compiled with -fno-omit-frame-pointer so x86-64 chains hold. - Defer execution is attributed to the function's closing brace like gc, and explicit panic statements get their own statement anchor. Signal-path robustness (the reflectmake flake, ~7% -> 0 over 300 runs): - The recover mark reads the frame-pointer chain, which after siglongjmp can reach a stale/unmapped slot; the guarded read (msync page probe) lives in the public runtime via a RecoverMark hook, and the core just calls it — an unguarded read self-faulted and corrupted the value the recover was extracting. - The fault handler does no async-signal-unsafe work: the snapshot buffer is preallocated (no bdwgc malloc in signal context) and the page size is primed at install (no sysconf). SA_NODEFER + an unblock on capture keep a savemask=0 longjmp escape from leaving the fault signal blocked, and a re-entered handler restores the default disposition for one clean core; fault-context walks probe page readability before dereferencing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove issue14646/issue5856/issue33724 xfails; the C-fault regression runs three sequential faults (a handler leaving the signal blocked after the longjmp escape cores on the second) and asserts the fault-site chain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cpunion
force-pushed
the
codex/stage5-panic-snapshot
branch
from
July 20, 2026 06:11
566b0d7 to
78652f0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #2090
Dependency
Stack: #2079 -> #2090 -> this PR.
#2079 provides
//llgo:tlsand//llgo:gls; #2090 stores goroutine runtime state in one GLS-backedg. This PR uses the same facilities and no longer adds a pthread key.Problem
LLGo implements panic/recover with longjmp. The jump removes the panicking frames before deferred functions run, while gc keeps those logical frames visible to
runtime.Caller,runtime.Callers/CallersFrames, anddebug.Stack, including afterrecoveruntil the recovering defer returns.#2028 already owns recoverable hardware faults, the
SA_SIGINFOhandler, and fault-context unwinding. The missing layer is preserving ordinary panic snapshots and exposing both ordinary and fault snapshots through caller APIs.Implementation
panicPCStorein//llgo:tls. Signal handling performs no allocation, pthread-key lookup, or first-use initialization.gstate to determine whether a panic is still active.panic.The Linux validation found that the old frame liveness check inspected a C helper frame after it had returned; the next call could reuse that slot and terminate the walk. The final implementation synchronously consumes the existing helper's active FP links in Go and returns the stable Go caller frame. It adds no C symbol or cache ABI.
Conformance
Removed xfails for:
fixedbugs/issue14646.gofixedbugs/issue5856.gofixedbugs/issue33724.goThe remaining statement-line cases are still limited by line granularity in untracked functions and are not skipped by this PR.
Validation
Tests were serialized with
GOMAXPROCS=2,GOMEMLIMIT=8GiB, and-p=1where supported.cl/compile.gobranch;deferRunPosis 100% covered. The nested runtime module is exercised by LLGo integration tests because the root Codecov job does not instrument it.runtime/internal/runtimeandruntime/internal/lib/runtimecompile under the host Go toolchain.Limits