summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/wasmdebug
diff options
context:
space:
mode:
authorLibravatar Terin Stock <terinjokes@gmail.com>2025-03-09 17:47:56 +0100
committerLibravatar Terin Stock <terinjokes@gmail.com>2025-03-10 01:59:49 +0100
commit3ac1ee16f377d31a0fb80c8dae28b6239ac4229e (patch)
treef61faa581feaaeaba2542b9f2b8234a590684413 /vendor/github.com/tetratelabs/wazero/internal/wasmdebug
parent[chore] update URLs to forked source (diff)
downloadgotosocial-3ac1ee16f377d31a0fb80c8dae28b6239ac4229e.tar.xz
[chore] remove vendor
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/wasmdebug')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go170
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/wasmdebug/dwarf.go225
2 files changed, 0 insertions, 395 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go b/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go
deleted file mode 100644
index ff0e0cccc..000000000
--- a/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/debug.go
+++ /dev/null
@@ -1,170 +0,0 @@
-// 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")
- }
-}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/dwarf.go b/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/dwarf.go
deleted file mode 100644
index 50ba7b2b3..000000000
--- a/vendor/github.com/tetratelabs/wazero/internal/wasmdebug/dwarf.go
+++ /dev/null
@@ -1,225 +0,0 @@
-package wasmdebug
-
-import (
- "debug/dwarf"
- "errors"
- "fmt"
- "io"
- "sort"
- "strings"
- "sync"
-)
-
-// DWARFLines is used to retrieve source code line information from the DWARF data.
-type DWARFLines struct {
- // d is created by DWARF custom sections.
- d *dwarf.Data
- // linesPerEntry maps dwarf.Offset for dwarf.Entry to the list of lines contained by the entry.
- // The value is sorted in the increasing order by the address.
- linesPerEntry map[dwarf.Offset][]line
- mux sync.Mutex
-}
-
-type line struct {
- addr uint64
- pos dwarf.LineReaderPos
-}
-
-// NewDWARFLines returns DWARFLines for the given *dwarf.Data.
-func NewDWARFLines(d *dwarf.Data) *DWARFLines {
- if d == nil {
- return nil
- }
- return &DWARFLines{d: d, linesPerEntry: map[dwarf.Offset][]line{}}
-}
-
-// isTombstoneAddr returns true if the given address is invalid a.k.a tombstone address which was made no longer valid
-// by linker. According to the DWARF spec[1], the value is encoded as 0xffffffff for Wasm (as 32-bit target),
-// but some tools encode it either in -1, -2 [2] or 1<<32 (This might not be by tools, but by debug/dwarf package's bug).
-//
-// [1] https://dwarfstd.org/issues/200609.1.html
-// [2] https://github.com/WebAssembly/binaryen/blob/97178d08d4a20d2a5e3a6be813fc6a7079ef86e1/src/wasm/wasm-debug.cpp#L651-L660
-// [3] https://reviews.llvm.org/D81784
-func isTombstoneAddr(addr uint64) bool {
- addr32 := int32(addr)
- return addr32 == -1 || addr32 == -2 ||
- addr32 == 0 // This covers 1 <<32.
-}
-
-// Line returns the line information for the given instructionOffset which is an offset in
-// the code section of the original Wasm binary. Returns empty string if the info is not found.
-func (d *DWARFLines) Line(instructionOffset uint64) (ret []string) {
- if d == nil {
- return
- }
-
- // DWARFLines is created per Wasm binary, so there's a possibility that multiple instances
- // created from a same binary face runtime error at the same time, and that results in
- // concurrent access to this function.
- d.mux.Lock()
- defer d.mux.Unlock()
-
- r := d.d.Reader()
-
- var inlinedRoutines []*dwarf.Entry
- var cu *dwarf.Entry
- var inlinedDone bool
-entry:
- for {
- ent, err := r.Next()
- if err != nil || ent == nil {
- break
- }
-
- // If we already found the compilation unit and relevant inlined routines, we can stop searching entries.
- if cu != nil && inlinedDone {
- break
- }
-
- switch ent.Tag {
- case dwarf.TagCompileUnit, dwarf.TagInlinedSubroutine:
- default:
- // Only CompileUnit and InlinedSubroutines are relevant.
- continue
- }
-
- // Check if the entry spans the range which contains the target instruction.
- ranges, err := d.d.Ranges(ent)
- if err != nil {
- continue
- }
- for _, pcs := range ranges {
- start, end := pcs[0], pcs[1]
- if isTombstoneAddr(start) || isTombstoneAddr(end) {
- continue
- }
- if start <= instructionOffset && instructionOffset < end {
- switch ent.Tag {
- case dwarf.TagCompileUnit:
- cu = ent
- case dwarf.TagInlinedSubroutine:
- inlinedRoutines = append(inlinedRoutines, ent)
- // Search inlined subroutines until all the children.
- inlinedDone = !ent.Children
- // Not that "children" in the DWARF spec is defined as the next entry to this entry.
- // See "2.3 Relationship of Debugging Information Entries" in https://dwarfstd.org/doc/DWARF4.pdf
- }
- continue entry
- }
- }
- }
-
- // If the relevant compilation unit is not found, nothing we can do with this DWARF info.
- if cu == nil {
- return
- }
-
- lineReader, err := d.d.LineReader(cu)
- if err != nil || lineReader == nil {
- return
- }
- var lines []line
- var ok bool
- var le dwarf.LineEntry
- // Get the lines inside the entry.
- if lines, ok = d.linesPerEntry[cu.Offset]; !ok {
- // If not found, we create the list of lines by reading all the LineEntries in the Entry.
- //
- // Note that the dwarf.LineEntry.SeekPC API shouldn't be used because the Go's dwarf package assumes that
- // all the line entries in an Entry are sorted in increasing order which *might not* be true
- // for some languages. Such order requirement is not a part of DWARF specification,
- // and in fact Zig language tends to emit interleaved line information.
- //
- // Thus, here we read all line entries here, and sort them in the increasing order wrt addresses.
- for {
- pos := lineReader.Tell()
- err = lineReader.Next(&le)
- if errors.Is(err, io.EOF) {
- break
- } else if err != nil {
- return
- }
- // TODO: Maybe we should ignore tombstone addresses by using isTombstoneAddr,
- // but not sure if that would be an issue in practice.
- lines = append(lines, line{addr: le.Address, pos: pos})
- }
- sort.Slice(lines, func(i, j int) bool { return lines[i].addr < lines[j].addr })
- d.linesPerEntry[cu.Offset] = lines // Caches for the future inquiries for the same Entry.
- }
-
- // Now we have the lines for this entry. We can find the corresponding source line for instructionOffset
- // via binary search on the list.
- n := len(lines)
- index := sort.Search(n, func(i int) bool { return lines[i].addr >= instructionOffset })
-
- if index == n { // This case the address is not found. See the doc sort.Search.
- return
- }
-
- ln := lines[index]
- if ln.addr != instructionOffset {
- // If the address doesn't match exactly, the previous entry is the one that contains the instruction.
- // That can happen anytime as the DWARF spec allows it, and other tools can handle it in this way conventionally
- // https://github.com/gimli-rs/addr2line/blob/3a2dbaf84551a06a429f26e9c96071bb409b371f/src/lib.rs#L236-L242
- // https://github.com/kateinoigakukun/wasminspect/blob/f29f052f1b03104da9f702508ac0c1bbc3530ae4/crates/debugger/src/dwarf/mod.rs#L453-L459
- if index-1 < 0 {
- return
- }
- ln = lines[index-1]
- }
-
- // Advance the line reader for the found position.
- lineReader.Seek(ln.pos)
- err = lineReader.Next(&le)
- if err != nil {
- // If we reach this block, that means there's a bug in the []line creation logic above.
- panic("BUG: stored dwarf.LineReaderPos is invalid")
- }
-
- // In the inlined case, the line info is the innermost inlined function call.
- inlined := len(inlinedRoutines) != 0
- prefix := fmt.Sprintf("%#x: ", instructionOffset)
- ret = append(ret, formatLine(prefix, le.File.Name, int64(le.Line), int64(le.Column), inlined))
-
- if inlined {
- prefix = strings.Repeat(" ", len(prefix))
- files := lineReader.Files()
- // inlinedRoutines contain the inlined call information in the reverse order (children is higher than parent),
- // so we traverse the reverse order and emit the inlined calls.
- for i := len(inlinedRoutines) - 1; i >= 0; i-- {
- inlined := inlinedRoutines[i]
- fileIndex, ok := inlined.Val(dwarf.AttrCallFile).(int64)
- if !ok {
- return
- } else if fileIndex >= int64(len(files)) {
- // This in theory shouldn't happen according to the spec, but guard against ill-formed DWARF info.
- return
- }
- fileName := files[fileIndex]
- line, _ := inlined.Val(dwarf.AttrCallLine).(int64)
- col, _ := inlined.Val(dwarf.AttrCallColumn).(int64)
- ret = append(ret, formatLine(prefix, fileName.Name, line, col,
- // Last one is the origin of the inlined function calls.
- i != 0))
- }
- }
- return
-}
-
-func formatLine(prefix, fileName string, line, col int64, inlined bool) string {
- builder := strings.Builder{}
- builder.WriteString(prefix)
- builder.WriteString(fileName)
-
- if line != 0 {
- builder.WriteString(fmt.Sprintf(":%d", line))
- if col != 0 {
- builder.WriteString(fmt.Sprintf(":%d", col))
- }
- }
-
- if inlined {
- builder.WriteString(" (inlined)")
- }
- return builder.String()
-}