Skip to content

Commit b881d42

Browse files
mknyszekgopherbot
authored andcommitted
sweet: split benchmark results from log and/or debug output
Currently benchmark output all comes out in one big stream and ends up in the results/<benchmark>/<toolchain-name>.results file. Recently, we started dumping a lot more logging information from benchmarks by default, but this has caused problems with benchmark result parsing, since they contain lines that look naively like key-value pairs in the benchmark format. This change resolves this problem by splitting the benchmark result stream from the log and/or debug output stream, placing the latter in results/<benchmark>/toolchain-name>.log. By convention, we make it so that all benchmark driver binaries emit benchmark results to stdout and everything else to stderr to easily separate the streams in the harness and redirect them to different files. Change-Id: Iea4cc253a72db176378a6cc9f68b7f36b9ff60ae Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/602135 Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent c9275a8 commit b881d42

File tree

11 files changed

+55
-30
lines changed

11 files changed

+55
-30
lines changed

sweet/benchmarks/go-build/main.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func run(pkgPath string) error {
104104
baseCmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
105105
baseCmd.Dir = pkgPath
106106
baseCmd.Env = common.NewEnvFromEnviron().MustSet("GOROOT=" + filepath.Dir(filepath.Dir(goTool))).Collapse()
107-
baseCmd.Stdout = os.Stdout
107+
baseCmd.Stdout = os.Stderr // Redirect all tool output to stderr.
108108
baseCmd.Stderr = os.Stderr
109109
cmd, err := cgroups.WrapCommand(baseCmd, "test.scope")
110110
if err != nil {
@@ -133,7 +133,8 @@ func printOtherResults(dir string) error {
133133
if err != nil {
134134
return err
135135
}
136-
if _, err := io.Copy(os.Stderr, f); err != nil {
136+
// Results canonically go to stdout.
137+
if _, err := io.Copy(os.Stdout, f); err != nil {
137138
f.Close()
138139
return err
139140
}
@@ -154,6 +155,9 @@ func runToolexec() error {
154155
benchmark = true
155156
default:
156157
cmd := exec.Command(flag.Args()[0], flag.Args()[1:]...)
158+
// Passing through stdout is necessary here because the go tool needs
159+
// to be able to read stdout from some of these tools. This won't appear
160+
// in the final output.
157161
cmd.Stdout = os.Stdout
158162
cmd.Stderr = os.Stderr
159163
return cmd.Run()
@@ -186,6 +190,9 @@ func runToolexec() error {
186190
}
187191
}
188192
cmd := exec.Command(flag.Args()[0], append(extraFlags, flag.Args()[1:]...)...)
193+
// Passing through stdout is necessary here because the go tool needs
194+
// to be able to read stdout from some of these tools. This won't appear
195+
// in the final output.
189196
cmd.Stdout = os.Stdout
190197
cmd.Stderr = os.Stderr
191198
if benchmark {

sweet/benchmarks/internal/driver/driver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ func (b *B) report() {
387387
})
388388

389389
// Write out stats.
390-
var out io.Writer = os.Stderr
390+
var out io.Writer = os.Stdout
391391
if b.resultsWriter != nil {
392392
out = b.resultsWriter
393393
}

sweet/cmd/sweet/benchmark.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,17 +348,24 @@ func (b *benchmark) execute(cfgs []*common.Config, r *runCfg) error {
348348
args = append(args, dc.DriverArgs()...)
349349
}
350350

351+
// Create log and results file.
351352
results, err := os.Create(filepath.Join(resultsDir, fmt.Sprintf("%s.results", cfg.Name)))
352353
if err != nil {
353354
return fmt.Errorf("create %s results file for %s: %v", b.name, cfg.Name, err)
354355
}
355356
defer results.Close()
357+
log, err := os.Create(filepath.Join(resultsDir, fmt.Sprintf("%s.log", cfg.Name)))
358+
if err != nil {
359+
return fmt.Errorf("create %s log file for %s: %v", b.name, cfg.Name, err)
360+
}
361+
defer results.Close()
356362
setups = append(setups, common.RunConfig{
357363
BinDir: binDir,
358364
TmpDir: tmpDir,
359365
AssetsDir: assetsDir,
360366
Args: args,
361367
Results: results,
368+
Log: log,
362369
Short: r.short,
363370
})
364371
}
@@ -384,11 +391,11 @@ func (b *benchmark) execute(cfgs []*common.Config, r *runCfg) error {
384391
if err := b.harness.Run(cfgs[i], &setup); err != nil {
385392
debug.SetGCPercent(gogc)
386393
// Useful error messages are often in the log. Grab the end.
387-
logTail, tailErr := readFileTail(setup.Results)
394+
logTail, tailErr := readFileTail(setup.Log)
388395
if tailErr != nil {
389396
logTail = fmt.Sprintf("error reading log tail: %s", tailErr)
390397
}
391-
setup.Results.Close()
398+
setup.Log.Close()
392399
return fmt.Errorf("run benchmark %s for config %s: %v\nLog tail:\n%s", b.name, cfgs[i].Name, err, logTail)
393400
}
394401
debug.SetGCPercent(gogc)

sweet/cmd/sweet/integration_test.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -153,38 +153,44 @@ func TestSweetEndToEnd(t *testing.T) {
153153
defer outputMu.Unlock()
154154

155155
// Poke at the results directory.
156-
var matches []string
157-
addMatches := func(fileName string) {
158-
m1, err := filepath.Glob(filepath.Join(resultsDir, "*", fileName))
156+
var resultFiles []string
157+
addResultFiles := func(fileName string) {
158+
matches, err := filepath.Glob(filepath.Join(resultsDir, "*", fileName))
159159
if err != nil {
160160
t.Errorf("failed to search results directory for %s: %v", fileName, err)
161-
} else if len(m1) == 0 {
161+
} else if len(matches) == 0 {
162162
t.Logf("no %s results", fileName)
163163
}
164-
matches = append(matches, m1...)
164+
resultFiles = append(resultFiles, matches...)
165165
}
166166
if hasPGO {
167-
addMatches("go.profile.results")
167+
addResultFiles("go.profile.results")
168168
}
169-
addMatches("go.results")
169+
addResultFiles("go.results")
170170

171171
// Dump additional information in case of error, and
172172
// check for reasonable results in the case of no error.
173-
for _, match := range matches {
174-
benchmark := filepath.Base(filepath.Dir(match))
173+
for _, resultFile := range resultFiles {
174+
benchmark := filepath.Base(filepath.Dir(resultFile))
175175
if runErr != nil {
176176
t.Logf("output for %s:", benchmark)
177+
logFile := resultFile[:len(resultFile)-len(filepath.Ext(resultFile))] + ".log"
178+
log, err := os.ReadFile(logFile)
179+
if err != nil {
180+
t.Errorf("failed to read log for %s: %v", benchmark, err)
181+
continue
182+
}
183+
t.Log(string(log))
177184
}
178-
data, err := os.ReadFile(match)
185+
data, err := os.ReadFile(resultFile)
179186
if err != nil {
180-
t.Errorf("failed to read results for %si: %v", benchmark, err)
187+
t.Errorf("failed to read results for %s: %v", benchmark, err)
181188
continue
182189
}
183-
if runErr != nil {
184-
t.Log(string(data))
185-
continue
190+
// TODO(mknyszek): Do some more exhaustive checking with the benchfmt package.
191+
if !strings.Contains(string(data), "Benchmark") {
192+
t.Errorf("no benchmark data found in result file for %s", benchmark)
186193
}
187-
// TODO(mknyszek): Check to make sure the results look reasonable.
188194
}
189195
if runErr != nil {
190196
t.Logf("command output:\n%s", string(output))

sweet/common/harness.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,16 @@ type RunConfig struct {
6969

7070
// Results is the file to which benchmark results should be appended
7171
// in the Go benchmark format.
72+
//
73+
// By convention, this is the benchmark binary's stdout.
7274
Results *os.File
7375

76+
// Log contains additional information information from the benchmark,
77+
// for example for debugging.
78+
//
79+
// By convention, this is the benchmark binary's stderr.
80+
Log *os.File
81+
7482
// Short indicates whether or not to run a short version of the benchmarks
7583
// for testing. Guaranteed to be the same as GetConfig.Short and
7684
// BuildConfig.Short.

sweet/harnesses/cockroachdb.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func (h CockroachDB) Run(cfg *common.Config, rcfg *common.RunConfig) error {
163163
)
164164
cmd.Env = cfg.ExecEnv.Collapse()
165165
cmd.Stdout = rcfg.Results
166-
cmd.Stderr = rcfg.Results
166+
cmd.Stderr = rcfg.Log
167167
log.TraceCommand(cmd, false)
168168
if err := cmd.Start(); err != nil {
169169
return err

sweet/harnesses/etcd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (h Etcd) Run(cfg *common.Config, rcfg *common.RunConfig) error {
8282
)
8383
cmd.Env = cfg.ExecEnv.Collapse()
8484
cmd.Stdout = rcfg.Results
85-
cmd.Stderr = rcfg.Results
85+
cmd.Stderr = rcfg.Log
8686
log.TraceCommand(cmd, false)
8787
if err := cmd.Run(); err != nil {
8888
return err

sweet/harnesses/go-build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ func (h GoBuild) Run(pcfg *common.Config, rcfg *common.RunConfig) error {
150150
)
151151
cmd.Env = cfg.ExecEnv.Collapse()
152152
cmd.Stdout = rcfg.Results
153-
cmd.Stderr = rcfg.Results
153+
cmd.Stderr = rcfg.Log
154154
log.TraceCommand(cmd, false)
155155
if err := cmd.Run(); err != nil {
156156
return err

sweet/harnesses/gvisor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (h GVisor) Run(cfg *common.Config, rcfg *common.RunConfig) error {
7474
)
7575
cmd.Env = cfg.ExecEnv.Collapse()
7676
cmd.Stdout = rcfg.Results
77-
cmd.Stderr = rcfg.Results
77+
cmd.Stderr = rcfg.Log
7878
log.TraceCommand(cmd, false)
7979
return cmd.Run()
8080
}

sweet/harnesses/local.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ type localBenchHarness struct {
1616
binName string
1717
genArgs func(cfg *common.Config, rcfg *common.RunConfig) []string
1818
beforeRun func(cfg *common.Config, rcfg *common.RunConfig) error
19-
noStdout bool
2019
}
2120

2221
func (h *localBenchHarness) CheckPrerequisites() error {
@@ -42,10 +41,8 @@ func (h *localBenchHarness) Run(cfg *common.Config, rcfg *common.RunConfig) erro
4241
append(rcfg.Args, h.genArgs(cfg, rcfg)...)...,
4342
)
4443
cmd.Env = cfg.ExecEnv.Collapse()
45-
if !h.noStdout {
46-
cmd.Stdout = rcfg.Results
47-
}
48-
cmd.Stderr = rcfg.Results
44+
cmd.Stdout = rcfg.Results
45+
cmd.Stderr = rcfg.Log
4946
log.TraceCommand(cmd, false)
5047
return cmd.Run()
5148
}

0 commit comments

Comments
 (0)