Source file src/cmd/compile/internal/ssa/stmtlines_test.go

     1  // Copyright 2018 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 ssa_test
     6  
     7  import (
     8  	cmddwarf "cmd/internal/dwarf"
     9  	"cmd/internal/quoted"
    10  	"debug/dwarf"
    11  	"debug/elf"
    12  	"debug/macho"
    13  	"debug/pe"
    14  	"fmt"
    15  	"internal/platform"
    16  	"internal/testenv"
    17  	"internal/xcoff"
    18  	"io"
    19  	"os"
    20  	"runtime"
    21  	"sort"
    22  	"testing"
    23  )
    24  
    25  func open(path string) (*dwarf.Data, error) {
    26  	if fh, err := elf.Open(path); err == nil {
    27  		return fh.DWARF()
    28  	}
    29  
    30  	if fh, err := pe.Open(path); err == nil {
    31  		return fh.DWARF()
    32  	}
    33  
    34  	if fh, err := macho.Open(path); err == nil {
    35  		return fh.DWARF()
    36  	}
    37  
    38  	if fh, err := xcoff.Open(path); err == nil {
    39  		return fh.DWARF()
    40  	}
    41  
    42  	return nil, fmt.Errorf("unrecognized executable format")
    43  }
    44  
    45  func must(err error) {
    46  	if err != nil {
    47  		panic(err)
    48  	}
    49  }
    50  
    51  type Line struct {
    52  	File string
    53  	Line int
    54  }
    55  
    56  func TestStmtLines(t *testing.T) {
    57  	if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) {
    58  		t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH)
    59  	}
    60  
    61  	if runtime.GOOS == "aix" {
    62  		extld := os.Getenv("CC")
    63  		if extld == "" {
    64  			extld = "gcc"
    65  		}
    66  		extldArgs, err := quoted.Split(extld)
    67  		if err != nil {
    68  			t.Fatal(err)
    69  		}
    70  		enabled, err := cmddwarf.IsDWARFEnabledOnAIXLd(extldArgs)
    71  		if err != nil {
    72  			t.Fatal(err)
    73  		}
    74  		if !enabled {
    75  			t.Skip("skipping on aix: no DWARF with ld version < 7.2.2 ")
    76  		}
    77  	}
    78  
    79  	// Build cmd/go forcing DWARF enabled, as a large test case.
    80  	dir := t.TempDir()
    81  	out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-w=0", "-o", dir+"/test.exe", "cmd/go").CombinedOutput()
    82  	if err != nil {
    83  		t.Fatalf("go build: %v\n%s", err, out)
    84  	}
    85  
    86  	lines := map[Line]bool{}
    87  	dw, err := open(dir + "/test.exe")
    88  	must(err)
    89  	rdr := dw.Reader()
    90  	rdr.Seek(0)
    91  	for {
    92  		e, err := rdr.Next()
    93  		must(err)
    94  		if e == nil {
    95  			break
    96  		}
    97  		if e.Tag != dwarf.TagCompileUnit {
    98  			continue
    99  		}
   100  		pkgname, _ := e.Val(dwarf.AttrName).(string)
   101  		if pkgname == "runtime" {
   102  			continue
   103  		}
   104  		if pkgname == "crypto/internal/nistec/fiat" {
   105  			continue // golang.org/issue/49372
   106  		}
   107  		if e.Val(dwarf.AttrStmtList) == nil {
   108  			continue
   109  		}
   110  		lrdr, err := dw.LineReader(e)
   111  		must(err)
   112  
   113  		var le dwarf.LineEntry
   114  
   115  		for {
   116  			err := lrdr.Next(&le)
   117  			if err == io.EOF {
   118  				break
   119  			}
   120  			must(err)
   121  			fl := Line{le.File.Name, le.Line}
   122  			lines[fl] = lines[fl] || le.IsStmt
   123  		}
   124  	}
   125  
   126  	nonStmtLines := []Line{}
   127  	for line, isstmt := range lines {
   128  		if !isstmt {
   129  			nonStmtLines = append(nonStmtLines, line)
   130  		}
   131  	}
   132  
   133  	var m int
   134  	if runtime.GOARCH == "amd64" {
   135  		m = 1 // > 99% obtained on amd64, no backsliding
   136  	} else if runtime.GOARCH == "riscv64" {
   137  		m = 3 // XXX temporary update threshold to 97% for regabi
   138  	} else {
   139  		m = 2 // expect 98% elsewhere.
   140  	}
   141  
   142  	if len(nonStmtLines)*100 > m*len(lines) {
   143  		t.Errorf("Saw too many (%s, > %d%%) lines without statement marks, total=%d, nostmt=%d ('-run TestStmtLines -v' lists failing lines)\n", runtime.GOARCH, m, len(lines), len(nonStmtLines))
   144  	}
   145  	t.Logf("Saw %d out of %d lines without statement marks", len(nonStmtLines), len(lines))
   146  	if testing.Verbose() {
   147  		sort.Slice(nonStmtLines, func(i, j int) bool {
   148  			if nonStmtLines[i].File != nonStmtLines[j].File {
   149  				return nonStmtLines[i].File < nonStmtLines[j].File
   150  			}
   151  			return nonStmtLines[i].Line < nonStmtLines[j].Line
   152  		})
   153  		for _, l := range nonStmtLines {
   154  			t.Logf("%s:%d has no DWARF is_stmt mark\n", l.File, l.Line)
   155  		}
   156  	}
   157  	t.Logf("total=%d, nostmt=%d\n", len(lines), len(nonStmtLines))
   158  }
   159  

View as plain text