github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/stmtlines_test.go (about)

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