Skip to content

Commit

Permalink
feat: add stack trace on machine
Browse files Browse the repository at this point in the history
  • Loading branch information
omarsy committed May 20, 2024
1 parent c4cffb5 commit 3d1fcae
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 4 deletions.
26 changes: 26 additions & 0 deletions gno.land/cmd/gnoland/testdata/panic1.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# test recursion

## start a new node
gnoland start

gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/panic -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

! gnokey maketx call -pkgpath gno.land/r/demo/panic --func Trigger --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1

stderr 'panic\(i\<VPBlock\(1\,0\)\>\)'
stderr 'gno.land/r/demo/panic:5'
stderr 'p\<VPBlock\(3\,0\)\>\(\)'
stderr 'gno.land/r/demo/panic:9'

-- panic.gno --
package main

func p() {
i := "here"
panic(i)
}

func Trigger() {
p()
}

34 changes: 34 additions & 0 deletions gno.land/cmd/gnoland/testdata/panic2.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# test panic recursion
## start a new node
gnoland start

gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/panic -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

! gnokey maketx call -pkgpath gno.land/r/demo/panic --func Trigger --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1


stderr 'panic\(\(const \(\"here\" \<untyped\> string\)\)\)'
stderr 'gno.land/r/demo/panic:5'
stderr 'p\<VPBlock\(3\,0\)\>\(i\<VPBlock\(1\,0\)\> \+ \(const \(1 int\)\)\)'
stderr 'gno.land/r/demo/panic:7'
stderr 'p\<VPBlock\(3\,0\)\>\(i\<VPBlock\(1\,0\)\> \+ \(const \(1 int\)\)\)'
stderr 'gno.land/r/demo/panic:7'
stderr 'p\<VPBlock\(3\,0\)\>\(i\<VPBlock\(1\,0\)\> \+ \(const \(1 int\)\)\)'
stderr 'gno.land/r/demo/panic:7'
stderr 'p\<VPBlock\(3\,0\)\>\(\(const \(0 int\)\)\)'
stderr 'gno.land/r/demo/panic:11'

-- panic.gno --
package main

func p(i int) {
if i == 3 {
panic("here")
}
p(i+1)
}

func Trigger() {
p(0)
}

2 changes: 1 addition & 1 deletion gno.land/pkg/sdk/vm/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) {
panic(r)
default:
err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\n%s\n",
r, m.String())
r, m.Stacktrace())
return
}
}
Expand Down
30 changes: 27 additions & 3 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ type Exception struct {
// Frame is used to reference the frame a panic occurred in so that recover() knows if the
// currently executing deferred function is able to recover from the panic.
Frame *Frame

Stack *Stack
}

func (e Exception) Sprint(m *Machine) string {
Expand All @@ -47,6 +49,7 @@ type Machine struct {
Package *PackageValue // active package
Realm *Realm // active realm
Alloc *Allocator // memory allocations
Stack *Stack // stack of expressions
Exceptions []Exception
NumResults int // number of results returned
Cycles int64 // number of "cpu" cycles
Expand Down Expand Up @@ -164,6 +167,7 @@ func NewMachineWithOptions(opts MachineOptions) *Machine {
mm.Store = store
mm.Context = context
mm.GasMeter = vmGasMeter
mm.Stack = NewStack()
mm.Debugger.enabled = opts.Debug
mm.Debugger.in = opts.Input
mm.Debugger.out = output
Expand Down Expand Up @@ -1539,12 +1543,13 @@ func (m *Machine) PopStmt() Stmt {
m.Printf("-s %v\n", s)
}
if bs, ok := s.(*bodyStmt); ok {
return bs.PopActiveStmt()
s = bs.PopActiveStmt()
} else {
// general case.
m.Stmts = m.Stmts[:numStmts-1]
return s
}

m.Stack.onStmtPopped(s)
return s
}

func (m *Machine) ForcePopStmt() (s Stmt) {
Expand Down Expand Up @@ -1740,6 +1745,7 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) {
if rlm != nil && m.Realm != rlm {
m.Realm = rlm // enter new realm
}
m.Stack.OnFramePushed(fr)
}

func (m *Machine) PushFrameGoNative(cx *CallExpr, fv *NativeValue) {
Expand Down Expand Up @@ -1774,6 +1780,7 @@ func (m *Machine) PopFrame() Frame {
m.Printf("-F %#v\n", f)
}
m.Frames = m.Frames[:numFrames-1]
m.Stack.OnFramePopped(f)
return *f
}

Expand Down Expand Up @@ -2004,6 +2011,7 @@ func (m *Machine) Panic(ex TypedValue) {
Exception{
Value: ex,
Frame: m.MustLastCallFrame(1),
Stack: m.Stack.Copy(),

Check warning on line 2014 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2014

Added line #L2014 was not covered by tests
},
)

Expand Down Expand Up @@ -2148,6 +2156,22 @@ func (m *Machine) String() string {
return builder.String()
}

func (m *Machine) Stacktrace() string {
var builder strings.Builder
builder.WriteString("Stacktrace:\n")
for i, ex := range m.Exceptions {
builder.WriteString(fmt.Sprintf(" Exception %d: \n ", i))

Check warning on line 2163 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2159-L2163

Added lines #L2159 - L2163 were not covered by tests

for i := len(ex.Stack.Execs) - 1; i >= 0; i-- {
exec := ex.Stack.Execs[i]
builder.WriteString(fmt.Sprintf(" %v\n", exec.Stmt))
builder.WriteString(fmt.Sprintf(" %v:%d\n", exec.Fun.PkgPath, exec.Stmt.GetLine()))

Check warning on line 2168 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2165-L2168

Added lines #L2165 - L2168 were not covered by tests
}
}

return builder.String()

Check warning on line 2172 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2172

Added line #L2172 was not covered by tests
}

//----------------------------------------
// utility

Expand Down
40 changes: 40 additions & 0 deletions gnovm/pkg/gnolang/stack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package gnolang

type execution struct {
Stmt Stmt
Fun *FuncValue
}
type Stack struct {
Execs []execution
}

func NewStack() *Stack {
return &Stack{
Execs: make([]execution, 0),
}
}

func (s *Stack) onStmtPopped(stmt Stmt) {
if len(s.Execs) > 0 {
s.Execs[len(s.Execs)-1].Stmt = stmt
}
}

func (s *Stack) OnFramePushed(frame *Frame) {
if frame.Func != nil {
s.Execs = append(s.Execs, execution{Fun: frame.Func})
}
}

func (s *Stack) OnFramePopped(frame *Frame) {
if frame.Func != nil {
s.Execs = s.Execs[:len(s.Execs)-1]
}
}

func (s *Stack) Copy() *Stack {
cpy := NewStack()
cpy.Execs = make([]execution, len(s.Execs))
copy(cpy.Execs, s.Execs)
return cpy

Check warning on line 39 in gnovm/pkg/gnolang/stack.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/stack.go#L35-L39

Added lines #L35 - L39 were not covered by tests
}

0 comments on commit 3d1fcae

Please sign in to comment.