github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/src/cmd/link/dwarf_test.go (about)

     1  // Copyright 2017 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  	"cmd/internal/objfile"
     9  	"debug/dwarf"
    10  	"internal/testenv"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path"
    16  	"path/filepath"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  )
    21  
    22  func TestDWARF(t *testing.T) {
    23  	testenv.MustHaveCGO(t)
    24  	testenv.MustHaveGoBuild(t)
    25  
    26  	if runtime.GOOS == "plan9" {
    27  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
    28  	}
    29  
    30  	out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput()
    31  	if err != nil {
    32  		t.Fatalf("go list: %v\n%s", err, out)
    33  	}
    34  	if string(out) != "false\n" {
    35  		t.Fatalf("cmd/link is stale - run go install cmd/link")
    36  	}
    37  
    38  	tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
    39  	if err != nil {
    40  		t.Fatal("TempDir failed: ", err)
    41  	}
    42  	defer os.RemoveAll(tmpDir)
    43  
    44  	for _, prog := range []string{"testprog", "testprogcgo"} {
    45  		t.Run(prog, func(t *testing.T) {
    46  			exe := filepath.Join(tmpDir, prog+".exe")
    47  			dir := "../../runtime/testdata/" + prog
    48  			out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, dir).CombinedOutput()
    49  			if err != nil {
    50  				t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
    51  			}
    52  
    53  			f, err := objfile.Open(exe)
    54  			if err != nil {
    55  				t.Fatal(err)
    56  			}
    57  			defer f.Close()
    58  
    59  			syms, err := f.Symbols()
    60  			if err != nil {
    61  				t.Fatal(err)
    62  			}
    63  
    64  			var addr uint64
    65  			for _, sym := range syms {
    66  				if sym.Name == "main.main" {
    67  					addr = sym.Addr
    68  					break
    69  				}
    70  			}
    71  			if addr == 0 {
    72  				t.Fatal("cannot find main.main in symbols")
    73  			}
    74  
    75  			d, err := f.DWARF()
    76  			if err != nil {
    77  				t.Fatal(err)
    78  			}
    79  
    80  			// TODO: We'd like to use filepath.Join here.
    81  			// Also related: golang.org/issue/19784.
    82  			wantFile := path.Join(prog, "main.go")
    83  			wantLine := 24
    84  			r := d.Reader()
    85  			var line dwarf.LineEntry
    86  			for {
    87  				cu, err := r.Next()
    88  				if err != nil {
    89  					t.Fatal(err)
    90  				}
    91  				if cu == nil {
    92  					break
    93  				}
    94  				if cu.Tag != dwarf.TagCompileUnit {
    95  					r.SkipChildren()
    96  					continue
    97  				}
    98  				lr, err := d.LineReader(cu)
    99  				if err != nil {
   100  					t.Fatal(err)
   101  				}
   102  				for {
   103  					err := lr.Next(&line)
   104  					if err == io.EOF {
   105  						break
   106  					}
   107  					if err != nil {
   108  						t.Fatal(err)
   109  					}
   110  					if line.Address == addr {
   111  						if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
   112  							t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
   113  						}
   114  						return
   115  					}
   116  				}
   117  			}
   118  			t.Fatalf("did not find file:line for %#x (main.main)", addr)
   119  		})
   120  	}
   121  }