dwarf: ignores tombstone entries in inlined information (#1488)
Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
@@ -45,6 +45,35 @@ func TestWithDebugInfo(t *testing.T) {
|
|||||||
bin []byte
|
bin []byte
|
||||||
exp string
|
exp string
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "tinygo",
|
||||||
|
bin: dwarftestdata.TinyGoWasm,
|
||||||
|
exp: `module[] function[_start] failed: wasm error: unreachable
|
||||||
|
wasm stack trace:
|
||||||
|
.runtime._panic(i32)
|
||||||
|
0x18f3: /runtime_tinygowasm.go:70:6
|
||||||
|
.main.c()
|
||||||
|
0x2ff9: /main.go:16:7
|
||||||
|
.main.b()
|
||||||
|
0x2f97: /main.go:12:3
|
||||||
|
.main.a()
|
||||||
|
0x2f39: /main.go:8:3
|
||||||
|
.main.main()
|
||||||
|
0x2149: /main.go:4:3
|
||||||
|
.runtime.run$1()
|
||||||
|
0x1fcb: /scheduler_any.go:25:11
|
||||||
|
.runtime.run$1$gowrapper(i32)
|
||||||
|
0x6f0: /scheduler_any.go:23:2
|
||||||
|
.tinygo_launch(i32)
|
||||||
|
0x23: /task_asyncify_wasm.S:59
|
||||||
|
.runtime.scheduler()
|
||||||
|
0x1ec4: /task_asyncify.go:109:17 (inlined)
|
||||||
|
/scheduler.go:236:11
|
||||||
|
.runtime.run()
|
||||||
|
0x1d92: /scheduler_any.go:28:11
|
||||||
|
._start()
|
||||||
|
0x1d12: /runtime_wasm_wasi.go:21:5`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "zig",
|
name: "zig",
|
||||||
bin: dwarftestdata.ZigWasm,
|
bin: dwarftestdata.ZigWasm,
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import (
|
|||||||
// line number, source code file, etc, can change. Therefore,
|
// line number, source code file, etc, can change. Therefore,
|
||||||
// even though these binaries are huge, we check them in to the repositories.
|
// even though these binaries are huge, we check them in to the repositories.
|
||||||
|
|
||||||
|
//go:embed testdata/tinygo/main.wasm
|
||||||
|
var TinyGoWasm []byte
|
||||||
|
|
||||||
//go:embed testdata/zig/main.wasm
|
//go:embed testdata/zig/main.wasm
|
||||||
var ZigWasm []byte
|
var ZigWasm []byte
|
||||||
|
|
||||||
|
|||||||
17
internal/testing/dwarftestdata/testdata/tinygo/main.go
vendored
Normal file
17
internal/testing/dwarftestdata/testdata/tinygo/main.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a()
|
||||||
|
}
|
||||||
|
|
||||||
|
func a() {
|
||||||
|
b()
|
||||||
|
}
|
||||||
|
|
||||||
|
func b() {
|
||||||
|
c()
|
||||||
|
}
|
||||||
|
|
||||||
|
func c() {
|
||||||
|
panic("NOOOOOOOOOOOOOOO")
|
||||||
|
}
|
||||||
BIN
internal/testing/dwarftestdata/testdata/tinygo/main.wasm
vendored
Executable file
BIN
internal/testing/dwarftestdata/testdata/tinygo/main.wasm
vendored
Executable file
Binary file not shown.
@@ -33,6 +33,19 @@ func NewDWARFLines(d *dwarf.Data) *DWARFLines {
|
|||||||
return &DWARFLines{d: d, linesPerEntry: map[dwarf.Offset][]line{}}
|
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
|
// 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.
|
// 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) {
|
func (d *DWARFLines) Line(instructionOffset uint64) (ret []string) {
|
||||||
@@ -76,7 +89,11 @@ entry:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, pcs := range ranges {
|
for _, pcs := range ranges {
|
||||||
if pcs[0] <= instructionOffset && instructionOffset < pcs[1] {
|
start, end := pcs[0], pcs[1]
|
||||||
|
if isTombstoneAddr(start) || isTombstoneAddr(end) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if start <= instructionOffset && instructionOffset < end {
|
||||||
switch ent.Tag {
|
switch ent.Tag {
|
||||||
case dwarf.TagCompileUnit:
|
case dwarf.TagCompileUnit:
|
||||||
cu = ent
|
cu = ent
|
||||||
@@ -122,6 +139,8 @@ entry:
|
|||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return
|
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})
|
lines = append(lines, line{addr: le.Address, pos: pos})
|
||||||
}
|
}
|
||||||
sort.Slice(lines, func(i, j int) bool { return lines[i].addr < lines[j].addr })
|
sort.Slice(lines, func(i, j int) bool { return lines[i].addr < lines[j].addr })
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package wasmdebug_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
@@ -195,3 +196,78 @@ func TestDWARFLines_Line_Rust(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDWARFLines_Line_TinyGo(t *testing.T) {
|
||||||
|
mod, err := binary.DecodeModule(dwarftestdata.TinyGoWasm, api.CoreFeaturesV2, wasm.MemoryLimitPages, false, true, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, mod.DWARFLines)
|
||||||
|
|
||||||
|
// codeSecStart is the beginning of the code section in the Wasm binary.
|
||||||
|
// If dwarftestdata.TinyGoWasm has been changed, we need to inspect by `wasm-tools objdump`.
|
||||||
|
const codeSecStart = 0x16f
|
||||||
|
|
||||||
|
// These cases are crafted by matching the stack trace result from wasmtime. To verify, run:
|
||||||
|
//
|
||||||
|
// WASMTIME_BACKTRACE_DETAILS=1 wasmtime run internal/testing/dwarftestdata/testdata/tinygo/main.wasm
|
||||||
|
//
|
||||||
|
// And this should produce the output as:
|
||||||
|
//
|
||||||
|
// Caused by:
|
||||||
|
// 0: failed to invoke command default
|
||||||
|
// 1: error while executing at wasm backtrace:
|
||||||
|
// 0: 0x1a62 - runtime.abort
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/runtime/runtime_tinygowasm.go:70:6 - runtime._panic
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/runtime/panic.go:52:7
|
||||||
|
// 1: 0x3168 - main.c
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/tmo/main.go:16:7
|
||||||
|
// 2: 0x3106 - main.b
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/tmo/main.go:12:3
|
||||||
|
// 3: 0x30a8 - main.a
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/tmo/main.go:8:3
|
||||||
|
// 4: 0x22b8 - main.main
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/tmo/main.go:4:3
|
||||||
|
// 5: 0x213a - runtime.run$1
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/runtime/scheduler_any.go:25:11
|
||||||
|
// 6: 0x85f - <goroutine wrapper>
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/runtime/scheduler_any.go:23:2
|
||||||
|
// 7: 0x192 - tinygo_launch
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/internal/task/task_asyncify_wasm.S:59
|
||||||
|
// 8: 0x2033 - (*internal/task.Task).Resume
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/internal/task/task_asyncify.go:109:17 - runtime.scheduler
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/runtime/scheduler.go:236:11
|
||||||
|
// 9: 0x1f01 - runtime.run
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/runtime/scheduler_any.go:28:11
|
||||||
|
// 10: 0x1e81 - _start
|
||||||
|
// at /Users/mathetake/Downloads/tinygo/src/runtime/runtime_wasm_wasi.go:21:5
|
||||||
|
for _, tc := range []struct {
|
||||||
|
offset uint64
|
||||||
|
exp []string
|
||||||
|
}{
|
||||||
|
{offset: 0x1e81 - codeSecStart, exp: []string{"runtime/runtime_wasm_wasi.go:21:5"}},
|
||||||
|
{offset: 0x1f01 - codeSecStart, exp: []string{"runtime/scheduler_any.go:28:11"}},
|
||||||
|
{offset: 0x2033 - codeSecStart, exp: []string{
|
||||||
|
"internal/task/task_asyncify.go:109:17",
|
||||||
|
"runtime/scheduler.go:236:11",
|
||||||
|
}},
|
||||||
|
{offset: 0x192 - codeSecStart, exp: []string{"internal/task/task_asyncify_wasm.S:59"}},
|
||||||
|
{offset: 0x85f - codeSecStart, exp: []string{"runtime/scheduler_any.go:23:2"}},
|
||||||
|
{offset: 0x213a - codeSecStart, exp: []string{"runtime/scheduler_any.go:25:11"}},
|
||||||
|
{offset: 0x22b8 - codeSecStart, exp: []string{"main.go:4:3"}},
|
||||||
|
{offset: 0x30a8 - codeSecStart, exp: []string{"main.go:8:3"}},
|
||||||
|
{offset: 0x3106 - codeSecStart, exp: []string{"main.go:12:3"}},
|
||||||
|
{offset: 0x3168 - codeSecStart, exp: []string{"main.go:16:7"}},
|
||||||
|
// Note(important): this case is different from the output of Wasmtime, which produces the incorrect inline info (panic.go:52:7).
|
||||||
|
// Actually, "runtime_tinygowasm.go:70:6" invokes trap() which is translated as "unreachable" instruction by LLVM, so there won't be
|
||||||
|
// any inlined function invocation here.
|
||||||
|
{offset: 0x1a62 - codeSecStart, exp: []string{"runtime/runtime_tinygowasm.go:70:6"}},
|
||||||
|
} {
|
||||||
|
tc := tc
|
||||||
|
t.Run(fmt.Sprintf("%#x/%s", tc.offset, tc.exp), func(t *testing.T) {
|
||||||
|
actual := mod.DWARFLines.Line(tc.offset)
|
||||||
|
require.Equal(t, len(tc.exp), len(actual), "\nexp: %s\ngot: %s", strings.Join(tc.exp, "\n"), strings.Join(actual, "\n"))
|
||||||
|
for i := range tc.exp {
|
||||||
|
require.Contains(t, actual[i], tc.exp[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user