From ef002d6cdfda2754a489eeebb17c1f3f193086e3 Mon Sep 17 00:00:00 2001 From: Mohsen Pashna Date: Thu, 23 Jan 2025 13:53:02 +1300 Subject: [PATCH 1/6] Update panic message to be less specific in trackPanic function --- panicwrap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panicwrap.go b/panicwrap.go index 1478244..f83d43c 100644 --- a/panicwrap.go +++ b/panicwrap.go @@ -280,7 +280,7 @@ func trackPanic(r io.Reader, w io.Writer, dur time.Duration, result chan<- strin panicBuf := new(bytes.Buffer) panicHeaders := [][]byte{ []byte("panic:"), - []byte("fatal error: fault"), + []byte("fatal error:"), } panicType := -1 From 83d044c426cf02b29b37f923ea236868e601ce1e Mon Sep 17 00:00:00 2001 From: Mohsen Pashna Date: Thu, 23 Jan 2025 13:53:07 +1300 Subject: [PATCH 2/6] Update module path and Go version in go.mod --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 40ccf87..9ef1b73 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/mitchellh/panicwrap +module github.com/mohsenpashna/panicwrap -go 1.13 +go 1.23.2 From 1bc39b916d5c976c91f230d0637f991ad9496275 Mon Sep 17 00:00:00 2001 From: Mohsen Pashna Date: Thu, 23 Jan 2025 13:54:48 +1300 Subject: [PATCH 3/6] Add test for fatal case in panic wrapping functionality --- panicwrap_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/panicwrap_test.go b/panicwrap_test.go index 8910a7f..d6517c4 100644 --- a/panicwrap_test.go +++ b/panicwrap_test.go @@ -97,6 +97,26 @@ func TestHelperProcess(*testing.T) { } os.Exit(exitStatus) + case "fatal": + exitStatus, err := BasicWrap(panicHandler) + + if err != nil { + fmt.Fprintf(os.Stderr, "wrap error: %s", err) + os.Exit(1) + } + + if exitStatus < 0 { + // force a concurrent map error + badmap := make(map[int]int) + go func() { + for { + badmap[0] = 0 + } + }() + for { + badmap[0] = 0 + } + } case "panic-long": exitStatus, err := BasicWrap(panicHandler) @@ -362,3 +382,19 @@ func TestWrapped_parent(t *testing.T) { t.Fatalf("bad: %#v", stdout.String()) } } + +func TestPanicWrap_fatal(t *testing.T) { + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + + p := helperProcess("fatal") + p.Stdout = stdout + p.Stderr = stderr + if err := p.Run(); err != nil { + t.Fatalf("err: %s", err) + } + + if wrapRe.FindString(stdout.String()) == "" { + t.Fatalf("didn't wrap: %#v", stdout.String()) + } +} From 2b1485045521f310d52f9f0eaa9285b0e3d3f04f Mon Sep 17 00:00:00 2001 From: Mohsen Pashna Date: Thu, 23 Jan 2025 14:05:28 +1300 Subject: [PATCH 4/6] Refactor BasicWrap and Wrap functions to return a boolean indicating completion status --- README.md | 8 ++++---- panicwrap.go | 18 +++++++++--------- panicwrap_test.go | 28 ++++++++++++++-------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index b48feae..3152c42 100644 --- a/README.md +++ b/README.md @@ -44,20 +44,20 @@ import ( ) func main() { - exitStatus, err := panicwrap.BasicWrap(panicHandler) + done, exitStatus, err := panicwrap.BasicWrap(panicHandler) if err != nil { // Something went wrong setting up the panic wrapper. Unlikely, // but possible. panic(err) } - // If exitStatus >= 0, then we're the parent process and the panicwrap + // If `done`, then we're the parent process and the panicwrap // re-executed ourselves and completed. Just exit with the proper status. - if exitStatus >= 0 { + if done { os.Exit(exitStatus) } - // Otherwise, exitStatus < 0 means we're the child. Continue executing as + // Otherwise, `!done` means we're the child. Continue executing as // normal... // Let's say we panic diff --git a/panicwrap.go b/panicwrap.go index f83d43c..ba88e00 100644 --- a/panicwrap.go +++ b/panicwrap.go @@ -78,7 +78,7 @@ type WrapConfig struct { // BasicWrap calls Wrap with the given handler function, using defaults // for everything else. See Wrap and WrapConfig for more information on // functionality and return values. -func BasicWrap(f HandlerFunc) (int, error) { +func BasicWrap(f HandlerFunc) (bool, int, error) { return Wrap(&WrapConfig{ Handler: f, }) @@ -97,9 +97,9 @@ func BasicWrap(f HandlerFunc) (int, error) { // // Once this is called, the given WrapConfig shouldn't be modified or used // any further. -func Wrap(c *WrapConfig) (int, error) { +func Wrap(c *WrapConfig) (bool, int, error) { if c.Handler == nil { - return -1, errors.New("Handler must be set") + return false, -1, errors.New("handler must be set") } if c.DetectDuration == 0 { @@ -112,13 +112,13 @@ func Wrap(c *WrapConfig) (int, error) { // If we're already wrapped, exit out. if Wrapped(c) { - return -1, nil + return false, -1, nil } // Get the path to our current executable exePath, err := os.Executable() if err != nil { - return -1, err + return false, -1, err } // Pipe the stderr so we can read all the data as we look for panics @@ -165,7 +165,7 @@ func Wrap(c *WrapConfig) (int, error) { } if err := cmd.Start(); err != nil { - return 1, err + return true, 1, err } // Listen to signals and capture them forever. We allow the child @@ -197,7 +197,7 @@ func Wrap(c *WrapConfig) (int, error) { exitErr, ok := err.(*exec.ExitError) if !ok { // This is some other kind of subprocessing error. - return 1, err + return true, 1, err } exitStatus := 1 @@ -218,10 +218,10 @@ func Wrap(c *WrapConfig) (int, error) { c.Handler(panicTxt) } - return exitStatus, nil + return true, exitStatus, nil } - return 0, nil + return true, 0, nil } // Wrapped checks if we're already wrapped according to the configuration diff --git a/panicwrap_test.go b/panicwrap_test.go index d6517c4..cae520f 100644 --- a/panicwrap_test.go +++ b/panicwrap_test.go @@ -60,13 +60,13 @@ func TestHelperProcess(*testing.T) { cmd, args := args[0], args[1:] switch cmd { case "no-panic-ordered-output": - exitStatus, err := BasicWrap(panicHandler) + done, exitStatus, err := BasicWrap(panicHandler) if err != nil { fmt.Fprintf(os.Stderr, "wrap error: %s", err) os.Exit(1) } - if exitStatus < 0 { + if !done { for i := 0; i < 1000; i++ { os.Stdout.Write([]byte("a")) os.Stderr.Write([]byte("b")) @@ -80,14 +80,14 @@ func TestHelperProcess(*testing.T) { fmt.Fprint(os.Stderr, "stderr out") os.Exit(0) case "panic-boundary": - exitStatus, err := BasicWrap(panicHandler) + done, exitStatus, err := BasicWrap(panicHandler) if err != nil { fmt.Fprintf(os.Stderr, "wrap error: %s", err) os.Exit(1) } - if exitStatus < 0 { + if !done { // Simulate a panic but on two boundaries... fmt.Fprint(os.Stderr, "pan") os.Stderr.Sync() @@ -98,14 +98,14 @@ func TestHelperProcess(*testing.T) { os.Exit(exitStatus) case "fatal": - exitStatus, err := BasicWrap(panicHandler) + done, exitStatus, err := BasicWrap(panicHandler) if err != nil { fmt.Fprintf(os.Stderr, "wrap error: %s", err) - os.Exit(1) + os.Exit(exitStatus) } - if exitStatus < 0 { + if !done { // force a concurrent map error badmap := make(map[int]int) go func() { @@ -118,14 +118,14 @@ func TestHelperProcess(*testing.T) { } } case "panic-long": - exitStatus, err := BasicWrap(panicHandler) + done, exitStatus, err := BasicWrap(panicHandler) if err != nil { fmt.Fprintf(os.Stderr, "wrap error: %s", err) os.Exit(1) } - if exitStatus < 0 { + if !done { // Make a fake panic by faking the header and adding a // bunch of garbage. fmt.Fprint(os.Stderr, "panic: foo\n\n") @@ -153,14 +153,14 @@ func TestHelperProcess(*testing.T) { HidePanic: hidePanic, } - exitStatus, err := Wrap(config) + done, exitStatus, err := Wrap(config) if err != nil { fmt.Fprintf(os.Stderr, "wrap error: %s", err) os.Exit(1) } - if exitStatus < 0 { + if !done { panic("uh oh") } @@ -174,13 +174,13 @@ func TestHelperProcess(*testing.T) { Handler: panicHandler, } - exitStatus, err := Wrap(config) + done, exitStatus, err := Wrap(config) if err != nil { fmt.Fprintf(os.Stderr, "wrap error: %s", err) os.Exit(1) } - if exitStatus < 0 { + if !done { if child { fmt.Printf("%v", Wrapped(nil)) } @@ -201,7 +201,7 @@ func TestHelperProcess(*testing.T) { os.Exit(0) } - exitCode, err := Wrap(config) + _, exitCode, err := Wrap(config) if err != nil { fmt.Fprintf(os.Stderr, "wrap error: %s", err) os.Exit(1) From a34f3afeb23f3bcf8dbceb44f0b8395a0286ab24 Mon Sep 17 00:00:00 2001 From: Mohsen Pashna Date: Thu, 23 Jan 2025 14:15:33 +1300 Subject: [PATCH 5/6] Increase buffer size for signal channels in Wrap function --- panicwrap.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panicwrap.go b/panicwrap.go index ba88e00..63daad5 100644 --- a/panicwrap.go +++ b/panicwrap.go @@ -170,8 +170,8 @@ func Wrap(c *WrapConfig) (bool, int, error) { // Listen to signals and capture them forever. We allow the child // process to handle them in some way. - sigCh := make(chan os.Signal) - fwdSigCh := make(chan os.Signal) + sigCh := make(chan os.Signal, 1) + fwdSigCh := make(chan os.Signal, 1) if len(c.IgnoreSignals) == 0 { c.IgnoreSignals = []os.Signal{os.Interrupt} } From f6ccc19ce2a30cbbffeeec2824d53b79b18b8383 Mon Sep 17 00:00:00 2001 From: Mohsen Pashna Date: Thu, 23 Jan 2025 14:16:15 +1300 Subject: [PATCH 6/6] Update README.md to include information about archived repository and pending PRs --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 3152c42..3158295 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,14 @@ problems with your application and _should_ crash the runtime. panicwrap is just meant as a way to monitor for panics. If you still think this is the worst idea ever, read the section below on why. +Note: Since the main repository is archived, and there are a few long waited +PRs, this is a fork include those changes. These are the PRs: +- https://github.com/mitchellh/panicwrap/pull/28 by https://github.com/brandonbloom +- https://github.com/mitchellh/panicwrap/pull/27 by https://github.com/max-b +- https://github.com/mitchellh/panicwrap/pull/20 by https://github.com/itizir + + + ## Features * **SIMPLE!**