cmd/bent: add pgo benchmarking scenario

How to use.

The configuration for PGO measurements is located in the configurations-pgo.toml
file. This file contains two configurations, and you will need both of them.
The first configuration "pgo-generate" is enabled by default. It has a new
field "PgoGen", than contains a name of a sub-directory, where the .prof files
will be put. The second configuration "pgo-use" is disabled by default. This
configuration is needed to use the .prof files that were created during the
first phase. It has a new field "PgoUse", with a sub-directory path to .prof
files.

The usages scenario is the following:

1. Run bent with the configuration "pgo-generate" to create .prof files
2. Disable the configuration "pgo-generate" and enable "pgo-use"
3. Run bent with this configuration. This will be pgo-guided run with
   better performance

Notes: we need two new fields, because PgoGen do not add any option,
but is enables environment variables for cpuprofile script. The PgoUse
option adds the -pgo=<path to prof file> to the build line. Probably we
could avoid a step with enabling/disabling configuration for our comfort,
but I do not know how to set the particular order of configuration launches.

Change-Id: I04adf494a01e62b491cc9afce7eed49b19f9fae5
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/565357
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/cmd/bent/bent.go b/cmd/bent/bent.go
index f15bc94..b2be732 100644
--- a/cmd/bent/bent.go
+++ b/cmd/bent/bent.go
@@ -95,7 +95,7 @@
 var copyConfigs = []string{
 	"benchmarks-all.toml", "benchmarks-50.toml", "benchmarks-gc.toml", "benchmarks-gcplus.toml", "benchmarks-trial.toml",
 	"configurations-sample.toml", "configurations-gollvm.toml", "configurations-cronjob.toml", "configurations-cmpjob.toml",
-	"suites.toml",
+	"configurations-pgo.toml", "suites.toml",
 }
 
 var defaultEnv []string
@@ -1002,6 +1002,11 @@
 				runEnv = append(runEnv, "BENT_I="+strconv.FormatInt(int64(i), 10))
 				runEnv = append(runEnv, "BENT_BINARY="+testBinaryName)
 
+				if config.PgoGen != "" {
+					// We want to generate pprof file for using pgo
+					config.RunWrapper = append(config.RunWrapper, "cpuprofile")
+				}
+
 				configWrapper := wrapperFor(config.RunWrapper)
 				benchWrapper := wrapperFor(b.RunWrapper)
 
@@ -1032,8 +1037,11 @@
 					if root != "" {
 						cmd.Env = replaceEnv(cmd.Env, "GOROOT", root)
 					}
-					cmd.Env = append(cmd.Env, "BENT_DIR="+dirs.wd)
 					cmd.Env = append(cmd.Env, "BENT_PROFILES="+path.Join(dirs.wd, config.thingBenchName("profiles")))
+					if config.PgoGen != "" {
+						// We want to generate pprof file for using pgo
+						cmd.Env = append(cmd.Env, "BENT_PGO="+path.Join(dirs.wd, config.PgoGen))
+					}
 
 					cmd.Env = append(cmd.Env, runEnv...)
 					cmd.Env = append(cmd.Env, sliceExpandEnv(config.RunEnv, cmd.Env)...)
@@ -1061,8 +1069,11 @@
 						cmd.Args = append(cmd.Args, "-e", e)
 					}
 
-					cmd.Args = append(cmd.Args, "-e", "BENT_DIR=/") // TODO this is not going to work well
 					cmd.Args = append(cmd.Args, "-e", "BENT_PROFILES="+path.Join(dirs.wd, config.thingBenchName("profiles")))
+					if config.PgoGen != "" {
+						// We want to generate pprof file for using pgo
+						cmd.Args = append(cmd.Args, "-e", "BENT_PGO="+path.Join(dirs.wd, config.PgoGen))
+					}
 					cmd.Args = append(cmd.Args, container)
 					cmd.Args = append(cmd.Args, wrappersAndBin...)
 					cmd.Args = append(cmd.Args, "-test.run="+b.Tests)
diff --git a/cmd/bent/configs/configurations-pgo.toml b/cmd/bent/configs/configurations-pgo.toml
new file mode 100644
index 0000000..93b9936
--- /dev/null
+++ b/cmd/bent/configs/configurations-pgo.toml
@@ -0,0 +1,11 @@
+[[Configurations]]
+  Name = "pgo-generate"
+  Root = "$HOME/work/go/"
+  PgoGen = "profiles" # Sub-directory where we put profiles
+#  Disabled = true
+
+[[Configurations]]
+  Name = "pgo-use"
+  Root = "$HOME/work/go/"
+  PgoUse = "profiles" # Sub-directory where we search profiles
+  Disabled = true
diff --git a/cmd/bent/configuration.go b/cmd/bent/configuration.go
index f65b6f9..b72dd53 100644
--- a/cmd/bent/configuration.go
+++ b/cmd/bent/configuration.go
@@ -26,6 +26,8 @@
 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
+	PgoGen      string   // Name of sub-directory to put profiles for later loading
+	PgoUse      string   // Name of sub-directory to take generated profile files
 	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
@@ -176,6 +178,12 @@
 	// Instead of cleaning the cache, specify -a; cache use changed with 1.20, which made builds take much longer.
 	cmd.Args = append(cmd.Args, "-a")
 	cmd.Args = append(cmd.Args, sliceExpandEnv(config.BuildFlags, cmd.Env)...)
+
+	if config.PgoUse != "" {
+		// We want to use pprof file for pgo
+		cmd.Args = append(cmd.Args, "-pgo="+path.Join(dirs.wd, config.PgoUse, bench.Name+".prof"))
+	}
+
 	if config.GcFlags != "" {
 		cmd.Args = append(cmd.Args, "-gcflags="+expandEnv(config.GcFlags, cmd.Env))
 	}
diff --git a/cmd/bent/scripts/cpuprofile b/cmd/bent/scripts/cpuprofile
index 111653f..e16ce41 100755
--- a/cmd/bent/scripts/cpuprofile
+++ b/cmd/bent/scripts/cpuprofile
@@ -1,7 +1,12 @@
 #!/bin/bash
 # Run args as command, but run cpuprofile and then pprof to capture test cpuprofile output
-pf="${BENT_PROFILES}/${BENT_BINARY}_${BENT_I}.prof"
-mkdir -p ${BENT_PROFILES}
+if [ -n ${BENT_PGO} ] ; then
+    pf="${BENT_PGO}/${BENT_BENCH}.prof"
+    mkdir -p ${BENT_PGO}
+else
+    pf="${BENT_PROFILES}/${BENT_BINARY}_${BENT_I}.prof"
+    mkdir -p ${BENT_PROFILES}
+fi
 "$@" -test.cpuprofile="$pf"
 echo cpuprofile in "$pf"
 if [[ x`which pprof` == x"" ]] ; then