1
1
mirror of https://github.com/wader/fq.git synced 2024-12-24 13:52:02 +03:00
fq/internal/recoverfn/recoverfn.go
Mattias Wadman e9d9f8aef9 fq: Use go 1.18
Rename s/interface{}/any/g
Preparation for using generics in decode API and native jq funcations etc
Remove some unused linter ignores as linter has been fixed
2022-05-20 15:23:16 +02:00

102 lines
1.9 KiB
Go

package recoverfn
import (
"fmt"
"io"
"os"
"runtime"
)
const stackSizeLimit = 256
type Raw struct {
RecoverV any
RecoverPC uintptr
PCs []uintptr
}
// Run runs fn and return Raw{}, true on no-panic
// on panic it recovers and return a raw stacktrace and panic value to inspect
func Run(fn func()) (Raw, bool) {
// TODO: once?
var recoverPC [1]uintptr
runtime.Callers(1, recoverPC[:])
pc, v := func() (pcs []uintptr, v any) {
defer func() {
if recoverErr := recover(); recoverErr != nil {
pcs = make([]uintptr, stackSizeLimit)
pcs = pcs[0:runtime.Callers(0, pcs)]
v = recoverErr
}
}()
fn()
return nil, nil
}()
if v == nil {
return Raw{}, true
}
return Raw{
RecoverV: v,
RecoverPC: recoverPC[0],
PCs: pc,
}, false
}
func (r Raw) frames(startSkip int, bottomSkip int, bottomPC uintptr) []runtime.Frame {
var bottomFrame runtime.Frame
bottomIndex := -1
if bottomPC != 0 {
bottomPCs := [1]uintptr{bottomPC}
bottomFrame, _ = runtime.CallersFrames(bottomPCs[:]).Next()
}
fs := make([]runtime.Frame, len(r.PCs))
frames := runtime.CallersFrames(r.PCs)
for i := 0; ; i++ {
f, more := frames.Next()
if !more {
break
}
if bottomPC != 0 && f.Function == bottomFrame.Function {
bottomIndex = i
}
fs[i] = f
}
endIndex := len(fs) - 1
if bottomIndex != -1 {
endIndex = bottomIndex - bottomSkip
}
return fs[startSkip:endIndex]
}
func (r Raw) Frames() []runtime.Frame {
// 3 to skip runtime.Callers, Recover help function and runtime.gopanic
// 1 to skip Recover defer recover() function
return r.frames(3, 1, r.RecoverPC)
}
func (r Raw) RePanic() {
var o io.Writer
o = os.Stderr
if p := os.Getenv("REPANIC_LOG"); p != "" {
if f, err := os.Create(p); err == nil {
o = f
defer f.Close()
}
}
fmt.Fprintf(o, "repanic: %v\n", r.RecoverV)
for _, f := range r.frames(0, 0, 0) {
fmt.Fprintf(o, "%s\n", f.Function)
fmt.Fprintf(o, "\t%s:%d\n", f.File, f.Line)
}
panic(r.RecoverV)
}