Source file src/runtime/hexdump_test.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/abi"
    10  	"internal/goarch"
    11  	"runtime"
    12  	"slices"
    13  	"strings"
    14  	"testing"
    15  	"unsafe"
    16  )
    17  
    18  func TestHexdumper(t *testing.T) {
    19  	check := func(label, got, want string) {
    20  		got = strings.TrimRight(got, "\n")
    21  		want = strings.TrimPrefix(want, "\n")
    22  		want = strings.TrimRight(want, "\n")
    23  		if got != want {
    24  			t.Errorf("%s: got\n%s\nwant\n%s", label, got, want)
    25  		}
    26  	}
    27  
    28  	data := make([]byte, 32)
    29  	for i := range data {
    30  		data[i] = 0x10 + byte(i)
    31  	}
    32  
    33  	check("basic", runtime.Hexdumper(0, 1, nil, data), `
    34             0 1 2 3  4 5 6 7   8 9 a b  c d e f  0123456789abcdef
    35  00000000: 10111213 14151617  18191a1b 1c1d1e1f  ................
    36  00000010: 20212223 24252627  28292a2b 2c2d2e2f   !"#$%&'()*+,-./`)
    37  
    38  	if !goarch.BigEndian {
    39  		// Different word sizes
    40  		check("word=4", runtime.Hexdumper(0, 4, nil, data), `
    41             3 2 1 0  7 6 5 4   b a 9 8  f e d c  0123456789abcdef
    42  00000000: 13121110 17161514  1b1a1918 1f1e1d1c  ................
    43  00000010: 23222120 27262524  2b2a2928 2f2e2d2c   !"#$%&'()*+,-./`)
    44  		check("word=8", runtime.Hexdumper(0, 8, nil, data), `
    45             7 6 5 4  3 2 1 0   f e d c  b a 9 8  0123456789abcdef
    46  00000000: 17161514 13121110  1f1e1d1c 1b1a1918  ................
    47  00000010: 27262524 23222120  2f2e2d2c 2b2a2928   !"#$%&'()*+,-./`)
    48  	}
    49  
    50  	// Starting offset
    51  	check("offset=1", runtime.Hexdumper(1, 1, nil, data), `
    52             0 1 2 3  4 5 6 7   8 9 a b  c d e f  0123456789abcdef
    53  00000000:   101112 13141516  1718191a 1b1c1d1e   ...............
    54  00000010: 1f202122 23242526  2728292a 2b2c2d2e  . !"#$%&'()*+,-.
    55  00000020: 2f                                    /`)
    56  	if !goarch.BigEndian {
    57  		// ... combined with a word size
    58  		check("offset=1 and word=4", runtime.Hexdumper(1, 4, nil, data), `
    59             3 2 1 0  7 6 5 4   b a 9 8  f e d c  0123456789abcdef
    60  00000000: 121110   16151413  1a191817 1e1d1c1b   ...............
    61  00000010: 2221201f 26252423  2a292827 2e2d2c2b  . !"#$%&'()*+,-.
    62  00000020:       2f                              /`)
    63  	}
    64  
    65  	// Partial data full of annoying boundaries.
    66  	partials := make([][]byte, 0)
    67  	for i := 0; i < len(data); i += 2 {
    68  		partials = append(partials, data[i:i+2])
    69  	}
    70  	check("partials", runtime.Hexdumper(1, 1, nil, partials...), `
    71             0 1 2 3  4 5 6 7   8 9 a b  c d e f  0123456789abcdef
    72  00000000:   101112 13141516  1718191a 1b1c1d1e   ...............
    73  00000010: 1f202122 23242526  2728292a 2b2c2d2e  . !"#$%&'()*+,-.
    74  00000020: 2f                                    /`)
    75  
    76  	// Marks.
    77  	check("marks", runtime.Hexdumper(0, 1, func(addr uintptr, start func()) {
    78  		if addr%7 == 0 {
    79  			start()
    80  			println("mark")
    81  		}
    82  	}, data), `
    83             0 1 2 3  4 5 6 7   8 9 a b  c d e f  0123456789abcdef
    84  00000000: 10111213 14151617  18191a1b 1c1d1e1f  ................
    85            ^ mark
    86                           ^ mark
    87                                            ^ mark
    88  00000010: 20212223 24252627  28292a2b 2c2d2e2f   !"#$%&'()*+,-./
    89                       ^ mark
    90                                        ^ mark`)
    91  	if !goarch.BigEndian {
    92  		check("marks and word=4", runtime.Hexdumper(0, 4, func(addr uintptr, start func()) {
    93  			if addr%7 == 0 {
    94  				start()
    95  				println("mark")
    96  			}
    97  		}, data), `
    98             3 2 1 0  7 6 5 4   b a 9 8  f e d c  0123456789abcdef
    99  00000000: 13121110 17161514  1b1a1918 1f1e1d1c  ................
   100            ^ mark
   101  00000010: 23222120 27262524  2b2a2928 2f2e2d2c   !"#$%&'()*+,-./
   102                                        ^ mark`)
   103  	}
   104  }
   105  
   106  func TestHexdumpWords(t *testing.T) {
   107  	if goarch.BigEndian || goarch.PtrSize != 8 {
   108  		// We could support these, but it's kind of a pain.
   109  		t.Skip("requires 64-bit little endian")
   110  	}
   111  
   112  	// Most of this is in hexdumper. Here we just test the symbolizer.
   113  
   114  	pc := abi.FuncPCABIInternal(TestHexdumpWords)
   115  	pcs := slices.Repeat([]uintptr{pc}, 3)
   116  
   117  	// Make sure pcs doesn't move around on us.
   118  	var p runtime.Pinner
   119  	defer p.Unpin()
   120  	p.Pin(&pcs[0])
   121  	// Get a 16 byte, 16-byte-aligned chunk of pcs so the hexdump is simple.
   122  	start := uintptr(unsafe.Pointer(&pcs[0]))
   123  	start = (start + 15) &^ uintptr(15)
   124  
   125  	// Do the hex dump.
   126  	got := runtime.HexdumpWords(start, 16)
   127  
   128  	// Construct the expected output.
   129  	pcStr := fmt.Sprintf("%016x", pc)
   130  	pcStr = pcStr[:8] + " " + pcStr[8:] // Add middle space
   131  	ascii := make([]byte, 8)
   132  	for i := range ascii {
   133  		b := byte(pc >> (8 * i))
   134  		if b >= ' ' && b <= '~' {
   135  			ascii[i] = b
   136  		} else {
   137  			ascii[i] = '.'
   138  		}
   139  	}
   140  	want := fmt.Sprintf(`
   141                     7 6 5 4  3 2 1 0   f e d c  b a 9 8  0123456789abcdef
   142  %016x: %s  %s  %s%s
   143                    ^ <runtime_test.TestHexdumpWords+0x0>
   144                                       ^ <runtime_test.TestHexdumpWords+0x0>
   145  `, start, pcStr, pcStr, ascii, ascii)
   146  	want = strings.TrimPrefix(want, "\n")
   147  
   148  	if got != want {
   149  		t.Errorf("got\n%s\nwant\n%s", got, want)
   150  	}
   151  }
   152  

View as plain text