From 5b8936f0db61b591551aca7041564b1838dd9fc1 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Fri, 8 Nov 2024 13:34:17 +0100 Subject: [PATCH] save --- gnovm/pkg/gnolang/frame.go | 27 +++++ gnovm/pkg/gnolang/machine.go | 199 +++++++++++++++++++++++++++++------ gnovm/pkg/gnolang/op_call.go | 7 -- gnovm/pkg/gnolang/values.go | 29 +++++ 4 files changed, 223 insertions(+), 39 deletions(-) diff --git a/gnovm/pkg/gnolang/frame.go b/gnovm/pkg/gnolang/frame.go index 2ac1027eb32..f2d646aa2d7 100644 --- a/gnovm/pkg/gnolang/frame.go +++ b/gnovm/pkg/gnolang/frame.go @@ -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: diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 3b927f2036c..75026841f37 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -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) } @@ -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, @@ -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() +} diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index ebc5b530cb0..ba5b7507cff 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -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) diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 68c2967811f..579dd453cdb 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -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())