Commit 0d8a3b20 authored by Daniel Martí's avatar Daniel Martí

cmd/compile: add TestIntendedInlining from runtime

Move it from the runtime package, as we will soon add more packages and
functions for it to check.

The test used the testEnv func, which cleaned certain environment
variables from a command, so it was moved to internal/testenv under a
more descriptive (and less ambiguous) name. Add a simple godoc to it
too.

For #21851.

Change-Id: I6f39c1f23b45377718355fafe66ffd87047d8ab6
Reviewed-on: https://go-review.googlesource.com/63550
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarIlya Tocar <ilya.tocar@intel.com>
parent d02477e9
// Copyright 2017 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 gc
import (
"bytes"
"internal/testenv"
"os/exec"
"testing"
)
// TestIntendedInlining tests that specific runtime functions are inlined.
// This allows refactoring for code clarity and re-use without fear that
// changes to the compiler will cause silent performance regressions.
func TestIntendedInlining(t *testing.T) {
if testing.Short() && testenv.Builder() == "" {
t.Skip("skipping in short mode")
}
testenv.MustHaveGoRun(t)
t.Parallel()
// want is the list of function names that should be inlined.
want := []string{"tophash", "add", "(*bmap).keys", "bucketShift", "bucketMask"}
m := make(map[string]bool, len(want))
for _, s := range want {
m[s] = true
}
cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "build", "-a", "-gcflags=-m", "runtime"))
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("%s", out)
t.Fatal(err)
}
lines := bytes.Split(out, []byte{'\n'})
for _, x := range lines {
f := bytes.Split(x, []byte(": can inline "))
if len(f) < 2 {
continue
}
fn := bytes.TrimSpace(f[1])
delete(m, string(fn))
}
for s := range m {
t.Errorf("function %s not inlined", s)
}
}
...@@ -209,3 +209,25 @@ func SkipFlakyNet(t *testing.T) { ...@@ -209,3 +209,25 @@ func SkipFlakyNet(t *testing.T) {
t.Skip("skipping test on builder known to have frequent network failures") t.Skip("skipping test on builder known to have frequent network failures")
} }
} }
// CleanCmdEnv will fill cmd.Env with the environment, excluding certain
// variables that could modify the behavior of the Go tools such as
// GODEBUG and GOTRACEBACK.
func CleanCmdEnv(cmd *exec.Cmd) *exec.Cmd {
if cmd.Env != nil {
panic("environment already set")
}
for _, env := range os.Environ() {
// Exclude GODEBUG from the environment to prevent its output
// from breaking tests that are trying to parse other command output.
if strings.HasPrefix(env, "GODEBUG=") {
continue
}
// Exclude GOTRACEBACK for the same reason.
if strings.HasPrefix(env, "GOTRACEBACK=") {
continue
}
cmd.Env = append(cmd.Env, env)
}
return cmd
}
...@@ -113,7 +113,7 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) { ...@@ -113,7 +113,7 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput() got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("exit status: %v\n%s", err, got) t.Fatalf("exit status: %v\n%s", err, got)
} }
...@@ -136,7 +136,7 @@ func TestCgoExternalThreadSignal(t *testing.T) { ...@@ -136,7 +136,7 @@ func TestCgoExternalThreadSignal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput() got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("exit status: %v\n%s", err, got) t.Fatalf("exit status: %v\n%s", err, got)
} }
...@@ -203,14 +203,14 @@ func TestCgoCheckBytes(t *testing.T) { ...@@ -203,14 +203,14 @@ func TestCgoCheckBytes(t *testing.T) {
const tries = 10 const tries = 10
var tot1, tot2 time.Duration var tot1, tot2 time.Duration
for i := 0; i < tries; i++ { for i := 0; i < tries; i++ {
cmd := testEnv(exec.Command(exe, "CgoCheckBytes")) cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i)) cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
start := time.Now() start := time.Now()
cmd.Run() cmd.Run()
d1 := time.Since(start) d1 := time.Since(start)
cmd = testEnv(exec.Command(exe, "CgoCheckBytes")) cmd = testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes"))
cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i)) cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
start = time.Now() start = time.Now()
...@@ -283,7 +283,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg string) { ...@@ -283,7 +283,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg string) {
t.Fatal(err) t.Fatal(err)
} }
got, err := testEnv(exec.Command(exe, runArg)).CombinedOutput() got, err := testenv.CleanCmdEnv(exec.Command(exe, runArg)).CombinedOutput()
if err != nil { if err != nil {
if testenv.Builder() == "linux-amd64-alpine" { if testenv.Builder() == "linux-amd64-alpine" {
// See Issue 18243 and Issue 19938. // See Issue 18243 and Issue 19938.
...@@ -295,7 +295,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg string) { ...@@ -295,7 +295,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg string) {
defer os.Remove(fn) defer os.Remove(fn)
for try := 0; try < 2; try++ { for try := 0; try < 2; try++ {
cmd := testEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1")) cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1"))
// Check that pprof works both with and without explicit executable on command line. // Check that pprof works both with and without explicit executable on command line.
if try == 0 { if try == 0 {
cmd.Args = append(cmd.Args, exe, fn) cmd.Args = append(cmd.Args, exe, fn)
...@@ -359,7 +359,7 @@ func TestRaceProf(t *testing.T) { ...@@ -359,7 +359,7 @@ func TestRaceProf(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
got, err := testEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput() got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -388,7 +388,7 @@ func TestRaceSignal(t *testing.T) { ...@@ -388,7 +388,7 @@ func TestRaceSignal(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
got, err := testEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput() got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
if err != nil { if err != nil {
t.Logf("%s\n", got) t.Logf("%s\n", got)
t.Fatal(err) t.Fatal(err)
...@@ -431,7 +431,7 @@ func TestCatchPanic(t *testing.T) { ...@@ -431,7 +431,7 @@ func TestCatchPanic(t *testing.T) {
} }
for _, early := range []bool{true, false} { for _, early := range []bool{true, false} {
cmd := testEnv(exec.Command(exe, "CgoCatchPanic")) cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic"))
// Make sure a panic results in a crash. // Make sure a panic results in a crash.
cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
if early { if early {
......
...@@ -32,25 +32,6 @@ func TestMain(m *testing.M) { ...@@ -32,25 +32,6 @@ func TestMain(m *testing.M) {
os.Exit(status) os.Exit(status)
} }
func testEnv(cmd *exec.Cmd) *exec.Cmd {
if cmd.Env != nil {
panic("environment already set")
}
for _, env := range os.Environ() {
// Exclude GODEBUG from the environment to prevent its output
// from breaking tests that are trying to parse other command output.
if strings.HasPrefix(env, "GODEBUG=") {
continue
}
// Exclude GOTRACEBACK for the same reason.
if strings.HasPrefix(env, "GOTRACEBACK=") {
continue
}
cmd.Env = append(cmd.Env, env)
}
return cmd
}
var testprog struct { var testprog struct {
sync.Mutex sync.Mutex
dir string dir string
...@@ -70,7 +51,7 @@ func runTestProg(t *testing.T, binary, name string) string { ...@@ -70,7 +51,7 @@ func runTestProg(t *testing.T, binary, name string) string {
t.Fatal(err) t.Fatal(err)
} }
cmd := testEnv(exec.Command(exe, name)) cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
var b bytes.Buffer var b bytes.Buffer
cmd.Stdout = &b cmd.Stdout = &b
cmd.Stderr = &b cmd.Stderr = &b
...@@ -139,7 +120,7 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) ...@@ -139,7 +120,7 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error)
exe := filepath.Join(testprog.dir, name+".exe") exe := filepath.Join(testprog.dir, name+".exe")
cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...) cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
cmd.Dir = "testdata/" + binary cmd.Dir = "testdata/" + binary
out, err := testEnv(cmd).CombinedOutput() out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil { if err != nil {
target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out) target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
testprog.target[name] = target testprog.target[name] = target
...@@ -158,14 +139,14 @@ var ( ...@@ -158,14 +139,14 @@ var (
func checkStaleRuntime(t *testing.T) { func checkStaleRuntime(t *testing.T) {
staleRuntimeOnce.Do(func() { staleRuntimeOnce.Do(func() {
// 'go run' uses the installed copy of runtime.a, which may be out of date. // 'go run' uses the installed copy of runtime.a, which may be out of date.
out, err := testEnv(exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput() out, err := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
if err != nil { if err != nil {
staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out)) staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out))
return return
} }
if string(out) != "false\n" { if string(out) != "false\n" {
t.Logf("go list -f {{.Stale}} runtime:\n%s", out) t.Logf("go list -f {{.Stale}} runtime:\n%s", out)
out, err := testEnv(exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.StaleReason}}", "runtime")).CombinedOutput() out, err := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.StaleReason}}", "runtime")).CombinedOutput()
if err != nil { if err != nil {
t.Logf("go list -f {{.StaleReason}} failed: %v", err) t.Logf("go list -f {{.StaleReason}} failed: %v", err)
} }
...@@ -468,7 +449,7 @@ func TestMemPprof(t *testing.T) { ...@@ -468,7 +449,7 @@ func TestMemPprof(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
got, err := testEnv(exec.Command(exe, "MemProf")).CombinedOutput() got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -476,7 +457,7 @@ func TestMemPprof(t *testing.T) { ...@@ -476,7 +457,7 @@ func TestMemPprof(t *testing.T) {
defer os.Remove(fn) defer os.Remove(fn)
for try := 0; try < 2; try++ { for try := 0; try < 2; try++ {
cmd := testEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top")) cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
// Check that pprof works both with and without explicit executable on command line. // Check that pprof works both with and without explicit executable on command line.
if try == 0 { if try == 0 {
cmd.Args = append(cmd.Args, exe, fn) cmd.Args = append(cmd.Args, exe, fn)
...@@ -586,7 +567,7 @@ func TestPanicRace(t *testing.T) { ...@@ -586,7 +567,7 @@ func TestPanicRace(t *testing.T) {
const tries = 10 const tries = 10
retry: retry:
for i := 0; i < tries; i++ { for i := 0; i < tries; i++ {
got, err := testEnv(exec.Command(exe, "PanicRace")).CombinedOutput() got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
if err == nil { if err == nil {
t.Logf("try %d: program exited successfully, should have failed", i+1) t.Logf("try %d: program exited successfully, should have failed", i+1)
continue continue
......
...@@ -65,13 +65,13 @@ func TestCrashDumpsAllThreads(t *testing.T) { ...@@ -65,13 +65,13 @@ func TestCrashDumpsAllThreads(t *testing.T) {
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe") cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
cmd.Dir = dir cmd.Dir = dir
out, err := testEnv(cmd).CombinedOutput() out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("building source: %v\n%s", err, out) t.Fatalf("building source: %v\n%s", err, out)
} }
cmd = exec.Command(filepath.Join(dir, "a.exe")) cmd = exec.Command(filepath.Join(dir, "a.exe"))
cmd = testEnv(cmd) cmd = testenv.CleanCmdEnv(cmd)
cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
// Set GOGC=off. Because of golang.org/issue/10958, the tight // Set GOGC=off. Because of golang.org/issue/10958, the tight
...@@ -184,7 +184,7 @@ func TestPanicSystemstack(t *testing.T) { ...@@ -184,7 +184,7 @@ func TestPanicSystemstack(t *testing.T) {
t.Parallel() t.Parallel()
cmd := exec.Command(os.Args[0], "testPanicSystemstackInternal") cmd := exec.Command(os.Args[0], "testPanicSystemstackInternal")
cmd = testEnv(cmd) cmd = testenv.CleanCmdEnv(cmd)
cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") cmd.Env = append(cmd.Env, "GOTRACEBACK=crash")
pr, pw, err := os.Pipe() pr, pw, err := os.Pipe()
if err != nil { if err != nil {
...@@ -249,7 +249,7 @@ func TestSignalExitStatus(t *testing.T) { ...@@ -249,7 +249,7 @@ func TestSignalExitStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = testEnv(exec.Command(exe, "SignalExitStatus")).Run() err = testenv.CleanCmdEnv(exec.Command(exe, "SignalExitStatus")).Run()
if err == nil { if err == nil {
t.Error("test program succeeded unexpectedly") t.Error("test program succeeded unexpectedly")
} else if ee, ok := err.(*exec.ExitError); !ok { } else if ee, ok := err.(*exec.ExitError); !ok {
......
...@@ -132,7 +132,7 @@ func testGdbPython(t *testing.T, cgo bool) { ...@@ -132,7 +132,7 @@ func testGdbPython(t *testing.T, cgo bool) {
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe") cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
cmd.Dir = dir cmd.Dir = dir
out, err := testEnv(cmd).CombinedOutput() out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("building source %v\n%s", err, out) t.Fatalf("building source %v\n%s", err, out)
} }
...@@ -278,7 +278,7 @@ func TestGdbBacktrace(t *testing.T) { ...@@ -278,7 +278,7 @@ func TestGdbBacktrace(t *testing.T) {
} }
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe") cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
cmd.Dir = dir cmd.Dir = dir
out, err := testEnv(cmd).CombinedOutput() out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("building source %v\n%s", err, out) t.Fatalf("building source %v\n%s", err, out)
} }
...@@ -348,7 +348,7 @@ func TestGdbAutotmpTypes(t *testing.T) { ...@@ -348,7 +348,7 @@ func TestGdbAutotmpTypes(t *testing.T) {
} }
cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", "a.exe") cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", "a.exe")
cmd.Dir = dir cmd.Dir = dir
out, err := testEnv(cmd).CombinedOutput() out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("building source %v\n%s", err, out) t.Fatalf("building source %v\n%s", err, out)
} }
......
...@@ -5,10 +5,7 @@ ...@@ -5,10 +5,7 @@
package runtime_test package runtime_test
import ( import (
"bytes"
"internal/testenv"
"io" "io"
"os/exec"
. "runtime" . "runtime"
"runtime/debug" "runtime/debug"
"strings" "strings"
...@@ -357,42 +354,3 @@ func TestVersion(t *testing.T) { ...@@ -357,42 +354,3 @@ func TestVersion(t *testing.T) {
t.Fatalf("cr/nl in version: %q", vers) t.Fatalf("cr/nl in version: %q", vers)
} }
} }
// TestIntendedInlining tests that specific runtime functions are inlined.
// This allows refactoring for code clarity and re-use without fear that
// changes to the compiler will cause silent performance regressions.
func TestIntendedInlining(t *testing.T) {
if testing.Short() && testenv.Builder() == "" {
t.Skip("skipping in short mode")
}
testenv.MustHaveGoRun(t)
t.Parallel()
// want is the list of function names that should be inlined.
want := []string{"tophash", "add", "(*bmap).keys", "bucketShift", "bucketMask"}
m := make(map[string]bool, len(want))
for _, s := range want {
m[s] = true
}
cmd := testEnv(exec.Command(testenv.GoToolPath(t), "build", "-a", "-gcflags=-m", "runtime"))
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("%s", out)
t.Fatal(err)
}
lines := bytes.Split(out, []byte{'\n'})
for _, x := range lines {
f := bytes.Split(x, []byte(": can inline "))
if len(f) < 2 {
continue
}
fn := bytes.TrimSpace(f[1])
delete(m, string(fn))
}
for s := range m {
t.Errorf("function %s not inlined", s)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment