Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!**
Expand Down Expand Up @@ -44,20 +52,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
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/mitchellh/panicwrap
module github.com/mohsenpashna/panicwrap

go 1.13
go 1.23.2
24 changes: 12 additions & 12 deletions panicwrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -165,13 +165,13 @@ 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
// 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}
}
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
58 changes: 47 additions & 11 deletions panicwrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand All @@ -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()
Expand All @@ -97,15 +97,35 @@ func TestHelperProcess(*testing.T) {
}

os.Exit(exitStatus)
case "fatal":
done, exitStatus, err := BasicWrap(panicHandler)

if err != nil {
fmt.Fprintf(os.Stderr, "wrap error: %s", err)
os.Exit(exitStatus)
}

if !done {
// 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)
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")
Expand Down Expand Up @@ -133,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")
}

Expand All @@ -154,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))
}
Expand All @@ -181,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)
Expand Down Expand Up @@ -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())
}
}