Home | History | Annotate | Download | only in addr2line
      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 	"io/ioutil"
     12 	"os"
     13 	"os/exec"
     14 	"path/filepath"
     15 	"runtime"
     16 	"strings"
     17 	"testing"
     18 )
     19 
     20 func loadSyms(t *testing.T) map[string]string {
     21 	cmd := exec.Command("go", "tool", "nm", os.Args[0])
     22 	out, err := cmd.CombinedOutput()
     23 	if err != nil {
     24 		t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
     25 	}
     26 	syms := make(map[string]string)
     27 	scanner := bufio.NewScanner(bytes.NewReader(out))
     28 	for scanner.Scan() {
     29 		f := strings.Fields(scanner.Text())
     30 		if len(f) < 3 {
     31 			continue
     32 		}
     33 		syms[f[2]] = f[0]
     34 	}
     35 	if err := scanner.Err(); err != nil {
     36 		t.Fatalf("error reading symbols: %v", err)
     37 	}
     38 	return syms
     39 }
     40 
     41 func runAddr2Line(t *testing.T, exepath, addr string) (funcname, path, lineno string) {
     42 	cmd := exec.Command(exepath, os.Args[0])
     43 	cmd.Stdin = strings.NewReader(addr)
     44 	out, err := cmd.CombinedOutput()
     45 	if err != nil {
     46 		t.Fatalf("go tool addr2line %v: %v\n%s", os.Args[0], err, string(out))
     47 	}
     48 	f := strings.Split(string(out), "\n")
     49 	if len(f) < 3 && f[2] == "" {
     50 		t.Fatal("addr2line output must have 2 lines")
     51 	}
     52 	funcname = f[0]
     53 	pathAndLineNo := f[1]
     54 	f = strings.Split(pathAndLineNo, ":")
     55 	if runtime.GOOS == "windows" && len(f) == 3 {
     56 		// Reattach drive letter.
     57 		f = []string{f[0] + ":" + f[1], f[2]}
     58 	}
     59 	if len(f) != 2 {
     60 		t.Fatalf("no line number found in %q", pathAndLineNo)
     61 	}
     62 	return funcname, f[0], f[1]
     63 }
     64 
     65 const symName = "cmd/addr2line.TestAddr2Line"
     66 
     67 func testAddr2Line(t *testing.T, exepath, addr string) {
     68 	funcName, srcPath, srcLineNo := runAddr2Line(t, exepath, addr)
     69 	if symName != funcName {
     70 		t.Fatalf("expected function name %v; got %v", symName, funcName)
     71 	}
     72 	fi1, err := os.Stat("addr2line_test.go")
     73 	if err != nil {
     74 		t.Fatalf("Stat failed: %v", err)
     75 	}
     76 	fi2, err := os.Stat(srcPath)
     77 	if err != nil {
     78 		t.Fatalf("Stat failed: %v", err)
     79 	}
     80 	if !os.SameFile(fi1, fi2) {
     81 		t.Fatalf("addr2line_test.go and %s are not same file", srcPath)
     82 	}
     83 	if srcLineNo != "89" {
     84 		t.Fatalf("line number = %v; want 89", srcLineNo)
     85 	}
     86 }
     87 
     88 // This is line 88. The test depends on that.
     89 func TestAddr2Line(t *testing.T) {
     90 	testenv.MustHaveGoBuild(t)
     91 
     92 	syms := loadSyms(t)
     93 
     94 	tmpDir, err := ioutil.TempDir("", "TestAddr2Line")
     95 	if err != nil {
     96 		t.Fatal("TempDir failed: ", err)
     97 	}
     98 	defer os.RemoveAll(tmpDir)
     99 
    100 	exepath := filepath.Join(tmpDir, "testaddr2line.exe")
    101 	out, err := exec.Command("go", "build", "-o", exepath, "cmd/addr2line").CombinedOutput()
    102 	if err != nil {
    103 		t.Fatalf("go build -o %v cmd/addr2line: %v\n%s", exepath, err, string(out))
    104 	}
    105 
    106 	testAddr2Line(t, exepath, syms[symName])
    107 	testAddr2Line(t, exepath, "0x"+syms[symName])
    108 }
    109