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 }