github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/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  		if os.Getenv("GOROOT_FINAL_OLD") != "" {
    36  			t.Skip("cmd/link is stale, but $GOROOT_FINAL_OLD is set")
    37  		}
    38  		t.Fatalf("cmd/link is stale - run go install cmd/link")
    39  	}
    40  
    41  	tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
    42  	if err != nil {
    43  		t.Fatal("TempDir failed: ", err)
    44  	}
    45  	defer os.RemoveAll(tmpDir)
    46  
    47  	for _, prog := range []string{"testprog", "testprogcgo"} {
    48  		t.Run(prog, func(t *testing.T) {
    49  			exe := filepath.Join(tmpDir, prog+".exe")
    50  			dir := "../../runtime/testdata/" + prog
    51  			out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, dir).CombinedOutput()
    52  			if err != nil {
    53  				t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
    54  			}
    55  
    56  			f, err := objfile.Open(exe)
    57  			if err != nil {
    58  				t.Fatal(err)
    59  			}
    60  			defer f.Close()
    61  
    62  			syms, err := f.Symbols()
    63  			if err != nil {
    64  				t.Fatal(err)
    65  			}
    66  
    67  			var addr uint64
    68  			for _, sym := range syms {
    69  				if sym.Name == "main.main" {
    70  					addr = sym.Addr
    71  					break
    72  				}
    73  			}
    74  			if addr == 0 {
    75  				t.Fatal("cannot find main.main in symbols")
    76  			}
    77  
    78  			d, err := f.DWARF()
    79  			if err != nil {
    80  				t.Fatal(err)
    81  			}
    82  
    83  			// TODO: We'd like to use filepath.Join here.
    84  			// Also related: golang.org/issue/19784.
    85  			wantFile := path.Join(prog, "main.go")
    86  			wantLine := 24
    87  			r := d.Reader()
    88  			var line dwarf.LineEntry
    89  			for {
    90  				cu, err := r.Next()
    91  				if err != nil {
    92  					t.Fatal(err)
    93  				}
    94  				if cu == nil {
    95  					break
    96  				}
    97  				if cu.Tag != dwarf.TagCompileUnit {
    98  					r.SkipChildren()
    99  					continue
   100  				}
   101  				lr, err := d.LineReader(cu)
   102  				if err != nil {
   103  					t.Fatal(err)
   104  				}
   105  				for {
   106  					err := lr.Next(&line)
   107  					if err == io.EOF {
   108  						break
   109  					}
   110  					if err != nil {
   111  						t.Fatal(err)
   112  					}
   113  					if line.Address == addr {
   114  						if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
   115  							t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
   116  						}
   117  						return
   118  					}
   119  				}
   120  			}
   121  			t.Fatalf("did not find file:line for %#x (main.main)", addr)
   122  		})
   123  	}
   124  }