1
1
mirror of https://github.com/wader/fq.git synced 2024-10-26 11:49:54 +03:00

decode,fuzz,dev: Move recoverable error check to recoverfn.Run

This preserves the callstack on non-recoverable panics so that using
a debugger and fuzzing is much easier.

Add vscode debug config.
Remove fuzz stacktrace log workaround.
This commit is contained in:
Mattias Wadman 2023-03-31 12:29:11 +02:00
parent 664bc3b1b7
commit c5f6809b02
6 changed files with 69 additions and 54 deletions

21
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug fq",
"type": "go",
"request": "launch",
"mode": "auto",
"showLog": true,
"program": ".",
"cwd": "${workspaceFolder}",
"env": {},
"args": [
"-d",
"mp4",
".",
"file"
],
}
]
}

View File

@ -61,6 +61,7 @@
"gojqex",
"golangci",
"gopacket",
"gopanic",
"GOPATH",
"gosec",
"gosimple",
@ -154,6 +155,8 @@
"tfhd",
"tfra",
"tmpl",
"to_xml",
"to_xmlentities",
"toactual",
"toarray",
"toboolean",
@ -167,8 +170,6 @@
"torepr",
"tosym",
"tovalue",
"to_xml",
"to_xmlentities",
"traf",
"trak",
"trex",

View File

@ -92,7 +92,7 @@ update-gomod: always
# fq -n '"..." | from_base64 | ...'
fuzz: always
# in other terminal: tail -f /tmp/repanic
FUZZTEST=1 REPANIC_LOG=/tmp/repanic go test -v -run Fuzz -fuzz=Fuzz ./format/
FUZZTEST=1 go test -v -run Fuzz -fuzz=Fuzz ./format/
# usage: make release VERSION=0.0.1
# tag forked dependeces for history and to make then stay around

View File

@ -1,9 +1,6 @@
package recoverfn
import (
"fmt"
"io"
"os"
"runtime"
)
@ -15,8 +12,13 @@ type Raw struct {
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
type RecoverableErrorer interface {
IsRecoverableError() bool
}
// Run runs fn and return Raw{}, true for no panic
// If panic is recoverable raw stacktrace and panic value is returned
// If panic is not recoverable we just panic again
func Run(fn func()) (Raw, bool) {
// TODO: once?
var recoverPC [1]uintptr
@ -24,10 +26,14 @@ func Run(fn func()) (Raw, bool) {
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
if recoverV := recover(); recoverV != nil {
if re, ok := recoverV.(RecoverableErrorer); ok && re.IsRecoverableError() {
pcs = make([]uintptr, stackSizeLimit)
pcs = pcs[0:runtime.Callers(0, pcs)]
v = recoverV
return
}
panic(recoverV)
}
}()
@ -81,21 +87,3 @@ func (r Raw) Frames() []runtime.Frame {
// 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)
}

View File

@ -8,13 +8,17 @@ import (
)
func test1() {
panic("hello")
panic(testError(true))
}
func test2() {
test1()
}
type testError bool
func (t testError) IsRecoverableError() bool { return bool(t) }
func TestNormal(t *testing.T) {
_, rOk := recoverfn.Run(func() {})
expectedROK := true
@ -31,9 +35,8 @@ func TestPanic(t *testing.T) {
t.Errorf("expected v %v, got %v", expectedROK, rOk)
}
expectedV := "hello"
if !reflect.DeepEqual(expectedV, r.RecoverV) {
t.Errorf("expected v %v, got %v", expectedV, r.RecoverV)
if _, ok := r.RecoverV.(testError); !ok {
t.Errorf("expected v %v, got %v", testError(true), r.RecoverV)
}
frames := r.Frames()

View File

@ -106,27 +106,29 @@ func decode(ctx context.Context, br bitio.ReaderAtSeeker, group Group, opts Opti
}
if !rOk {
if re, ok := r.RecoverV.(RecoverableErrorer); ok && re.IsRecoverableError() {
panicErr, _ := re.(error)
formatErr := FormatError{
Err: panicErr,
Format: f,
Stacktrace: r,
}
formatsErr.Errs = append(formatsErr.Errs, formatErr)
switch vv := d.Value.V.(type) {
case *Compound:
// TODO: hack, changes V
d.Value.V = vv
d.Value.Err = formatErr
}
if len(group) != 1 {
continue
}
var panicErr error
if err, ok := r.RecoverV.(error); ok {
panicErr = err
} else {
r.RePanic()
panicErr = fmt.Errorf("recoverable non-panic error :%v", r.RecoverV)
}
formatErr := FormatError{
Err: panicErr,
Format: f,
Stacktrace: r,
}
formatsErr.Errs = append(formatsErr.Errs, formatErr)
switch vv := d.Value.V.(type) {
case *Compound:
// TODO: hack, changes V
d.Value.V = vv
d.Value.Err = formatErr
}
if len(group) != 1 {
continue
}
}