Skip to content

Commit

Permalink
decode,fuzz,dev: Move recoverable error check to recoverfn.Run
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wader committed Mar 31, 2023
1 parent 664bc3b commit c5f6809
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 53 deletions.
21 changes: 21 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -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"
],
}
]
}
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"gojqex",
"golangci",
"gopacket",
"gopanic",
"GOPATH",
"gosec",
"gosimple",
Expand Down Expand Up @@ -154,6 +155,8 @@
"tfhd",
"tfra",
"tmpl",
"to_xml",
"to_xmlentities",
"toactual",
"toarray",
"toboolean",
Expand All @@ -167,8 +170,6 @@
"torepr",
"tosym",
"tovalue",
"to_xml",
"to_xmlentities",
"traf",
"trak",
"trex",
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 15 additions & 27 deletions internal/recoverfn/recoverfn.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package recoverfn

import (
"fmt"
"io"
"os"
"runtime"
)

Expand All @@ -15,19 +12,28 @@ 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
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
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)
}
}()

Expand Down Expand Up @@ -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)
}
11 changes: 7 additions & 4 deletions internal/recoverfn/recoverfn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down
40 changes: 21 additions & 19 deletions pkg/decode/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
var panicErr error
if err, ok := r.RecoverV.(error); ok {
panicErr = err
} else {
panicErr = fmt.Errorf("recoverable non-panic error :%v", r.RecoverV)
}

switch vv := d.Value.V.(type) {
case *Compound:
// TODO: hack, changes V
d.Value.V = vv
d.Value.Err = formatErr
}
formatErr := FormatError{
Err: panicErr,
Format: f,
Stacktrace: r,
}
formatsErr.Errs = append(formatsErr.Errs, formatErr)

if len(group) != 1 {
continue
}
} else {
r.RePanic()
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
}
}

Expand Down

0 comments on commit c5f6809

Please sign in to comment.