38 lines
1.9 KiB
Go
38 lines
1.9 KiB
Go
package obs
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime/debug"
|
|
)
|
|
|
|
// panic.go is SafeGo's opposite number, and the contrast is the point: SafeGo swallows so a background
|
|
// telemetry write dies alone, while on the PAID path a swallowed crash is how a run that died mid-book
|
|
// gets reported as a success. THE defect (row 176): unrecovered, a panic leaves through the Go runtime's
|
|
// handler, which exits 2 — the number tmctl's contract gives to "completed with flags" — and the
|
|
// supervisor records the crashed run as `ready`. Here a panic becomes an ordinary error instead.
|
|
|
|
// PanicError is a recovered runtime panic, carrying the stack of the goroutine that panicked.
|
|
//
|
|
// It deliberately has NO Unwrap: the recovered value may itself be an error (a panicked context.Canceled,
|
|
// a *pipeline.CeilingHalt thrown by accident), and exposing it to errors.Is/As would let a crash match a
|
|
// sentinel of the exit-code dictionary and read as a deliberate stop.
|
|
type PanicError struct {
|
|
// Where names the goroutine that died, since the message is often read without the stack.
|
|
Where string
|
|
Value any
|
|
Stack []byte
|
|
}
|
|
|
|
// Error renders the panic WITH its stack. The stack is part of the message rather than something a
|
|
// caller has to fish out with errors.As, because the one guaranteed reader is `tmctl: <err>` on stderr,
|
|
// and the wrapping between here and there is not under this type's control: a crash whose diagnosis
|
|
// depends on nobody using %v on the way up is a diagnosis that will be missing when it is needed.
|
|
func (e *PanicError) Error() string {
|
|
return fmt.Sprintf("PANIC in %s: %v\n%s", e.Where, e.Value, e.Stack)
|
|
}
|
|
|
|
// NewPanicError converts a recover() value into an error. Call it only when recover() returned non-nil;
|
|
// `where` names the goroutine ("wave worker", "tmctl").
|
|
func NewPanicError(where string, v any) *PanicError {
|
|
return &PanicError{Where: where, Value: v, Stack: debug.Stack()}
|
|
}
|