1// Copyright 2014 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 5package main 6 7import ( 8 "bufio" 9 "bytes" 10 "internal/testenv" 11 "os" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "sync" 16 "testing" 17) 18 19// TestMain executes the test binary as the addr2line command if 20// GO_ADDR2LINETEST_IS_ADDR2LINE is set, and runs the tests otherwise. 21func TestMain(m *testing.M) { 22 if os.Getenv("GO_ADDR2LINETEST_IS_ADDR2LINE") != "" { 23 main() 24 os.Exit(0) 25 } 26 27 os.Setenv("GO_ADDR2LINETEST_IS_ADDR2LINE", "1") // Set for subprocesses to inherit. 28 os.Exit(m.Run()) 29} 30 31// addr2linePath returns the path to the "addr2line" binary to run. 32func addr2linePath(t testing.TB) string { 33 t.Helper() 34 testenv.MustHaveExec(t) 35 36 addr2linePathOnce.Do(func() { 37 addr2lineExePath, addr2linePathErr = os.Executable() 38 }) 39 if addr2linePathErr != nil { 40 t.Fatal(addr2linePathErr) 41 } 42 return addr2lineExePath 43} 44 45var ( 46 addr2linePathOnce sync.Once 47 addr2lineExePath string 48 addr2linePathErr error 49) 50 51func loadSyms(t *testing.T, dbgExePath string) map[string]string { 52 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", dbgExePath) 53 out, err := cmd.CombinedOutput() 54 if err != nil { 55 t.Fatalf("%v: %v\n%s", cmd, err, string(out)) 56 } 57 syms := make(map[string]string) 58 scanner := bufio.NewScanner(bytes.NewReader(out)) 59 for scanner.Scan() { 60 f := strings.Fields(scanner.Text()) 61 if len(f) < 3 { 62 continue 63 } 64 syms[f[2]] = f[0] 65 } 66 if err := scanner.Err(); err != nil { 67 t.Fatalf("error reading symbols: %v", err) 68 } 69 return syms 70} 71 72func runAddr2Line(t *testing.T, dbgExePath, addr string) (funcname, path, lineno string) { 73 cmd := testenv.Command(t, addr2linePath(t), dbgExePath) 74 cmd.Stdin = strings.NewReader(addr) 75 out, err := cmd.CombinedOutput() 76 if err != nil { 77 t.Fatalf("go tool addr2line %v: %v\n%s", os.Args[0], err, string(out)) 78 } 79 f := strings.Split(string(out), "\n") 80 if len(f) < 3 && f[2] == "" { 81 t.Fatal("addr2line output must have 2 lines") 82 } 83 funcname = f[0] 84 pathAndLineNo := f[1] 85 f = strings.Split(pathAndLineNo, ":") 86 if runtime.GOOS == "windows" && len(f) == 3 { 87 // Reattach drive letter. 88 f = []string{f[0] + ":" + f[1], f[2]} 89 } 90 if len(f) != 2 { 91 t.Fatalf("no line number found in %q", pathAndLineNo) 92 } 93 return funcname, f[0], f[1] 94} 95 96const symName = "cmd/addr2line.TestAddr2Line" 97 98func testAddr2Line(t *testing.T, dbgExePath, addr string) { 99 funcName, srcPath, srcLineNo := runAddr2Line(t, dbgExePath, addr) 100 if symName != funcName { 101 t.Fatalf("expected function name %v; got %v", symName, funcName) 102 } 103 fi1, err := os.Stat("addr2line_test.go") 104 if err != nil { 105 t.Fatalf("Stat failed: %v", err) 106 } 107 108 // Debug paths are stored slash-separated, so convert to system-native. 109 srcPath = filepath.FromSlash(srcPath) 110 fi2, err := os.Stat(srcPath) 111 112 if err != nil { 113 t.Fatalf("Stat failed: %v", err) 114 } 115 if !os.SameFile(fi1, fi2) { 116 t.Fatalf("addr2line_test.go and %s are not same file", srcPath) 117 } 118 if want := "124"; srcLineNo != want { 119 t.Fatalf("line number = %v; want %s", srcLineNo, want) 120 } 121} 122 123// This is line 123. The test depends on that. 124func TestAddr2Line(t *testing.T) { 125 testenv.MustHaveGoBuild(t) 126 127 tmpDir, err := os.MkdirTemp("", "TestAddr2Line") 128 if err != nil { 129 t.Fatal("TempDir failed: ", err) 130 } 131 defer os.RemoveAll(tmpDir) 132 133 // Build copy of test binary with debug symbols, 134 // since the one running now may not have them. 135 exepath := filepath.Join(tmpDir, "testaddr2line_test.exe") 136 out, err := testenv.Command(t, testenv.GoToolPath(t), "test", "-c", "-o", exepath, "cmd/addr2line").CombinedOutput() 137 if err != nil { 138 t.Fatalf("go test -c -o %v cmd/addr2line: %v\n%s", exepath, err, string(out)) 139 } 140 141 syms := loadSyms(t, exepath) 142 143 testAddr2Line(t, exepath, syms[symName]) 144 testAddr2Line(t, exepath, "0x"+syms[symName]) 145} 146