Skip to content

Commit

Permalink
save
Browse files Browse the repository at this point in the history
  • Loading branch information
petar-dambovaliev committed Nov 8, 2024
1 parent 77b4ed3 commit 5b8936f
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 39 deletions.
27 changes: 27 additions & 0 deletions gnovm/pkg/gnolang/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,33 @@ func (s Stacktrace) String() string {
return builder.String()
}

func (s Stacktrace) StringDoubleSlash() string {
var builder strings.Builder

for i := 0; i < len(s.Calls); i++ {
if s.NumFramesElided > 0 && i == maxStacktraceSize/2 {
fmt.Fprintf(&builder, "...%d frame(s) elided...\\n", s.NumFramesElided)
}

call := s.Calls[i]
cx := call.Frame.Source.(*CallExpr)
switch {
case call.Frame.Func != nil && call.Frame.Func.IsNative():
fmt.Fprintf(&builder, "%s\\n", toExprTrace(cx))
fmt.Fprintf(&builder, " gonative:%s.%s\\n", call.Frame.Func.NativePkg, call.Frame.Func.NativeName)
case call.Frame.Func != nil:
fmt.Fprintf(&builder, "%s\\n", toExprTrace(cx))
fmt.Fprintf(&builder, " %s/%s:%d\\n", call.Frame.Func.PkgPath, call.Frame.Func.FileName, call.Stmt.GetLine())
case call.Frame.GoFunc != nil:
fmt.Fprintf(&builder, "%s\\n", toExprTrace(cx))
fmt.Fprintf(&builder, " gofunction:%s\\n", call.Frame.GoFunc.Value.Type())
default:
panic("StacktraceCall has a non-call Frame")
}
}
return builder.String()
}

func toExprTrace(ex Expr) string {
switch ex := ex.(type) {
case *CallExpr:
Expand Down
199 changes: 167 additions & 32 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,25 +828,65 @@ func (m *Machine) RunFunc(fn Name) {
m.RunStatement(S(Call(Nx(fn))))
}

func (m *Machine) RunMain() {
func (m *Machine) resume() {
defer func() {
if r := recover(); r != nil {
// we panicked because there was an invalid operation
// if the user had a recover, do it
hasRecover := m.setupRecoverPanic()
switch r := r.(type) {
case UnhandledPanicError:
panic(fmt.Sprintf("Machine.RunMain() panic: %s\nStacktrace: %s\n",
r.Error(), m.ExceptionsStacktrace()))
default:
panicStmt := m.makePanicFromRec(r)

m.PushStmt(panicStmt)
m.PushOp(OpExec)

if hasRecover {
m.Run()
m.resume()
return
}
}
}()

m.Run()
}

func (m *Machine) makePanicFromRec(r any) *PanicStmt {
var s strings.Builder

s.WriteString(`"`)
s.WriteString(`Machine.RunMain() panic: `)
s.WriteString(fmt.Sprintf(`%v`, r))
s.WriteString("\\n")
s.WriteString(`Machine State:`)
s.WriteString(m.StringDoubleSlash())
s.WriteString("\\n")
s.WriteString(`Stacktrace: `)
s.WriteString(m.Stacktrace().StringDoubleSlash())
s.WriteString("\\n")
s.WriteString(`"`)

panicStmt := &PanicStmt{
Exception: &BasicLitExpr{Value: s.String(), Kind: STRING},
}

return panicStmt
}

func (m *Machine) RunMain() {
defer func() {
if r := recover(); r != nil {
switch r := r.(type) {
case UnhandledPanicError:
fmt.Printf("Machine.RunMain() panic: %s\nStacktrace: %s\n",
r.Error(), m.ExceptionsStacktrace())
default:
fmt.Printf("Machine.RunMain() panic: %v\nMachine State:%s\nStacktrace: %s\n",
r, m.String(), m.Stacktrace())
panicStmt := m.makePanicFromRec(r)

m.PushStmt(panicStmt)
m.PushOp(OpExec)

m.resume()
return
}
panic(r)
}
Expand Down Expand Up @@ -2188,30 +2228,6 @@ func (m *Machine) CheckEmpty() error {
}
}

func (m *Machine) setupRuntimePanic(ex TypedValue) bool {
frm := m.LastCallFrame(1)

if frm == nil {
return false
}

m.Exceptions = append(
m.Exceptions,
Exception{
Value: ex,
Frame: frm,
Stacktrace: m.Stacktrace(),
},
)

m.PanicScope++
m.PopUntilLastCallFrame()
m.PushOp(OpPanic2)
m.PushOp(OpReturnCallDefers)

return true
}

func (m *Machine) Panic(ex TypedValue) {
m.Exceptions = append(
m.Exceptions,
Expand Down Expand Up @@ -2391,3 +2407,122 @@ func (m *Machine) ExceptionsStacktrace() string {

return builder.String()
}

func (m *Machine) StringDoubleSlash() string {
// Calculate some reasonable total length to avoid reallocation
// Assuming an average length of 32 characters per string
var (
vsLength = m.NumValues * 32
ssLength = len(m.Stmts) * 32
xsLength = len(m.Exprs) * 32
bsLength = 1024
obsLength = len(m.Blocks) * 32
fsLength = len(m.Frames) * 32
exceptionsLength = len(m.Exceptions)

totalLength = vsLength + ssLength + xsLength + bsLength + obsLength + fsLength + exceptionsLength
)

var builder strings.Builder
builder.Grow(totalLength)

builder.WriteString(fmt.Sprintf("Machine:\\n CheckTypes: %v\\n Op: %v\\n Values: (len: %d)\\n", m.CheckTypes, m.Ops[:m.NumOps], m.NumValues))

for i := m.NumValues - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\\n", i, m.Values[i]))
}

builder.WriteString(" Exprs:\\n")

for i := len(m.Exprs) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\\n", i, m.Exprs[i]))
}

builder.WriteString(" Stmts:\\n")

for i := len(m.Stmts) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\\n", i, m.Stmts[i]))
}

builder.WriteString(" Blocks:\\n")

for i := len(m.Blocks) - 1; i > 0; i-- {
b := m.Blocks[i]
if b == nil {
continue
}

gen := builder.Len()/3 + 1
gens := "@" // strings.Repeat("@", gen)

if pv, ok := b.Source.(*PackageNode); ok {
// package blocks have too much, so just
// print the pkgpath.
builder.WriteString(fmt.Sprintf(" %s(%d) %s\\n", gens, gen, pv.PkgPath))
} else {
bsi := b.StringIndentedSlash(" ")
builder.WriteString(fmt.Sprintf(" %s(%d) %s\\n", gens, gen, bsi))

if b.Source != nil {
sb := b.GetSource(m.Store).GetStaticBlock().GetBlock()
builder.WriteString(fmt.Sprintf(" (s vals) %s(%d) %s\\n", gens, gen, sb.StringIndentedSlash(" ")))

sts := b.GetSource(m.Store).GetStaticBlock().Types
builder.WriteString(fmt.Sprintf(" (s typs) %s(%d) %s\\n", gens, gen, sts))
}
}

// Update b
switch bp := b.Parent.(type) {
case nil:
b = nil
case *Block:
b = bp
case RefValue:
builder.WriteString(fmt.Sprintf(" (block ref %v)\\n", bp.ObjectID))
b = nil
default:
panic("should not happen")
}
}

builder.WriteString(" Blocks (other):\\n")

for i := len(m.Blocks) - 2; i >= 0; i-- {
b := m.Blocks[i]

if b == nil || b.Source == nil {
continue
}

if _, ok := b.Source.(*PackageNode); ok {
break // done, skip *PackageNode.
} else {
builder.WriteString(fmt.Sprintf(" #%d %s\\n", i,
b.StringIndentedSlash(" ")))
if b.Source != nil {
sb := b.GetSource(m.Store).GetStaticBlock().GetBlock()
builder.WriteString(fmt.Sprintf(" (static) #%d %s\\n", i,
sb.StringIndentedSlash(" ")))
}
}
}

builder.WriteString(" Frames:\\n")

for i := len(m.Frames) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %s\\n", i, m.Frames[i]))
}

if m.Realm != nil {
builder.WriteString(fmt.Sprintf(" Realm:\\n %s\\n", m.Realm.Path))
}

builder.WriteString(" Exceptions:\\n")

for _, ex := range m.Exceptions {
builder.WriteString(fmt.Sprintf(" %s\\n", ex.Sprint(m)))
}

return builder.String()
}
7 changes: 0 additions & 7 deletions gnovm/pkg/gnolang/op_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,13 +432,6 @@ func (m *Machine) doOpDefer() {
}
}

func (m *Machine) setupRecoverPanic() bool {
// Pop exception
ex := m.PopValue().Copy(m.Alloc)
// Panic
return m.setupRuntimePanic(ex)
}

func (m *Machine) doOpPanic1() {
// Pop exception
var ex TypedValue = m.PopValue().Copy(m.Alloc)
Expand Down
29 changes: 29 additions & 0 deletions gnovm/pkg/gnolang/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -2379,6 +2379,35 @@ func (b *Block) StringIndented(indent string) string {
return strings.Join(lines, "\n")
}

func (b *Block) StringIndentedSlash(indent string) string {
source := toString(b.Source)
if len(source) > 32 {
source = source[:32] + "..."
}
lines := make([]string, 0, 3)
lines = append(lines,
fmt.Sprintf("Block(ID:%v,Addr:%p,Source:%s,Parent:%p)",
b.ObjectInfo.ID, b, source, b.Parent)) // XXX Parent may be RefValue{}.
if b.Source != nil {
if _, ok := b.Source.(RefNode); ok {
lines = append(lines,
fmt.Sprintf("%s(RefNode names not shown)", indent))
} else {
for i, n := range b.Source.GetBlockNames() {
if len(b.Values) <= i {
lines = append(lines,
fmt.Sprintf("%s%s: undefined", indent, n))
} else {
lines = append(lines,
fmt.Sprintf("%s%s: %s",
indent, n, b.Values[i].String()))
}
}
}
}
return strings.Join(lines, "\\n")
}

func (b *Block) GetSource(store Store) BlockNode {
if rn, ok := b.Source.(RefNode); ok {
source := store.GetBlockNode(rn.GetLocation())
Expand Down

0 comments on commit 5b8936f

Please sign in to comment.