blob: 4e1628ac1e3749ef60081d56d20e81b583c41a59 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package common
import (
"context"
"fmt"
"os"
"path/filepath"
"go.skia.org/infra/go/gcs"
"go.skia.org/infra/go/now"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/util"
"go.skia.org/infra/task_driver/go/lib/os_steps"
"go.skia.org/infra/task_driver/go/td"
)
// PerfGCSBucketName is the name of the perf.skia.org GCS bucket.
const PerfGCSBucketName = "skia-perf"
// The name of the file produced by the C++ test that we should upload to Perf.
const resultsJSON = "results.json"
// BenchmarkInfo contains information about the CI task under which a benchmark is executed.
type BenchmarkInfo struct {
GitCommit string
TaskName string
TaskID string
ChangelistID string
PatchsetOrder string
}
// ComputeBenchmarkTestRunnerCLIFlags returns the command-line flags that should be passed to the
// benchmark test runner.
func ComputeBenchmarkTestRunnerCLIFlags(benchmarkInfo BenchmarkInfo) []string {
flags := []string{"--gitHash", benchmarkInfo.GitCommit}
if benchmarkInfo.ChangelistID != "" && benchmarkInfo.PatchsetOrder != "" {
flags = append(flags,
"--issue",
benchmarkInfo.ChangelistID,
"--patchset",
benchmarkInfo.PatchsetOrder)
}
flags = append(flags,
"--links",
"task",
"https://task-scheduler.skia.org/task/"+benchmarkInfo.TaskID)
if benchmarkInfo.ChangelistID != "" && benchmarkInfo.PatchsetOrder != "" {
flags = append(flags,
"changelist",
fmt.Sprintf("https://skia-review.googlesource.com/c/skia/+/%s/%s", benchmarkInfo.ChangelistID, benchmarkInfo.PatchsetOrder))
}
return flags
}
func UploadToPerf(ctx context.Context, gcsClient gcs.GCSClient, benchmarkInfo BenchmarkInfo, outputsZIPOrDir string) error {
// Fail loudly if the outputs ZIP or directory does not exist. All benchmark tests should produce
// a results.json file.
fileInfo, err := os.Stat(outputsZIPOrDir)
if err != nil {
return skerr.Wrap(err)
}
// If the undeclared outputs ZIP file or directory is a ZIP file, extract it.
outputsDir := ""
if fileInfo.IsDir() {
outputsDir = outputsZIPOrDir
} else {
var err error
outputsDir, err = ExtractOutputsZip(ctx, outputsZIPOrDir)
if err != nil {
return skerr.Wrap(err)
}
defer util.RemoveAll(outputsDir)
}
// Get path to the results.json file that we will upload to Perf.
resultsJSONPath := filepath.Join(outputsDir, resultsJSON)
fileInfo, err = os_steps.Stat(ctx, resultsJSONPath)
if err != nil {
return skerr.Wrapf(err, "while stating %q", resultsJSONPath)
}
if fileInfo.IsDir() {
return skerr.Fmt("file %q is a directory", resultsJSONPath)
}
resultsJSONBytes, err := os_steps.ReadFile(ctx, resultsJSONPath)
if err != nil {
return skerr.Wrap(err)
}
gcsPath := computePerfJSONFileGCSPath(ctx, benchmarkInfo)
gcsURL := fmt.Sprintf("gs://%s/%s", gcsClient.Bucket(), gcsPath)
return td.Do(ctx, td.Props(fmt.Sprintf("Upload %s", gcsURL)), func(ctx context.Context) error {
if err := gcsClient.SetFileContents(ctx, gcsPath, gcs.FILE_WRITE_OPTS_TEXT, resultsJSONBytes); err != nil {
return skerr.Wrapf(err, "while uploading %q", gcsURL)
}
return nil
})
}
// Based on
// https://skia.googlesource.com/skia/+/5096ed4deb06171700c74273b685713bec0ae597/infra/bots/task_drivers/codesize/codesize.go#504
// and
// https://skia.googlesource.com/skia/+/5096ed4deb06171700c74273b685713bec0ae597/infra/bots/task_drivers/codesize/codesize.go#517.
func computePerfJSONFileGCSPath(ctx context.Context, benchmarkInfo BenchmarkInfo) string {
timePrefix := now.Now(ctx).UTC().Format("2006/01/02/15") // YYYY/MM/DD/HH.
// Example: nano-json-v1/2022/01/31/01/033ccea12c0949d0f712471bfcb4ed6daf69aaff/BazelTest-Foo-Bar/results_CkPp9ElAaEXyYWNHpXHU.json.
return fmt.Sprintf("nano-json-v1/%s/%s/%s/results_%s.json", timePrefix, benchmarkInfo.GitCommit, benchmarkInfo.TaskName, benchmarkInfo.TaskID)
}