internal/govulncheck,buildbinary: measure build time

Adds measuring of the time it takes to build a binary for
govulncheck_compare. This will be followed by a CL that introduces
binary build time to the bigquery schema.

Change-Id: Ie62f685c2abd5666d606d07e6cfa2812f9bf6a0b
Reviewed-on: https://go-review.googlesource.com/c/pkgsite-metrics/+/518495
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Maceo Thompson <maceothompson@google.com>
diff --git a/cmd/govulncheck_compare/govulncheck_compare.go b/cmd/govulncheck_compare/govulncheck_compare.go
index 52214c5..7660590 100644
--- a/cmd/govulncheck_compare/govulncheck_compare.go
+++ b/cmd/govulncheck_compare/govulncheck_compare.go
@@ -59,7 +59,7 @@
 
 	for _, binary := range binaries {
 		pair := govulncheck.ComparePair{
-			BinaryResults: govulncheck.SandboxResponse{Stats: govulncheck.ScanStats{}},
+			BinaryResults: govulncheck.SandboxResponse{Stats: govulncheck.ScanStats{BuildTime: binary.BuildTime}},
 			SourceResults: govulncheck.SandboxResponse{Stats: govulncheck.ScanStats{}},
 		}
 
diff --git a/internal/buildbinary/bin.go b/internal/buildbinary/bin.go
index 892c232..b21d9af 100644
--- a/internal/buildbinary/bin.go
+++ b/internal/buildbinary/bin.go
@@ -9,6 +9,7 @@
 	"os/exec"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"golang.org/x/pkgsite-metrics/internal/derrors"
 )
@@ -16,6 +17,7 @@
 type BinaryInfo struct {
 	BinaryPath string
 	ImportPath string
+	BuildTime  time.Duration
 }
 
 // FindAndBuildBinaries finds and builds all possible binaries from a given module.
@@ -27,27 +29,30 @@
 	}
 
 	for i, target := range buildTargets {
-		path, err := runBuild(modulePath, target, i)
+		path, buildTime, err := runBuild(modulePath, target, i)
 		if err != nil {
 			return nil, err
 		}
 		binaries = append(binaries, &BinaryInfo{
 			BinaryPath: path,
 			ImportPath: target,
+			BuildTime:  buildTime,
 		})
 	}
 	return binaries, nil
 }
 
 // runBuild takes a given module and import path and attempts to build a binary
-func runBuild(modulePath, importPath string, i int) (binaryPath string, err error) {
+func runBuild(modulePath, importPath string, i int) (binaryPath string, buildTime time.Duration, err error) {
 	binName := fmt.Sprintf("bin%d", i)
 	cmd := exec.Command("go", "build", "-C", modulePath, "-o", binName, importPath)
+	start := time.Now()
 	if err = cmd.Run(); err != nil {
-		return "", err
+		return "", 0, err
 	}
+	buildTime = time.Since(start)
 	binaryPath = filepath.Join(modulePath, binName)
-	return binaryPath, nil
+	return binaryPath, buildTime, nil
 }
 
 // findBinaries finds all packages that compile to binaries in a given directory
diff --git a/internal/buildbinary/bin_test.go b/internal/buildbinary/bin_test.go
index 1d9f8b3..1249755 100644
--- a/internal/buildbinary/bin_test.go
+++ b/internal/buildbinary/bin_test.go
@@ -83,7 +83,7 @@
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := runBuild(tt.modulePath, tt.importPath, 1)
+			got, _, err := runBuild(tt.modulePath, tt.importPath, 1)
 			defer os.Remove(got)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("runBuild() error = %v, wantErr %v", err, tt.wantErr)
diff --git a/internal/govulncheck/govulncheck.go b/internal/govulncheck/govulncheck.go
index 142a054..9a95278 100644
--- a/internal/govulncheck/govulncheck.go
+++ b/internal/govulncheck/govulncheck.go
@@ -247,6 +247,10 @@
 	ScanSeconds float64
 	// ScanMemory is the peak (heap) memory used by govulncheck, in kb.
 	ScanMemory uint64
+	// BuildTime is the amount of time it takes to build a given binary
+	// *BEFORE* scanning it with govulncheck.
+	// This is only used in COMPARE - BINARY mode
+	BuildTime time.Duration
 }
 
 // SandboxResponse contains the raw govulncheck result