cmd/bent: pull Configuration into its own file
Nonfunctional change, pulling the Configuration structure and methods
out of bent.go into a separate file.
Change-Id: Ic1db482eb7affe79d0dc596135a67fcea2eb1606
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/290731
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Jeremy Faller <jeremy@golang.org>
Run-TryBot: Jeremy Faller <jeremy@golang.org>
Reviewed-by: David Chase <drchase@google.com>
diff --git a/cmd/bent/bent.go b/cmd/bent/bent.go
index 8dfc107..393f7a6 100644
--- a/cmd/bent/bent.go
+++ b/cmd/bent/bent.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Go Authors. All rights reserved.
+// 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.
@@ -7,12 +7,10 @@
package main
import (
- "bufio"
"bytes"
"embed"
"flag"
"fmt"
- "io"
"io/ioutil"
"math/rand"
"os"
@@ -22,7 +20,6 @@
"runtime"
"strconv"
"strings"
- "sync"
"time"
"github.com/BurntSushi/toml"
@@ -33,22 +30,6 @@
RealTime, UserTime, SysTime int64 // nanoseconds, -1 if missing.
}
-type Configuration struct {
- Name string // Short name used for binary names, mention on command line
- Root string // Specific Go root to use for this trial
- BuildFlags []string // BuildFlags supplied to 'go test -c' for building (e.g., "-p 1")
- AfterBuild []string // Array of commands to run, output of all commands for a configuration (across binaries) is collected in <runstamp>.<config>.<cmd>
- GcFlags string // GcFlags supplied to 'go test -c' for building
- GcEnv []string // Environment variables supplied to 'go test -c' for building
- RunFlags []string // Extra flags passed to the test binary
- RunEnv []string // Extra environment variables passed to the test binary
- RunWrapper []string // (Outermost) Command and args to precede whatever the operation is; may fail in the sandbox.
- Disabled bool // True if this configuration is temporarily disabled
- buildStats []BenchStat
- benchWriter *os.File
- rootCopy string // The contents of GOROOT are copied here to allow benchmarking of just the test compilation.
-}
-
type Benchmark struct {
Name string // Short name for benchmark/test
Contact string // Contact not used, but may be present in description
@@ -1039,243 +1020,6 @@
}
}
-func (c *Configuration) buildBenchName() string {
- return c.thingBenchName("build")
-}
-
-func (c *Configuration) thingBenchName(suffix string) string {
- if strings.ContainsAny(suffix, "/") {
- suffix = suffix[strings.LastIndex(suffix, "/")+1:]
- }
- return benchDir + "/" + runstamp + "." + c.Name + "." + suffix
-}
-
-func (c *Configuration) benchName(b *Benchmark) string {
- return b.Name + "_" + c.Name
-}
-
-func (c *Configuration) goCommand() string {
- gocmd := "go"
- if c.Root != "" {
- gocmd = c.Root + "bin/" + gocmd
- }
- return gocmd
-}
-
-func (c *Configuration) goCommandCopy() string {
- gocmd := "go"
- if c.rootCopy != "" {
- gocmd = c.rootCopy + "bin/" + gocmd
- }
- return gocmd
-}
-
-func (config *Configuration) createFilesForLater() {
- if config.Disabled {
- return
- }
- f, err := os.Create(config.buildBenchName())
- if err != nil {
- fmt.Println("Error creating build benchmark file ", config.buildBenchName(), ", err=", err)
- config.Disabled = true
- } else {
- fmt.Fprintf(f, "goos: %s\n", runtime.GOOS)
- fmt.Fprintf(f, "goarch: %s\n", runtime.GOARCH)
- f.Close() // will be appending later
- }
-
- for _, cmd := range config.AfterBuild {
- tbn := config.thingBenchName(cmd)
- f, err := os.Create(tbn)
- if err != nil {
- fmt.Printf("Error creating %s benchmark file %s, err=%v\n", cmd, config.thingBenchName(cmd), err)
- continue
- } else {
- f.Close() // will be appending later
- }
- }
-}
-
-func (config *Configuration) runOtherBenchmarks(b *Benchmark, cwd string) {
- // Run various other "benchmark" commands on the built binaries, e.g., size, quality of debugging information.
- if config.Disabled {
- return
- }
-
- for _, cmd := range config.AfterBuild {
- tbn := config.thingBenchName(cmd)
- f, err := os.OpenFile(tbn, os.O_WRONLY|os.O_APPEND, os.ModePerm)
- if err != nil {
- fmt.Printf("There was an error opening %s for append, error %v\n", tbn, err)
- continue
- }
-
- if !strings.ContainsAny(cmd, "/") {
- cmd = cwd + "/" + cmd
- }
- if b.Disabled {
- continue
- }
- testBinaryName := config.benchName(b)
- c := exec.Command(cmd, testBinDir+"/"+testBinaryName, b.Name)
-
- c.Env = defaultEnv
- if !b.NotSandboxed {
- c.Env = replaceEnv(c.Env, "GOOS", "linux")
- }
- // Match the build environment here.
- c.Env = replaceEnvs(c.Env, b.GcEnv)
- c.Env = replaceEnvs(c.Env, config.GcEnv)
-
- if verbose > 0 {
- fmt.Println(asCommandLine(cwd, c))
- }
- output, err := c.CombinedOutput()
- if verbose > 0 || err != nil {
- fmt.Println(string(output))
- } else {
- fmt.Print(".")
- }
- if err != nil {
- fmt.Printf("Error running %s\n", cmd)
- continue
- }
- f.Write(output)
- f.Sync()
- f.Close()
- }
-}
-
-func (config *Configuration) compileOne(bench *Benchmark, cwd string, count int) string {
- root := config.rootCopy
- gocmd := config.goCommandCopy()
- gopath := cwd + "/gopath"
-
- if explicitAll != 1 { // clear cache unless "-a[=1]" which requests -a on compilation.
- cmd := exec.Command(gocmd, "clean", "-cache")
- cmd.Env = defaultEnv
- if !bench.NotSandboxed {
- cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
- }
- if root != "" {
- cmd.Env = replaceEnv(cmd.Env, "GOROOT", root)
- }
- cmd.Env = replaceEnvs(cmd.Env, bench.GcEnv)
- cmd.Env = replaceEnvs(cmd.Env, config.GcEnv)
- cmd.Dir = gopath // Only want the cache-cleaning effect, not the binary-deleting effect. It's okay to clean gopath.
- s, _ := config.runBinary("", cmd, true)
- if s != "" {
- fmt.Println("Error running go clean -cache, ", s)
- }
- }
-
- // Prefix with time for build benchmarking:
- cmd := exec.Command("/usr/bin/time", "-p", gocmd, "test", "-vet=off", "-c")
- cmd.Args = append(cmd.Args, bench.BuildFlags...)
- // Do not normally need -a because cache was emptied first and std was -a installed with these flags.
- // But for -a=1, do it anyway
- if explicitAll == 1 {
- cmd.Args = append(cmd.Args, "-a")
- }
- cmd.Args = append(cmd.Args, config.BuildFlags...)
- if config.GcFlags != "" {
- cmd.Args = append(cmd.Args, "-gcflags="+config.GcFlags)
- }
- cmd.Args = append(cmd.Args, ".")
- cmd.Dir = gopath + "/src/" + bench.Repo
- cmd.Env = defaultEnv
- if !bench.NotSandboxed {
- cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
- }
- if root != "" {
- cmd.Env = replaceEnv(cmd.Env, "GOROOT", root)
- }
- cmd.Env = replaceEnvs(cmd.Env, bench.GcEnv)
- cmd.Env = replaceEnvs(cmd.Env, config.GcEnv)
-
- if verbose > 0 {
- fmt.Println(asCommandLine(cwd, cmd))
- } else {
- fmt.Print(".")
- }
-
- defer cleanup(gopath)
-
- output, err := cmd.CombinedOutput()
- if err != nil {
- s := ""
- switch e := err.(type) {
- case *exec.ExitError:
- s = fmt.Sprintf("There was an error running 'go test', output = %s", output)
- default:
- s = fmt.Sprintf("There was an error running 'go test', output = %s, error = %v", output, e)
- }
- fmt.Println(s + "DISABLING benchmark " + bench.Name)
- bench.Disabled = true // if it won't compile, it won't run, either.
- return s + "(" + bench.Name + ")\n"
- }
- soutput := string(output)
- // Capture times from the end of the output.
- rbt := extractTime(soutput, "real")
- ubt := extractTime(soutput, "user")
- sbt := extractTime(soutput, "sys")
- config.buildStats = append(config.buildStats,
- BenchStat{Name: bench.Name, RealTime: rbt, UserTime: ubt, SysTime: sbt})
-
- // Report and record build stats to testbin
-
- buf := new(bytes.Buffer)
- configGoArch := getenv(config.GcEnv, "GOARCH")
- if configGoArch != runtime.GOARCH && configGoArch != "" {
- s := fmt.Sprintf("goarch: %s-%s\n", runtime.GOARCH, configGoArch)
- if verbose > 0 {
- fmt.Print(s)
- }
- buf.WriteString(s)
- }
- s := fmt.Sprintf("Benchmark%s 1 %d build-real-ns/op %d build-user-ns/op %d build-sys-ns/op\n",
- strings.Title(bench.Name), rbt, ubt, sbt)
- if verbose > 0 {
- fmt.Print(s)
- }
- buf.WriteString(s)
- f, err := os.OpenFile(config.buildBenchName(), os.O_WRONLY|os.O_APPEND, os.ModePerm)
- if err != nil {
- fmt.Printf("There was an error opening %s for append, error %v\n", config.buildBenchName(), err)
- cleanup(gopath)
- os.Exit(2)
- }
- f.Write(buf.Bytes())
- f.Sync()
- f.Close()
-
- // Move generated binary to well-known place.
- from := cmd.Dir + "/" + bench.testBinaryName()
- to := testBinDir + "/" + config.benchName(bench)
- err = os.Rename(from, to)
- if err != nil {
- fmt.Printf("There was an error renaming %s to %s, %v\n", from, to, err)
- cleanup(gopath)
- os.Exit(1)
- }
- // Trim /usr/bin/time info from soutput, it's ugly
- if verbose > 0 {
- fmt.Println("mv " + from + " " + to + "")
- i := strings.LastIndex(soutput, "real")
- if i >= 0 {
- soutput = soutput[:i]
- }
- fmt.Print(soutput)
- }
-
- // Do this here before any cleanup.
- if count == 0 {
- config.runOtherBenchmarks(bench, cwd)
- }
-
- return ""
-}
-
func escape(s string) string {
s = strings.Replace(s, "\\", "\\\\", -1)
s = strings.Replace(s, "'", "\\'", -1)
@@ -1333,122 +1077,11 @@
fmt.Printf("Copied asset %s to current directory\n", file)
}
-// runBinary runs cmd and displays the output.
-// If the command returns an error, returns an error string.
-func (c *Configuration) runBinary(cwd string, cmd *exec.Cmd, printWorkingDot bool) (string, int) {
- line := asCommandLine(cwd, cmd)
- if verbose > 0 {
- fmt.Println(line)
- } else {
- if printWorkingDot {
- fmt.Print(".")
- }
- }
-
- rc := 0
-
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return fmt.Sprintf("Error [stdoutpipe] running '%s', %v", line, err), rc
- }
- stderr, err := cmd.StderrPipe()
- if err != nil {
- return fmt.Sprintf("Error [stderrpipe] running '%s', %v", line, err), rc
- }
- err = cmd.Start()
- if err != nil {
- return fmt.Sprintf("Error [command start] running '%s', %v", line, err), rc
- }
-
- var mu = &sync.Mutex{}
-
- f := func(r *bufio.Reader, done chan error) {
- for {
- bytes, err := r.ReadBytes('\n')
- n := len(bytes)
- if n > 0 {
- mu.Lock()
- nw, err := c.benchWriter.Write(bytes[0:n])
- if err != nil {
- fmt.Printf("Error writing, err = %v, nwritten = %d, nrequested = %d\n", err, nw, n)
- }
- c.benchWriter.Sync()
- fmt.Print(string(bytes[0:n]))
- mu.Unlock()
- }
- if err == io.EOF || n == 0 {
- break
- }
- if err != nil {
- done <- err
- return
- }
- }
- done <- nil
- }
-
- doneS := make(chan error)
- doneE := make(chan error)
-
- go f(bufio.NewReader(stdout), doneS)
- go f(bufio.NewReader(stderr), doneE)
-
- errS := <-doneS
- errE := <-doneE
-
- err = cmd.Wait()
- rc = cmd.ProcessState.ExitCode()
-
- if err != nil {
- switch e := err.(type) {
- case *exec.ExitError:
- return fmt.Sprintf("Error running '%s', stderr = %s, rc = %d", line, e.Stderr, rc), rc
- default:
- return fmt.Sprintf("Error running '%s', %v, rc = %d", line, e, rc), rc
-
- }
- }
- if errS != nil {
- return fmt.Sprintf("Error [read stdout] running '%s', %v, rc = %d", line, errS, rc), rc
- }
- if errE != nil {
- return fmt.Sprintf("Error [read stderr] running '%s', %v, rc = %d", line, errE, rc), rc
- }
- return "", rc
-}
-
// testBinaryName returns the name of the binary produced by "go test -c"
func (b *Benchmark) testBinaryName() string {
return b.Repo[strings.LastIndex(b.Repo, "/")+1:] + ".test"
}
-// extractTime extracts a time (from /usr/bin/time -p) based on the tag
-// and returns the time converted to nanoseconds. Missing times and bad
-// data result in NaN.
-func extractTime(output, label string) int64 {
- // find tag in first column
- li := strings.LastIndex(output, label)
- if li < 0 {
- return -1
- }
- output = output[li+len(label):]
- // lose intervening white space
- li = strings.IndexAny(output, "0123456789-.eEdD")
- if li < 0 {
- return -1
- }
- output = output[li:]
- li = strings.IndexAny(output, "\n\r\t ")
- if li >= 0 { // failing to find EOL is a special case of done.
- output = output[:li]
- }
- x, err := strconv.ParseFloat(output, 64)
- if err != nil {
- return -1
- }
- return int64(x * 1000 * 1000 * 1000)
-}
-
// inheritEnv extracts ev from the os environment and
// returns env extended with that new environment variable.
// Does not check if ev already exists in env.
diff --git a/cmd/bent/configuration.go b/cmd/bent/configuration.go
new file mode 100644
index 0000000..a606b3f
--- /dev/null
+++ b/cmd/bent/configuration.go
@@ -0,0 +1,387 @@
+// 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.
+
+// +build go1.16
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// Configuration is a structure that holds all the variables necessary to
+// initiate a bent run. These structures are read from a .toml file at
+// boot-time.
+type Configuration struct {
+ Name string // Short name used for binary names, mention on command line
+ Root string // Specific Go root to use for this trial
+ BuildFlags []string // BuildFlags supplied to 'go test -c' for building (e.g., "-p 1")
+ AfterBuild []string // Array of commands to run, output of all commands for a configuration (across binaries) is collected in <runstamp>.<config>.<cmd>
+ GcFlags string // GcFlags supplied to 'go test -c' for building
+ GcEnv []string // Environment variables supplied to 'go test -c' for building
+ RunFlags []string // Extra flags passed to the test binary
+ RunEnv []string // Extra environment variables passed to the test binary
+ RunWrapper []string // (Outermost) Command and args to precede whatever the operation is; may fail in the sandbox.
+ Disabled bool // True if this configuration is temporarily disabled
+ buildStats []BenchStat
+ benchWriter *os.File
+ rootCopy string // The contents of GOROOT are copied here to allow benchmarking of just the test compilation.
+}
+
+func (c *Configuration) buildBenchName() string {
+ return c.thingBenchName("build")
+}
+
+func (c *Configuration) thingBenchName(suffix string) string {
+ if strings.ContainsAny(suffix, "/") {
+ suffix = suffix[strings.LastIndex(suffix, "/")+1:]
+ }
+ return benchDir + "/" + runstamp + "." + c.Name + "." + suffix
+}
+
+func (c *Configuration) benchName(b *Benchmark) string {
+ return b.Name + "_" + c.Name
+}
+
+func (c *Configuration) goCommand() string {
+ gocmd := "go"
+ if c.Root != "" {
+ gocmd = c.Root + "bin/" + gocmd
+ }
+ return gocmd
+}
+
+func (c *Configuration) goCommandCopy() string {
+ gocmd := "go"
+ if c.rootCopy != "" {
+ gocmd = c.rootCopy + "bin/" + gocmd
+ }
+ return gocmd
+}
+
+func (config *Configuration) createFilesForLater() {
+ if config.Disabled {
+ return
+ }
+ f, err := os.Create(config.buildBenchName())
+ if err != nil {
+ fmt.Println("Error creating build benchmark file ", config.buildBenchName(), ", err=", err)
+ config.Disabled = true
+ } else {
+ fmt.Fprintf(f, "goos: %s\n", runtime.GOOS)
+ fmt.Fprintf(f, "goarch: %s\n", runtime.GOARCH)
+ f.Close() // will be appending later
+ }
+
+ for _, cmd := range config.AfterBuild {
+ tbn := config.thingBenchName(cmd)
+ f, err := os.Create(tbn)
+ if err != nil {
+ fmt.Printf("Error creating %s benchmark file %s, err=%v\n", cmd, config.thingBenchName(cmd), err)
+ continue
+ } else {
+ f.Close() // will be appending later
+ }
+ }
+}
+
+func (config *Configuration) runOtherBenchmarks(b *Benchmark, cwd string) {
+ // Run various other "benchmark" commands on the built binaries, e.g., size, quality of debugging information.
+ if config.Disabled {
+ return
+ }
+
+ for _, cmd := range config.AfterBuild {
+ tbn := config.thingBenchName(cmd)
+ f, err := os.OpenFile(tbn, os.O_WRONLY|os.O_APPEND, os.ModePerm)
+ if err != nil {
+ fmt.Printf("There was an error opening %s for append, error %v\n", tbn, err)
+ continue
+ }
+
+ if !strings.ContainsAny(cmd, "/") {
+ cmd = cwd + "/" + cmd
+ }
+ if b.Disabled {
+ continue
+ }
+ testBinaryName := config.benchName(b)
+ c := exec.Command(cmd, testBinDir+"/"+testBinaryName, b.Name)
+
+ c.Env = defaultEnv
+ if !b.NotSandboxed {
+ c.Env = replaceEnv(c.Env, "GOOS", "linux")
+ }
+ // Match the build environment here.
+ c.Env = replaceEnvs(c.Env, b.GcEnv)
+ c.Env = replaceEnvs(c.Env, config.GcEnv)
+
+ if verbose > 0 {
+ fmt.Println(asCommandLine(cwd, c))
+ }
+ output, err := c.CombinedOutput()
+ if verbose > 0 || err != nil {
+ fmt.Println(string(output))
+ } else {
+ fmt.Print(".")
+ }
+ if err != nil {
+ fmt.Printf("Error running %s\n", cmd)
+ continue
+ }
+ f.Write(output)
+ f.Sync()
+ f.Close()
+ }
+}
+
+func (config *Configuration) compileOne(bench *Benchmark, cwd string, count int) string {
+ root := config.rootCopy
+ gocmd := config.goCommandCopy()
+ gopath := cwd + "/gopath"
+
+ if explicitAll != 1 { // clear cache unless "-a[=1]" which requests -a on compilation.
+ cmd := exec.Command(gocmd, "clean", "-cache")
+ cmd.Env = defaultEnv
+ if !bench.NotSandboxed {
+ cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
+ }
+ if root != "" {
+ cmd.Env = replaceEnv(cmd.Env, "GOROOT", root)
+ }
+ cmd.Env = replaceEnvs(cmd.Env, bench.GcEnv)
+ cmd.Env = replaceEnvs(cmd.Env, config.GcEnv)
+ cmd.Dir = gopath // Only want the cache-cleaning effect, not the binary-deleting effect. It's okay to clean gopath.
+ s, _ := config.runBinary("", cmd, true)
+ if s != "" {
+ fmt.Println("Error running go clean -cache, ", s)
+ }
+ }
+
+ // Prefix with time for build benchmarking:
+ cmd := exec.Command("/usr/bin/time", "-p", gocmd, "test", "-vet=off", "-c")
+ cmd.Args = append(cmd.Args, bench.BuildFlags...)
+ // Do not normally need -a because cache was emptied first and std was -a installed with these flags.
+ // But for -a=1, do it anyway
+ if explicitAll == 1 {
+ cmd.Args = append(cmd.Args, "-a")
+ }
+ cmd.Args = append(cmd.Args, config.BuildFlags...)
+ if config.GcFlags != "" {
+ cmd.Args = append(cmd.Args, "-gcflags="+config.GcFlags)
+ }
+ cmd.Args = append(cmd.Args, ".")
+ cmd.Dir = gopath + "/src/" + bench.Repo
+ cmd.Env = defaultEnv
+ if !bench.NotSandboxed {
+ cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
+ }
+ if root != "" {
+ cmd.Env = replaceEnv(cmd.Env, "GOROOT", root)
+ }
+ cmd.Env = replaceEnvs(cmd.Env, bench.GcEnv)
+ cmd.Env = replaceEnvs(cmd.Env, config.GcEnv)
+
+ if verbose > 0 {
+ fmt.Println(asCommandLine(cwd, cmd))
+ } else {
+ fmt.Print(".")
+ }
+
+ defer cleanup(gopath)
+
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ s := ""
+ switch e := err.(type) {
+ case *exec.ExitError:
+ s = fmt.Sprintf("There was an error running 'go test', output = %s", output)
+ default:
+ s = fmt.Sprintf("There was an error running 'go test', output = %s, error = %v", output, e)
+ }
+ fmt.Println(s + "DISABLING benchmark " + bench.Name)
+ bench.Disabled = true // if it won't compile, it won't run, either.
+ return s + "(" + bench.Name + ")\n"
+ }
+ soutput := string(output)
+ // Capture times from the end of the output.
+ rbt := extractTime(soutput, "real")
+ ubt := extractTime(soutput, "user")
+ sbt := extractTime(soutput, "sys")
+ config.buildStats = append(config.buildStats,
+ BenchStat{Name: bench.Name, RealTime: rbt, UserTime: ubt, SysTime: sbt})
+
+ // Report and record build stats to testbin
+
+ buf := new(bytes.Buffer)
+ configGoArch := getenv(config.GcEnv, "GOARCH")
+ if configGoArch != runtime.GOARCH && configGoArch != "" {
+ s := fmt.Sprintf("goarch: %s-%s\n", runtime.GOARCH, configGoArch)
+ if verbose > 0 {
+ fmt.Print(s)
+ }
+ buf.WriteString(s)
+ }
+ s := fmt.Sprintf("Benchmark%s 1 %d build-real-ns/op %d build-user-ns/op %d build-sys-ns/op\n",
+ strings.Title(bench.Name), rbt, ubt, sbt)
+ if verbose > 0 {
+ fmt.Print(s)
+ }
+ buf.WriteString(s)
+ f, err := os.OpenFile(config.buildBenchName(), os.O_WRONLY|os.O_APPEND, os.ModePerm)
+ if err != nil {
+ fmt.Printf("There was an error opening %s for append, error %v\n", config.buildBenchName(), err)
+ cleanup(gopath)
+ os.Exit(2)
+ }
+ f.Write(buf.Bytes())
+ f.Sync()
+ f.Close()
+
+ // Move generated binary to well-known place.
+ from := cmd.Dir + "/" + bench.testBinaryName()
+ to := testBinDir + "/" + config.benchName(bench)
+ err = os.Rename(from, to)
+ if err != nil {
+ fmt.Printf("There was an error renaming %s to %s, %v\n", from, to, err)
+ cleanup(gopath)
+ os.Exit(1)
+ }
+ // Trim /usr/bin/time info from soutput, it's ugly
+ if verbose > 0 {
+ fmt.Println("mv " + from + " " + to + "")
+ i := strings.LastIndex(soutput, "real")
+ if i >= 0 {
+ soutput = soutput[:i]
+ }
+ fmt.Print(soutput)
+ }
+
+ // Do this here before any cleanup.
+ if count == 0 {
+ config.runOtherBenchmarks(bench, cwd)
+ }
+
+ return ""
+}
+
+// runBinary runs cmd and displays the output.
+// If the command returns an error, returns an error string.
+func (c *Configuration) runBinary(cwd string, cmd *exec.Cmd, printWorkingDot bool) (string, int) {
+ line := asCommandLine(cwd, cmd)
+ if verbose > 0 {
+ fmt.Println(line)
+ } else {
+ if printWorkingDot {
+ fmt.Print(".")
+ }
+ }
+
+ rc := 0
+
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return fmt.Sprintf("Error [stdoutpipe] running '%s', %v", line, err), rc
+ }
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ return fmt.Sprintf("Error [stderrpipe] running '%s', %v", line, err), rc
+ }
+ err = cmd.Start()
+ if err != nil {
+ return fmt.Sprintf("Error [command start] running '%s', %v", line, err), rc
+ }
+
+ var mu = &sync.Mutex{}
+
+ f := func(r *bufio.Reader, done chan error) {
+ for {
+ bytes, err := r.ReadBytes('\n')
+ n := len(bytes)
+ if n > 0 {
+ mu.Lock()
+ nw, err := c.benchWriter.Write(bytes[0:n])
+ if err != nil {
+ fmt.Printf("Error writing, err = %v, nwritten = %d, nrequested = %d\n", err, nw, n)
+ }
+ c.benchWriter.Sync()
+ fmt.Print(string(bytes[0:n]))
+ mu.Unlock()
+ }
+ if err == io.EOF || n == 0 {
+ break
+ }
+ if err != nil {
+ done <- err
+ return
+ }
+ }
+ done <- nil
+ }
+
+ doneS := make(chan error)
+ doneE := make(chan error)
+
+ go f(bufio.NewReader(stdout), doneS)
+ go f(bufio.NewReader(stderr), doneE)
+
+ errS := <-doneS
+ errE := <-doneE
+
+ err = cmd.Wait()
+ rc = cmd.ProcessState.ExitCode()
+
+ if err != nil {
+ switch e := err.(type) {
+ case *exec.ExitError:
+ return fmt.Sprintf("Error running '%s', stderr = %s, rc = %d", line, e.Stderr, rc), rc
+ default:
+ return fmt.Sprintf("Error running '%s', %v, rc = %d", line, e, rc), rc
+
+ }
+ }
+ if errS != nil {
+ return fmt.Sprintf("Error [read stdout] running '%s', %v, rc = %d", line, errS, rc), rc
+ }
+ if errE != nil {
+ return fmt.Sprintf("Error [read stderr] running '%s', %v, rc = %d", line, errE, rc), rc
+ }
+ return "", rc
+}
+
+// extractTime extracts a time (from /usr/bin/time -p) based on the tag
+// and returns the time converted to nanoseconds. Missing times and bad
+// data result in NaN.
+func extractTime(output, label string) int64 {
+ // find tag in first column
+ li := strings.LastIndex(output, label)
+ if li < 0 {
+ return -1
+ }
+ output = output[li+len(label):]
+ // lose intervening white space
+ li = strings.IndexAny(output, "0123456789-.eEdD")
+ if li < 0 {
+ return -1
+ }
+ output = output[li:]
+ li = strings.IndexAny(output, "\n\r\t ")
+ if li >= 0 { // failing to find EOL is a special case of done.
+ output = output[:li]
+ }
+ x, err := strconv.ParseFloat(output, 64)
+ if err != nil {
+ return -1
+ }
+ return int64(x * 1000 * 1000 * 1000)
+}