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

View as plain text