summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-05-27 15:46:15 +0000
committerLibravatar GitHub <noreply@github.com>2024-05-27 17:46:15 +0200
commit1e7b32490dfdccddd04f46d4b0416b48d749d51b (patch)
tree62a11365933a5a11e0800af64cbdf9172e5e6e7a /vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go
parent[chore] Small styling + link issues (#2933) (diff)
downloadgotosocial-1e7b32490dfdccddd04f46d4b0416b48d749d51b.tar.xz
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go170
1 files changed, 170 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go b/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go
new file mode 100644
index 000000000..ff0e0cccc
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go
@@ -0,0 +1,170 @@
+// Package wasmdebug contains utilities used to give consistent search keys between stack traces and error messages.
+// Note: This is named wasmdebug to avoid conflicts with the normal go module.
+// Note: This only imports "api" as importing "wasm" would create a cyclic dependency.
+package wasmdebug
+
+import (
+ "fmt"
+ "runtime"
+ "runtime/debug"
+ "strconv"
+ "strings"
+
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/internal/wasmruntime"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// FuncName returns the naming convention of "moduleName.funcName".
+//
+// - moduleName is the possibly empty name the module was instantiated with.
+// - funcName is the name in the Custom Name section.
+// - funcIdx is the position in the function index, prefixed with
+// imported functions.
+//
+// Note: "moduleName.$funcIdx" is used when the funcName is empty, as commonly
+// the case in TinyGo.
+func FuncName(moduleName, funcName string, funcIdx uint32) string {
+ var ret strings.Builder
+
+ // Start module.function
+ ret.WriteString(moduleName)
+ ret.WriteByte('.')
+ if funcName == "" {
+ ret.WriteByte('$')
+ ret.WriteString(strconv.Itoa(int(funcIdx)))
+ } else {
+ ret.WriteString(funcName)
+ }
+
+ return ret.String()
+}
+
+// signature returns a formatted signature similar to how it is defined in Go.
+//
+// * paramTypes should be from wasm.FunctionType
+// * resultTypes should be from wasm.FunctionType
+// TODO: add paramNames
+func signature(funcName string, paramTypes []api.ValueType, resultTypes []api.ValueType) string {
+ var ret strings.Builder
+ ret.WriteString(funcName)
+
+ // Start params
+ ret.WriteByte('(')
+ paramCount := len(paramTypes)
+ switch paramCount {
+ case 0:
+ case 1:
+ ret.WriteString(api.ValueTypeName(paramTypes[0]))
+ default:
+ ret.WriteString(api.ValueTypeName(paramTypes[0]))
+ for _, vt := range paramTypes[1:] {
+ ret.WriteByte(',')
+ ret.WriteString(api.ValueTypeName(vt))
+ }
+ }
+ ret.WriteByte(')')
+
+ // Start results
+ resultCount := len(resultTypes)
+ switch resultCount {
+ case 0:
+ case 1:
+ ret.WriteByte(' ')
+ ret.WriteString(api.ValueTypeName(resultTypes[0]))
+ default: // As this is used for errors, don't panic if there are multiple returns, even if that's invalid!
+ ret.WriteByte(' ')
+ ret.WriteByte('(')
+ ret.WriteString(api.ValueTypeName(resultTypes[0]))
+ for _, vt := range resultTypes[1:] {
+ ret.WriteByte(',')
+ ret.WriteString(api.ValueTypeName(vt))
+ }
+ ret.WriteByte(')')
+ }
+
+ return ret.String()
+}
+
+// ErrorBuilder helps build consistent errors, particularly adding a WASM stack trace.
+//
+// AddFrame should be called beginning at the frame that panicked until no more frames exist. Once done, call Format.
+type ErrorBuilder interface {
+ // AddFrame adds the next frame.
+ //
+ // * funcName should be from FuncName
+ // * paramTypes should be from wasm.FunctionType
+ // * resultTypes should be from wasm.FunctionType
+ // * sources is the source code information for this frame and can be empty.
+ //
+ // Note: paramTypes and resultTypes are present because signature misunderstanding, mismatch or overflow are common.
+ AddFrame(funcName string, paramTypes, resultTypes []api.ValueType, sources []string)
+
+ // FromRecovered returns an error with the wasm stack trace appended to it.
+ FromRecovered(recovered interface{}) error
+}
+
+func NewErrorBuilder() ErrorBuilder {
+ return &stackTrace{}
+}
+
+type stackTrace struct {
+ // frameCount is the number of stack frame currently pushed into lines.
+ frameCount int
+ // lines contains the stack trace and possibly the inlined source code information.
+ lines []string
+}
+
+// GoRuntimeErrorTracePrefix is the prefix coming before the Go runtime stack trace included in the face of runtime.Error.
+// This is exported for testing purpose.
+const GoRuntimeErrorTracePrefix = "Go runtime stack trace:"
+
+func (s *stackTrace) FromRecovered(recovered interface{}) error {
+ if false {
+ debug.PrintStack()
+ }
+
+ if exitErr, ok := recovered.(*sys.ExitError); ok { // Don't wrap an exit error!
+ return exitErr
+ }
+
+ stack := strings.Join(s.lines, "\n\t")
+
+ // If the error was internal, don't mention it was recovered.
+ if wasmErr, ok := recovered.(*wasmruntime.Error); ok {
+ return fmt.Errorf("wasm error: %w\nwasm stack trace:\n\t%s", wasmErr, stack)
+ }
+
+ // If we have a runtime.Error, something severe happened which should include the stack trace. This could be
+ // a nil pointer from wazero or a user-defined function from HostModuleBuilder.
+ if runtimeErr, ok := recovered.(runtime.Error); ok {
+ return fmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s\n\n%s\n%s",
+ runtimeErr, stack, GoRuntimeErrorTracePrefix, debug.Stack())
+ }
+
+ // At this point we expect the error was from a function defined by HostModuleBuilder that intentionally called panic.
+ if runtimeErr, ok := recovered.(error); ok { // e.g. panic(errors.New("whoops"))
+ return fmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s", runtimeErr, stack)
+ } else { // e.g. panic("whoops")
+ return fmt.Errorf("%v (recovered by wazero)\nwasm stack trace:\n\t%s", recovered, stack)
+ }
+}
+
+// MaxFrames is the maximum number of frames to include in the stack trace.
+const MaxFrames = 30
+
+// AddFrame implements ErrorBuilder.AddFrame
+func (s *stackTrace) AddFrame(funcName string, paramTypes, resultTypes []api.ValueType, sources []string) {
+ if s.frameCount == MaxFrames {
+ return
+ }
+ s.frameCount++
+ sig := signature(funcName, paramTypes, resultTypes)
+ s.lines = append(s.lines, sig)
+ for _, source := range sources {
+ s.lines = append(s.lines, "\t"+source)
+ }
+ if s.frameCount == MaxFrames {
+ s.lines = append(s.lines, "... maybe followed by omitted frames")
+ }
+}