-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cmd/compile: extend dump-to-file to handle "genssa" (asm) case.
Extend the existing dump-to-file to also do assembly output to make it easier to write debug-information tests that check for line-numbering in particular orders. Includes POC test (which is silent w/o -v): go test -v -run TestDebugLines cmd/compile/internal/ssa === RUN TestDebugLines Preserving temporary directory /var/folders/v6/xyzzy/T/debug_lines_test321 About to run (cd /var/folders/v6/xyzzy/T/debug_lines_test321; \ GOSSADIR=/var/folders/v6/xyzzy/T/debug_lines_test321 \ /Users/drchase/work/go/bin/go build -o foo.o \ '-gcflags=-N -l -d=ssa/genssa/dump=sayhi' \ /Users/drchase/work/go/src/cmd/compile/internal/ssa/testdata/sayhi.go ) Saw stmt# 8 for submatch '8' on dump line #7 = ' v107 00005 (+8) MOVQ AX, "".n(SP)' Saw stmt# 9 for submatch '9' on dump line #9 = ' v87 00007 (+9) MOVUPS X15, ""..autotmp_2-32(SP)' Saw stmt# 10 for submatch '10' on dump line #46 = ' v65 00044 (+10) MOVUPS X15, ""..autotmp_2-32(SP)' Saw stmt# 11 for submatch '11' on dump line #83 = ' v131 00081 (+11) MOVQ "".wg+8(SP), AX' --- PASS: TestDebugLines (4.95s) PASS ok cmd/compile/internal/ssa 5.685s Includes a test to ensure that inlining information is printed correctly. Updates #47880. Change-Id: I83b596476a88687d71d5b65dbb94641a576d747e Reviewed-on: https://go-review.googlesource.com/c/go/+/348970 Trust: David Chase <[email protected]> Run-TryBot: David Chase <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
- Loading branch information
Showing
8 changed files
with
360 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright 2021 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package ssa_test | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"flag" | ||
"runtime" | ||
"sort" | ||
|
||
// "flag" | ||
"fmt" | ||
"internal/testenv" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"reflect" | ||
"regexp" | ||
"strconv" | ||
"testing" | ||
) | ||
|
||
// Matches lines in genssa output that are marked "isstmt", and the parenthesized plus-prefixed line number is a submatch | ||
var asmLine *regexp.Regexp = regexp.MustCompile(`^\s[vb][0-9]+\s+[0-9]+\s\(\+([0-9]+)\)`) | ||
|
||
// this matches e.g. ` v123456789 000007 (+9876654310) MOVUPS X15, ""..autotmp_2-32(SP)` | ||
|
||
// Matches lines in genssa output that describe an inlined file (on a Unix filesystem). Note it expects an unadventurous choice of basename. | ||
var inlineLine *regexp.Regexp = regexp.MustCompile(`^#\s/.*/[-a-zA-Z0-9_]+\.go:([0-9]+)`) | ||
|
||
// this matches e.g. # /pa/inline-dumpxxxx.go:6 | ||
|
||
var testGoArchFlag = flag.String("arch", "", "run test for specified architecture") | ||
|
||
func testGoArch() string { | ||
if *testGoArchFlag == "" { | ||
return runtime.GOARCH | ||
} | ||
return *testGoArchFlag | ||
} | ||
|
||
func TestDebugLines(t *testing.T) { | ||
if runtime.GOOS == "windows" { | ||
t.Skip("Windows lacks $HOME which complicates workaround for 'missing $GOPATH'") // $HOME needed to work around #43938 | ||
} | ||
// This test is potentially fragile, the goal is that debugging should step properly through "sayhi" | ||
// If the blocks are reordered in a way that changes the statement order but execution flows correctly, | ||
// then rearrange the expected numbers. Register abi and not-register-abi also have different sequences, | ||
// at least for now. | ||
|
||
switch testGoArch() { | ||
case "arm64", "amd64": // register ABI | ||
testDebugLines(t, "sayhi.go", "sayhi", []int{8, 9, 10, 11}) | ||
|
||
case "arm", "386": // probably not register ABI for a while | ||
testDebugLines(t, "sayhi.go", "sayhi", []int{9, 10, 11}) | ||
|
||
default: // expect ppc64le and riscv will pick up register ABI soonish, not sure about others | ||
t.Skip("skipped for many architectures, also changes w/ register ABI") | ||
} | ||
} | ||
|
||
func TestInlineLines(t *testing.T) { | ||
if runtime.GOOS == "windows" { | ||
t.Skip("Windows lacks $HOME which complicates workaround for 'missing $GOPATH'") // $HOME needed to work around #43938 | ||
} | ||
if runtime.GOARCH != "amd64" && *testGoArchFlag == "" { | ||
// As of september 2021, works for everything except mips64, but still potentially fragile | ||
t.Skip("only runs for amd64 unless -arch explicitly supplied") | ||
} | ||
|
||
want := [][]int{{3}, {4, 10}, {4, 10, 16}, {4, 10}, {4, 11, 16}, {4, 11}, {4}, {5, 10}, {5, 10, 16}, {5, 10}, {5, 11, 16}, {5, 11}, {5}} | ||
testInlineStack(t, "inline-dump.go", "f", want) | ||
} | ||
|
||
func compileAndDump(t *testing.T, file, function, moreGCFlags string) []byte { | ||
testenv.MustHaveGoBuild(t) | ||
|
||
tmpdir, err := ioutil.TempDir("", "debug_lines_test") | ||
if err != nil { | ||
panic(fmt.Sprintf("Problem creating TempDir, error %v", err)) | ||
} | ||
if testing.Verbose() { | ||
fmt.Printf("Preserving temporary directory %s\n", tmpdir) | ||
} else { | ||
defer os.RemoveAll(tmpdir) | ||
} | ||
|
||
source, err := filepath.Abs(filepath.Join("testdata", file)) | ||
if err != nil { | ||
panic(fmt.Sprintf("Could not get abspath of testdata directory and file, %v", err)) | ||
} | ||
|
||
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "foo.o", "-gcflags=-d=ssa/genssa/dump="+function+" "+moreGCFlags, source) | ||
cmd.Dir = tmpdir | ||
cmd.Env = replaceEnv(cmd.Env, "GOSSADIR", tmpdir) | ||
cmd.Env = replaceEnv(cmd.Env, "HOME", os.Getenv("HOME")) // workaround for #43938 | ||
testGoos := "linux" // default to linux | ||
if testGoArch() == "wasm" { | ||
testGoos = "js" | ||
} | ||
cmd.Env = replaceEnv(cmd.Env, "GOOS", testGoos) | ||
cmd.Env = replaceEnv(cmd.Env, "GOARCH", testGoArch()) | ||
|
||
if testing.Verbose() { | ||
fmt.Printf("About to run %s\n", asCommandLine("", cmd)) | ||
} | ||
|
||
var stdout, stderr bytes.Buffer | ||
cmd.Stdout = &stdout | ||
cmd.Stderr = &stderr | ||
|
||
if err := cmd.Run(); err != nil { | ||
t.Fatalf("error running cmd %s: %v\nstdout:\n%sstderr:\n%s\n", asCommandLine("", cmd), err, stdout.String(), stderr.String()) | ||
} | ||
|
||
if s := stderr.String(); s != "" { | ||
t.Fatalf("Wanted empty stderr, instead got:\n%s\n", s) | ||
} | ||
|
||
dumpFile := filepath.Join(tmpdir, function+"_01__genssa.dump") | ||
dumpBytes, err := os.ReadFile(dumpFile) | ||
if err != nil { | ||
t.Fatalf("Could not read dump file %s, err=%v", dumpFile, err) | ||
} | ||
return dumpBytes | ||
} | ||
|
||
func sortInlineStacks(x [][]int) { | ||
sort.Slice(x, func(i, j int) bool { | ||
if len(x[i]) != len(x[j]) { | ||
return len(x[i]) < len(x[j]) | ||
} | ||
for k := range x[i] { | ||
if x[i][k] != x[j][k] { | ||
return x[i][k] < x[j][k] | ||
} | ||
} | ||
return false | ||
}) | ||
} | ||
|
||
// testInlineStack ensures that inlining is described properly in the comments in the dump file | ||
func testInlineStack(t *testing.T, file, function string, wantStacks [][]int) { | ||
// this is an inlining reporting test, not an optimization test. -N makes it less fragile | ||
dumpBytes := compileAndDump(t, file, function, "-N") | ||
dump := bufio.NewScanner(bytes.NewReader(dumpBytes)) | ||
dumpLineNum := 0 | ||
var gotStmts []int | ||
var gotStacks [][]int | ||
for dump.Scan() { | ||
line := dump.Text() | ||
dumpLineNum++ | ||
matches := inlineLine.FindStringSubmatch(line) | ||
if len(matches) == 2 { | ||
stmt, err := strconv.ParseInt(matches[1], 10, 32) | ||
if err != nil { | ||
t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err) | ||
} | ||
if testing.Verbose() { | ||
fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line) | ||
} | ||
gotStmts = append(gotStmts, int(stmt)) | ||
} else if len(gotStmts) > 0 { | ||
gotStacks = append(gotStacks, gotStmts) | ||
gotStmts = nil | ||
} | ||
} | ||
if len(gotStmts) > 0 { | ||
gotStacks = append(gotStacks, gotStmts) | ||
gotStmts = nil | ||
} | ||
sortInlineStacks(gotStacks) | ||
sortInlineStacks(wantStacks) | ||
if !reflect.DeepEqual(wantStacks, gotStacks) { | ||
t.Errorf("wanted inlines %+v but got %+v", wantStacks, gotStacks) | ||
} | ||
|
||
} | ||
|
||
// testDebugLines compiles testdata/<file> with flags -N -l and -d=ssa/genssa/dump=<function> | ||
// then verifies that the statement-marked lines in that file are the same as those in wantStmts | ||
// These files must all be short because this is super-fragile. | ||
// "go build" is run in a temporary directory that is normally deleted, unless -test.v | ||
func testDebugLines(t *testing.T, file, function string, wantStmts []int) { | ||
dumpBytes := compileAndDump(t, file, function, "-N -l") | ||
dump := bufio.NewScanner(bytes.NewReader(dumpBytes)) | ||
var gotStmts []int | ||
dumpLineNum := 0 | ||
for dump.Scan() { | ||
line := dump.Text() | ||
dumpLineNum++ | ||
matches := asmLine.FindStringSubmatch(line) | ||
if len(matches) == 2 { | ||
stmt, err := strconv.ParseInt(matches[1], 10, 32) | ||
if err != nil { | ||
t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err) | ||
} | ||
if testing.Verbose() { | ||
fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line) | ||
} | ||
gotStmts = append(gotStmts, int(stmt)) | ||
} | ||
} | ||
if !reflect.DeepEqual(wantStmts, gotStmts) { | ||
t.Errorf("wanted stmts %v but got %v", wantStmts, gotStmts) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.