cmd/bent: cleanup, add helpers, avoid possible slice-aliasing errors.

Change-Id: I1d3fefe39898bf0913692c667318d111de7429b8
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/432362
Reviewed-by: Michael Pratt <mpratt@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/bent/bent.go b/cmd/bent/bent.go
index 51645d2..fb411c1 100644
--- a/cmd/bent/bent.go
+++ b/cmd/bent/bent.go
@@ -108,6 +108,11 @@
 
 var defaultEnv []string
 
+// DefaultEnv returns a fresh copy of defaultEnv
+func DefaultEnv() []string {
+	return append([]string{}, defaultEnv...)
+}
+
 type pair struct {
 	b, c int
 }
@@ -116,7 +121,7 @@
 }
 
 // To disambiguate repeated test runs in the same directory.
-var runstamp = strings.Replace(strings.Replace(time.Now().Format("2006-01-02T15:04:05"), "-", "", -1), ":", "", -1)
+var runstamp = strings.Replace(strings.Replace(time.Now().UTC().Format("2006-01-02T15:04:05"), "-", "", -1), ":", "", -1)
 
 func cleanup(gopath string) {
 	bin := path.Join(gopath, "bin")
@@ -286,7 +291,6 @@
 		updateFlags(&b.ExtraFiles, s.ExtraFiles)
 		updateFlags(&b.BuildFlags, s.BuildFlags)
 		updateFlags(&b.GcEnv, s.GcEnv)
-
 	}
 
 	var moreArgs []string
@@ -544,7 +548,7 @@
 				os.Remove(goDotMod) // always want a fresh go.mod
 			}
 			cmd := exec.Command("go", "mod", "init", "build")
-			cmd.Env = defaultEnv
+			cmd.Env = DefaultEnv()
 			cmd.Dir = bench.BuildDir
 
 			if verbose > 0 {
@@ -560,12 +564,13 @@
 			}
 
 			cmd = exec.Command("go", "get", "-d", "-t", "-v", bench.Repo+bench.Version)
-			cmd.Env = replaceEnvs(defaultEnv, bench.GcEnv)
+			cmd.Env = DefaultEnv()
 			cmd.Dir = bench.BuildDir
 
 			if !bench.NotSandboxed { // Do this so that OS-dependent dependencies are done correctly.
 				cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
 			}
+			cmd.Env = replaceEnvs(cmd.Env, sliceExpandEnv(bench.GcEnv, cmd.Env))
 			if verbose > 0 {
 				fmt.Println(asCommandLine(dirs.wd, cmd))
 			} else {
@@ -653,19 +658,20 @@
 					return // The alternate OS is linux
 				}
 				cmd := exec.Command(gocmd, "install", "-a")
-				cmd.Args = append(cmd.Args, config.BuildFlags...)
-				if config.GcFlags != "" {
-					cmd.Args = append(cmd.Args, "-gcflags="+config.GcFlags)
-				}
-				cmd.Args = append(cmd.Args, "std")
-				cmd.Env = defaultEnv
+				cmd.Env = DefaultEnv()
 				if withAltOS {
 					cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
 				}
 				if rootCopy != "" {
 					cmd.Env = replaceEnv(cmd.Env, "GOROOT", rootCopy)
 				}
-				cmd.Env = replaceEnvs(cmd.Env, config.GcEnv)
+				cmd.Env = replaceEnvs(cmd.Env, sliceExpandEnv(config.GcEnv, cmd.Env))
+
+				cmd.Args = append(cmd.Args, sliceExpandEnv(config.BuildFlags, cmd.Env)...)
+				if config.GcFlags != "" {
+					cmd.Args = append(cmd.Args, "-gcflags="+expandEnv(config.GcFlags, cmd.Env))
+				}
+				cmd.Args = append(cmd.Args, "std")
 
 				s, _ := config.runBinary("", cmd, true)
 				if s != "" {
@@ -840,7 +846,7 @@
 		// Capture output of "go list -f {{.Dir}} $PKG"
 
 		cmd := exec.Command("go", "list", "-f", "{{.Dir}}", bench.Repo)
-		cmd.Env = defaultEnv
+		cmd.Env = DefaultEnv()
 		cmd.Dir = bench.BuildDir
 
 		if verbose > 0 {
@@ -1005,7 +1011,7 @@
 					cmd.Args = append(cmd.Args, "-test.bench="+b.Benchmarks)
 
 					cmd.Dir = b.RunDir
-					cmd.Env = defaultEnv
+					cmd.Env = DefaultEnv()
 					if root != "" {
 						cmd.Env = replaceEnv(cmd.Env, "GOROOT", root)
 					}
@@ -1311,6 +1317,17 @@
 	return env
 }
 
+func removeEmptyEnvs(env []string) []string {
+	var newenv []string
+	for _, e := range env {
+		if i := strings.Index(e, "="); i == -1 || i == len(e)-1 {
+			continue
+		}
+		newenv = append(newenv, e)
+	}
+	return newenv
+}
+
 // ifMissingAddEnv returns a new environment derived from env
 // by adding ev=evv if env does not define env.
 func ifMissingAddEnv(env []string, ev string, evv string) []string {
diff --git a/cmd/bent/configuration.go b/cmd/bent/configuration.go
index 00b745d..f2f2207 100644
--- a/cmd/bent/configuration.go
+++ b/cmd/bent/configuration.go
@@ -153,15 +153,15 @@
 
 	if explicitAll != 1 { // clear cache unless "-a[=1]" which requests -a on compilation.
 		cmd := exec.Command(gocmd, "clean", "-cache")
-		cmd.Env = defaultEnv
+		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.Env = replaceEnvs(cmd.Env, sliceExpandEnv(bench.GcEnv, cmd.Env))
+		cmd.Env = replaceEnvs(cmd.Env, sliceExpandEnv(config.GcEnv, cmd.Env))
 		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 != "" {
@@ -172,7 +172,8 @@
 	cmd := exec.Command(gocmd, "test", "-vet=off", "-c")
 	compileTo := path.Join(dirs.wd, dirs.testBinDir, config.benchName(bench))
 
-	cmd.Env = defaultEnv
+	cmd.Env = DefaultEnv()
+
 	if !bench.NotSandboxed {
 		cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
 	}
@@ -241,15 +242,16 @@
 	// Report and record build stats to testbin
 
 	buf := new(bytes.Buffer)
-
 	var s string
 	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("goarch: %s-%s\n", runtime.GOARCH, configGoArch)
+	} else {
+		s = fmt.Sprintf("goarch: %s\n", runtime.GOARCH)
 	}
+	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), bs.RealTime.Nanoseconds(), bs.UserTime.Nanoseconds(), bs.SysTime.Nanoseconds())
 	if verbose > 0 {