| // Copyright 2023 Google LLC |
| // |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This task driver runs a single Bazel test target representing one or more benchmarks, or a Bazel |
| // test suite consisting of multiple such targets, using the provided config (which is assumed to |
| // be in //bazel/buildrc). Benchmark results are uploaded to a GCS bucket for ingestion by Perf. |
| // This task driver handles any setup steps needed to run Bazel on our CI machines before running |
| // the task, such as setting up logs and the Bazel cache. |
| |
| package main |
| |
| import ( |
| "context" |
| "flag" |
| "fmt" |
| "path/filepath" |
| |
| "cloud.google.com/go/storage" |
| "go.skia.org/infra/go/auth" |
| sk_exec "go.skia.org/infra/go/exec" |
| "go.skia.org/infra/go/gcs" |
| "go.skia.org/infra/go/gcs/gcsclient" |
| "go.skia.org/infra/go/skerr" |
| "go.skia.org/infra/task_driver/go/lib/auth_steps" |
| "go.skia.org/infra/task_driver/go/lib/bazel" |
| "go.skia.org/infra/task_driver/go/lib/os_steps" |
| "go.skia.org/infra/task_driver/go/td" |
| "go.skia.org/skia/infra/bots/task_drivers/common" |
| "google.golang.org/api/option" |
| ) |
| |
| var ( |
| // Required properties for this task. |
| // |
| // We want the cache to be on a bigger disk than default. The root disk, where the home directory |
| // (and default Bazel cache) lives, is only 15 GB on our GCE VMs. |
| projectId = flag.String("project_id", "", "ID of the Google Cloud project.") |
| taskID = flag.String("task_id", "", "ID of this task.") |
| taskName = flag.String("task_name", "", "Name of the task.") |
| workdir = flag.String("workdir", ".", "Working directory.") |
| |
| gitCommit = flag.String("git_commit", "", "The git hash to which the data should be associated.") |
| changelistID = flag.String("changelist_id", "", "Should be non-empty only when run on the CQ.") |
| patchsetOrderStr = flag.String("patchset_order", "", "Should be non-zero only when run on the CQ.") |
| |
| // Optional flags. |
| local = flag.Bool("local", false, "True if running locally (as opposed to on the CI/CQ)") |
| output = flag.String("o", "", "If provided, dump a JSON blob of step data to the given file. Prints to stdout if '-' is given.") |
| ) |
| |
| func main() { |
| bazelFlags := common.MakeBazelFlags(common.MakeBazelFlagsOpts{ |
| Label: true, |
| Config: true, |
| DeviceSpecificConfig: true, |
| }) |
| |
| // StartRun calls flag.Parse(). |
| ctx := td.StartRun(projectId, taskID, taskName, output, local) |
| defer td.EndRun(ctx) |
| |
| bazelFlags.Validate(ctx) |
| |
| wd, err := os_steps.Abs(ctx, *workdir) |
| if err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| opts := bazel.BazelOptions{ |
| CachePath: *bazelFlags.CacheDir, |
| } |
| if err := bazel.EnsureBazelRCFile(ctx, opts); err != nil { |
| td.Fatal(ctx, err) |
| } |
| |
| // Make an HTTP client with the required permissions to upload to the perf.skia.org GCS bucket. |
| httpClient, _, err := auth_steps.InitHttpClient(ctx, *local, auth.ScopeReadWrite, auth.ScopeUserinfoEmail) |
| if err != nil { |
| td.Fatal(ctx, skerr.Wrap(err)) |
| } |
| |
| // Make a GCS client to to upload to the perf.skia.org GCS bucket. |
| store, err := storage.NewClient(ctx, option.WithHTTPClient(httpClient)) |
| if err != nil { |
| td.Fatal(ctx, skerr.Wrap(err)) |
| } |
| gcsClient := gcsclient.New(store, common.PerfGCSBucketName) |
| |
| if err := run(ctx, *bazelFlags.CacheDir, taskDriverArgs{ |
| BenchmarkInfo: common.BenchmarkInfo{ |
| GitCommit: *gitCommit, |
| TaskName: *taskName, |
| TaskID: *taskID, |
| ChangelistID: *changelistID, |
| PatchsetOrder: *patchsetOrderStr, |
| }, |
| checkoutDir: filepath.Join(wd, "skia"), |
| bazelLabel: *bazelFlags.Label, |
| bazelConfig: *bazelFlags.Config, |
| deviceSpecificBazelConfig: *bazelFlags.DeviceSpecificConfig, |
| }, gcsClient); err != nil { |
| td.Fatal(ctx, err) |
| } |
| } |
| |
| // taskDriverArgs gathers the inputs to this task driver, and decouples the task driver's |
| // entry-point function from the command line flags, which facilitates writing unit tests. |
| type taskDriverArgs struct { |
| common.BenchmarkInfo |
| checkoutDir string |
| bazelLabel string |
| bazelConfig string |
| deviceSpecificBazelConfig string |
| } |
| |
| // run is the entrypoint of this task driver. |
| func run(ctx context.Context, bazelCacheDir string, tdArgs taskDriverArgs, gcsClient gcs.GCSClient) error { |
| outputsZipPath, err := common.ValidateLabelAndReturnOutputsZipPath(tdArgs.checkoutDir, tdArgs.bazelLabel) |
| if err != nil { |
| return skerr.Wrap(err) |
| } |
| |
| testArgs := common.ComputeBenchmarkTestRunnerCLIFlags(tdArgs.BenchmarkInfo) |
| if err := bazelTest(ctx, tdArgs.checkoutDir, tdArgs.bazelLabel, tdArgs.bazelConfig, tdArgs.deviceSpecificBazelConfig, testArgs); err != nil { |
| return skerr.Wrap(err) |
| } |
| |
| if err := common.UploadToPerf(ctx, gcsClient, tdArgs.BenchmarkInfo, outputsZipPath); err != nil { |
| return skerr.Wrap(err) |
| } |
| |
| if !*local { |
| if err := common.BazelCleanIfLowDiskSpace(ctx, bazelCacheDir, tdArgs.checkoutDir, "bazelisk"); err != nil { |
| return skerr.Wrap(err) |
| } |
| } |
| |
| return nil |
| } |
| |
| // bazelTest runs the test referenced by the given fully qualified Bazel label under the given |
| // config. |
| func bazelTest(ctx context.Context, checkoutDir, label, config, deviceSpecificConfig string, testArgs []string) error { |
| args := []string{"test", |
| label, |
| "--config=" + config, // Should be defined in //bazel/buildrc. |
| "--config=" + deviceSpecificConfig, // Should be defined in //bazel/devicesrc. |
| "--test_output=errors", |
| "--jobs=100", |
| } |
| for _, testArg := range testArgs { |
| args = append(args, "--test_arg="+testArg) |
| } |
| |
| return td.Do(ctx, td.Props(fmt.Sprintf("Test %s with config %s", label, config)), func(ctx context.Context) error { |
| runCmd := &sk_exec.Command{ |
| Name: "bazelisk", |
| Args: args, |
| InheritEnv: true, // Makes sure bazelisk is on PATH. |
| Dir: checkoutDir, |
| LogStdout: true, |
| LogStderr: true, |
| } |
| _, err := sk_exec.RunCommand(ctx, runCmd) |
| if err != nil { |
| return err |
| } |
| return nil |
| }) |
| } |