Source file src/cmd/addr2line/addr2line_test.go

     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  
     5  package main
     6  
     7  import (
     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.
    21  func 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.
    32  func 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  
    45  var (
    46  	addr2linePathOnce sync.Once
    47  	addr2lineExePath  string
    48  	addr2linePathErr  error
    49  )
    50  
    51  func 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  
    72  func 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  
    96  const symName = "cmd/addr2line.TestAddr2Line"
    97  
    98  func 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.
   124  func 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  

View as plain text