blob: cc44e48e4ba12e9a6109ee2e3ebc23951a799801 [file] [log] [blame]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime_test
import (
"fmt"
"internal/abi"
"internal/goarch"
"runtime"
"slices"
"strings"
"testing"
"unsafe"
)
func TestHexdumper(t *testing.T) {
check := func(label, got, want string) {
got = strings.TrimRight(got, "\n")
want = strings.TrimPrefix(want, "\n")
want = strings.TrimRight(want, "\n")
if got != want {
t.Errorf("%s: got\n%s\nwant\n%s", label, got, want)
}
}
data := make([]byte, 32)
for i := range data {
data[i] = 0x10 + byte(i)
}
check("basic", runtime.Hexdumper(0, 1, nil, data), `
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00000000: 10111213 14151617 18191a1b 1c1d1e1f ................
00000010: 20212223 24252627 28292a2b 2c2d2e2f !"#$%&'()*+,-./`)
if !goarch.BigEndian {
// Different word sizes
check("word=4", runtime.Hexdumper(0, 4, nil, data), `
3 2 1 0 7 6 5 4 b a 9 8 f e d c 0123456789abcdef
00000000: 13121110 17161514 1b1a1918 1f1e1d1c ................
00000010: 23222120 27262524 2b2a2928 2f2e2d2c !"#$%&'()*+,-./`)
check("word=8", runtime.Hexdumper(0, 8, nil, data), `
7 6 5 4 3 2 1 0 f e d c b a 9 8 0123456789abcdef
00000000: 17161514 13121110 1f1e1d1c 1b1a1918 ................
00000010: 27262524 23222120 2f2e2d2c 2b2a2928 !"#$%&'()*+,-./`)
}
// Starting offset
check("offset=1", runtime.Hexdumper(1, 1, nil, data), `
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00000000: 101112 13141516 1718191a 1b1c1d1e ...............
00000010: 1f202122 23242526 2728292a 2b2c2d2e . !"#$%&'()*+,-.
00000020: 2f /`)
if !goarch.BigEndian {
// ... combined with a word size
check("offset=1 and word=4", runtime.Hexdumper(1, 4, nil, data), `
3 2 1 0 7 6 5 4 b a 9 8 f e d c 0123456789abcdef
00000000: 121110 16151413 1a191817 1e1d1c1b ...............
00000010: 2221201f 26252423 2a292827 2e2d2c2b . !"#$%&'()*+,-.
00000020: 2f /`)
}
// Partial data full of annoying boundaries.
partials := make([][]byte, 0)
for i := 0; i < len(data); i += 2 {
partials = append(partials, data[i:i+2])
}
check("partials", runtime.Hexdumper(1, 1, nil, partials...), `
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00000000: 101112 13141516 1718191a 1b1c1d1e ...............
00000010: 1f202122 23242526 2728292a 2b2c2d2e . !"#$%&'()*+,-.
00000020: 2f /`)
// Marks.
check("marks", runtime.Hexdumper(0, 1, func(addr uintptr, start func()) {
if addr%7 == 0 {
start()
println("mark")
}
}, data), `
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00000000: 10111213 14151617 18191a1b 1c1d1e1f ................
^ mark
^ mark
^ mark
00000010: 20212223 24252627 28292a2b 2c2d2e2f !"#$%&'()*+,-./
^ mark
^ mark`)
if !goarch.BigEndian {
check("marks and word=4", runtime.Hexdumper(0, 4, func(addr uintptr, start func()) {
if addr%7 == 0 {
start()
println("mark")
}
}, data), `
3 2 1 0 7 6 5 4 b a 9 8 f e d c 0123456789abcdef
00000000: 13121110 17161514 1b1a1918 1f1e1d1c ................
^ mark
00000010: 23222120 27262524 2b2a2928 2f2e2d2c !"#$%&'()*+,-./
^ mark`)
}
}
func TestHexdumpWords(t *testing.T) {
if goarch.BigEndian || goarch.PtrSize != 8 {
// We could support these, but it's kind of a pain.
t.Skip("requires 64-bit little endian")
}
// Most of this is in hexdumper. Here we just test the symbolizer.
pc := abi.FuncPCABIInternal(TestHexdumpWords)
pcs := slices.Repeat([]uintptr{pc}, 3)
// Make sure pcs doesn't move around on us.
var p runtime.Pinner
defer p.Unpin()
p.Pin(&pcs[0])
// Get a 16 byte, 16-byte-aligned chunk of pcs so the hexdump is simple.
start := uintptr(unsafe.Pointer(&pcs[0]))
start = (start + 15) &^ uintptr(15)
// Do the hex dump.
got := runtime.HexdumpWords(start, 16)
// Construct the expected output.
pcStr := fmt.Sprintf("%016x", pc)
pcStr = pcStr[:8] + " " + pcStr[8:] // Add middle space
ascii := make([]byte, 8)
for i := range ascii {
b := byte(pc >> (8 * i))
if b >= ' ' && b <= '~' {
ascii[i] = b
} else {
ascii[i] = '.'
}
}
want := fmt.Sprintf(`
7 6 5 4 3 2 1 0 f e d c b a 9 8 0123456789abcdef
%016x: %s %s %s%s
^ <runtime_test.TestHexdumpWords+0x0>
^ <runtime_test.TestHexdumpWords+0x0>
`, start, pcStr, pcStr, ascii, ascii)
want = strings.TrimPrefix(want, "\n")
if got != want {
t.Errorf("got\n%s\nwant\n%s", got, want)
}
}