Skip to content

Commit

Permalink
cleans up the stack trace, (#138)
Browse files Browse the repository at this point in the history
* cleans up the stack trace, removing the lines from hollywood itself. the stack trace is still printed, but on a single, long line.

* test the stack trace cleanup.

* make the test fail faster rather than just hanging.
  • Loading branch information
perbu authored Jan 1, 2024
1 parent 54133c9 commit fae6c0f
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 3 deletions.
2 changes: 1 addition & 1 deletion actor/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func TestRestarts(t *testing.T) {
if msg.data != 10 {
panic("I failed to process this message")
} else {
fmt.Println("finally processed all my messsages after borking.", msg.data)
fmt.Println("finally processed all my messages after borking", msg.data)
wg.Done()
}
}
Expand Down
26 changes: 24 additions & 2 deletions actor/process.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package actor

import (
"bytes"
"fmt"
"github.com/DataDog/gostackparse"
"log/slog"
"runtime/debug"
"sync"
Expand Down Expand Up @@ -150,8 +152,7 @@ func (p *process) tryRestart(v any) {
p.Start()
return
}
stackTrace := debug.Stack()
fmt.Println(string(stackTrace))
stackTrace := cleanTrace(debug.Stack())
// If we reach the max restarts, we shutdown the inbox and clean
// everything up.
if p.restarts == p.MaxRestarts {
Expand Down Expand Up @@ -211,3 +212,24 @@ func (p *process) Send(_ *PID, msg any, sender *PID) {
p.inbox.Send(Envelope{Msg: msg, Sender: sender})
}
func (p *process) Shutdown(wg *sync.WaitGroup) { p.cleanup(wg) }

func cleanTrace(stack []byte) []byte {
goros, err := gostackparse.Parse(bytes.NewReader(stack))
if err != nil {
slog.Error("failed to parse stacktrace", "err", err)
return stack
}
if len(goros) != 1 {
slog.Error("expected only one goroutine", "goroutines", len(goros))
return stack
}
// skip the first frames:
goros[0].Stack = goros[0].Stack[4:]
buf := bytes.NewBuffer(nil)
_, _ = fmt.Fprintf(buf, "goroutine %d [%s]\n", goros[0].ID, goros[0].State)
for _, frame := range goros[0].Stack {
_, _ = fmt.Fprintf(buf, "%s\n", frame.Func)
_, _ = fmt.Fprint(buf, "\t", frame.File, ":", frame.Line, "\n")
}
return buf.Bytes()
}
49 changes: 49 additions & 0 deletions actor/process_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package actor

import (
"bytes"
"fmt"
"github.com/stretchr/testify/require"
"testing"
"time"
)

// Test_CleanTrace tests that the stack trace is cleaned up correctly and that the function
// which triggers the panic is at the top of the stack trace.
func Test_CleanTrace(t *testing.T) {
e, err := NewEngine(nil)
require.NoError(t, err)
type triggerPanic struct {
data int
}
stopCh := make(chan struct{})
pid := e.SpawnFunc(func(c *Context) {
fmt.Printf("Got message type %T\n", c.Message())
switch c.Message().(type) {
case Started:
c.Engine().Subscribe(c.pid)
case triggerPanic:
panicWrapper()
case ActorRestartedEvent:
m := c.Message().(ActorRestartedEvent)
// split the panic into lines:
lines := bytes.Split(m.Stacktrace, []byte("\n"))
// check that the second line is the panicWrapper function:
if bytes.Contains(lines[1], []byte("panicWrapper")) {
fmt.Println("stack trace contains panicWrapper at the right line")
stopCh <- struct{}{}
}
}
}, "foo", WithMaxRestarts(1))
e.Send(pid, triggerPanic{1})
select {
case <-stopCh:
fmt.Println("test passed")
case <-time.After(time.Second):
t.Error("test timed out. stack trace likely did not contain panicWrapper at the right line")
}
}

func panicWrapper() {
panic("foo")
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/anthdm/hollywood
go 1.21

require (
github.com/DataDog/gostackparse v0.7.0
github.com/grandcat/zeroconf v1.0.0
github.com/planetscale/vtprotobuf v0.4.0
github.com/prometheus/client_golang v1.15.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4=
github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
Expand Down Expand Up @@ -49,6 +51,7 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
Expand Down

0 comments on commit fae6c0f

Please sign in to comment.