Skip to content

Commit 508ef87

Browse files
committed
cmd/dist: make it possible to filter output of background commands
Many of the commands dist test executes are "background" commands run by a work queue system. The work queue allows it to run commands in parallel, but still serialize their output. Currently, the work queue system assumes that exec.Cmd.Stdout and Stderr will be nil and that it can take complete control over them. We're about to inject output filters on many of these commands, so we need a way to interpose on Stdout and Stderr. This CL rearranges responsibilities in the work queue system to make that possible. Now, the thing enqueuing the work item is responsible to constructing the Cmd to write its output to work.out. There's only one place that constructs work objects (there used to be many more), so that's relatively easy, and sets us up to add filters. For #37486. Change-Id: I55ab71ddd456a12fdbf676bb49f698fc08a5689b Reviewed-on: https://go-review.googlesource.com/c/go/+/494957 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Run-TryBot: Austin Clements <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 3d46eed commit 508ef87

File tree

1 file changed

+24
-18
lines changed

1 file changed

+24
-18
lines changed

src/cmd/dist/test.go

+24-18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"flag"
1010
"fmt"
11+
"io"
1112
"io/fs"
1213
"log"
1314
"os"
@@ -81,9 +82,9 @@ type tester struct {
8182

8283
type work struct {
8384
dt *distTest
84-
cmd *exec.Cmd
85+
cmd *exec.Cmd // Must write stdout/stderr to work.out
8586
start chan bool
86-
out []byte
87+
out bytes.Buffer
8788
err error
8889
end chan bool
8990
}
@@ -315,9 +316,9 @@ type goTest struct {
315316
testFlags []string // Additional flags accepted by this test
316317
}
317318

318-
// bgCommand returns a go test Cmd. The result has Stdout and Stderr set to nil
319-
// and is intended to be added to the work queue.
320-
func (opts *goTest) bgCommand(t *tester) *exec.Cmd {
319+
// bgCommand returns a go test Cmd. The result will write its output to stdout
320+
// and stderr. If stdout==stderr, bgCommand ensures Writes are serialized.
321+
func (opts *goTest) bgCommand(t *tester, stdout, stderr io.Writer) *exec.Cmd {
321322
goCmd, build, run, pkgs, testFlags, setupCmd := opts.buildArgs(t)
322323

323324
// Combine the flags.
@@ -334,16 +335,15 @@ func (opts *goTest) bgCommand(t *tester) *exec.Cmd {
334335

335336
cmd := exec.Command(goCmd, args...)
336337
setupCmd(cmd)
338+
cmd.Stdout = stdout
339+
cmd.Stderr = stderr
337340

338341
return cmd
339342
}
340343

341344
// command returns a go test Cmd intended to be run immediately.
342345
func (opts *goTest) command(t *tester) *exec.Cmd {
343-
cmd := opts.bgCommand(t)
344-
cmd.Stdout = os.Stdout
345-
cmd.Stderr = os.Stderr
346-
return cmd
346+
return opts.bgCommand(t, os.Stdout, os.Stderr)
347347
}
348348

349349
func (opts *goTest) run(t *tester) error {
@@ -948,10 +948,8 @@ func (t *tester) registerTest(name, heading string, test *goTest, opts ...regist
948948
if preFunc != nil && !preFunc(dt) {
949949
return nil
950950
}
951-
w := &work{
952-
dt: dt,
953-
cmd: test.bgCommand(t),
954-
}
951+
w := &work{dt: dt}
952+
w.cmd = test.bgCommand(t, &w.out, &w.out)
955953
t.worklist = append(t.worklist, w)
956954
return nil
957955
})
@@ -1225,17 +1223,23 @@ func (t *tester) runPending(nextTest *distTest) {
12251223
for _, w := range worklist {
12261224
w.start = make(chan bool)
12271225
w.end = make(chan bool)
1226+
// w.cmd must be set up to write to w.out. We can't check that, but we
1227+
// can check for easy mistakes.
1228+
if w.cmd.Stdout == nil || w.cmd.Stdout == os.Stdout || w.cmd.Stderr == nil || w.cmd.Stderr == os.Stderr {
1229+
panic("work.cmd.Stdout/Stderr must be redirected")
1230+
}
12281231
go func(w *work) {
12291232
if !<-w.start {
12301233
timelog("skip", w.dt.name)
1231-
w.out = []byte(fmt.Sprintf("skipped due to earlier error\n"))
1234+
w.out.WriteString("skipped due to earlier error\n")
12321235
} else {
12331236
timelog("start", w.dt.name)
1234-
w.out, w.err = w.cmd.CombinedOutput()
1237+
w.err = w.cmd.Run()
12351238
if w.err != nil {
12361239
if isUnsupportedVMASize(w) {
12371240
timelog("skip", w.dt.name)
1238-
w.out = []byte(fmt.Sprintf("skipped due to unsupported VMA\n"))
1241+
w.out.Reset()
1242+
w.out.WriteString("skipped due to unsupported VMA\n")
12391243
w.err = nil
12401244
}
12411245
}
@@ -1272,7 +1276,9 @@ func (t *tester) runPending(nextTest *distTest) {
12721276
}
12731277
ended++
12741278
<-w.end
1275-
os.Stdout.Write(w.out)
1279+
os.Stdout.Write(w.out.Bytes())
1280+
// We no longer need the output, so drop the buffer.
1281+
w.out = bytes.Buffer{}
12761282
if w.err != nil {
12771283
log.Printf("Failed: %v", w.err)
12781284
t.failed = true
@@ -1599,7 +1605,7 @@ func buildModeSupported(compiler, buildmode, goos, goarch string) bool {
15991605
// arm64 machine configured with 39-bit VMA)
16001606
func isUnsupportedVMASize(w *work) bool {
16011607
unsupportedVMA := []byte("unsupported VMA range")
1602-
return w.dt.name == "race" && bytes.Contains(w.out, unsupportedVMA)
1608+
return w.dt.name == "race" && bytes.Contains(w.out.Bytes(), unsupportedVMA)
16031609
}
16041610

16051611
// isEnvSet reports whether the environment variable evar is

0 commit comments

Comments
 (0)