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)
+}