1// Copyright 2023 Google LLC 2// 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5 6package common 7 8import ( 9 "context" 10 "fmt" 11 "os" 12 "path/filepath" 13 14 "go.skia.org/infra/go/gcs" 15 "go.skia.org/infra/go/now" 16 "go.skia.org/infra/go/skerr" 17 "go.skia.org/infra/go/util" 18 "go.skia.org/infra/task_driver/go/lib/os_steps" 19 "go.skia.org/infra/task_driver/go/td" 20) 21 22// PerfGCSBucketName is the name of the perf.skia.org GCS bucket. 23const PerfGCSBucketName = "skia-perf" 24 25// The name of the file produced by the C++ test that we should upload to Perf. 26const resultsJSON = "results.json" 27 28// BenchmarkInfo contains information about the CI task under which a benchmark is executed. 29type BenchmarkInfo struct { 30 GitCommit string 31 TaskName string 32 TaskID string 33 ChangelistID string 34 PatchsetOrder string 35} 36 37// ComputeBenchmarkTestRunnerCLIFlags returns the command-line flags that should be passed to the 38// benchmark test runner. 39func ComputeBenchmarkTestRunnerCLIFlags(benchmarkInfo BenchmarkInfo) []string { 40 flags := []string{"--gitHash", benchmarkInfo.GitCommit} 41 42 if benchmarkInfo.ChangelistID != "" && benchmarkInfo.PatchsetOrder != "" { 43 flags = append(flags, 44 "--issue", 45 benchmarkInfo.ChangelistID, 46 "--patchset", 47 benchmarkInfo.PatchsetOrder) 48 } 49 50 flags = append(flags, 51 "--links", 52 "task", 53 "https://task-scheduler.skia.org/task/"+benchmarkInfo.TaskID) 54 if benchmarkInfo.ChangelistID != "" && benchmarkInfo.PatchsetOrder != "" { 55 flags = append(flags, 56 "changelist", 57 fmt.Sprintf("https://skia-review.googlesource.com/c/skia/+/%s/%s", benchmarkInfo.ChangelistID, benchmarkInfo.PatchsetOrder)) 58 } 59 60 return flags 61} 62 63func UploadToPerf(ctx context.Context, gcsClient gcs.GCSClient, benchmarkInfo BenchmarkInfo, outputsZIPOrDir string) error { 64 // Fail loudly if the outputs ZIP or directory does not exist. All benchmark tests should produce 65 // a results.json file. 66 fileInfo, err := os.Stat(outputsZIPOrDir) 67 if err != nil { 68 return skerr.Wrap(err) 69 } 70 71 // If the undeclared outputs ZIP file or directory is a ZIP file, extract it. 72 outputsDir := "" 73 if fileInfo.IsDir() { 74 outputsDir = outputsZIPOrDir 75 } else { 76 var err error 77 outputsDir, err = ExtractOutputsZip(ctx, outputsZIPOrDir) 78 if err != nil { 79 return skerr.Wrap(err) 80 } 81 defer util.RemoveAll(outputsDir) 82 } 83 84 // Get path to the results.json file that we will upload to Perf. 85 resultsJSONPath := filepath.Join(outputsDir, resultsJSON) 86 fileInfo, err = os_steps.Stat(ctx, resultsJSONPath) 87 if err != nil { 88 return skerr.Wrapf(err, "while stating %q", resultsJSONPath) 89 } 90 if fileInfo.IsDir() { 91 return skerr.Fmt("file %q is a directory", resultsJSONPath) 92 } 93 94 resultsJSONBytes, err := os_steps.ReadFile(ctx, resultsJSONPath) 95 if err != nil { 96 return skerr.Wrap(err) 97 } 98 99 gcsPath := computePerfJSONFileGCSPath(ctx, benchmarkInfo) 100 gcsURL := fmt.Sprintf("gs://%s/%s", gcsClient.Bucket(), gcsPath) 101 return td.Do(ctx, td.Props(fmt.Sprintf("Upload %s", gcsURL)), func(ctx context.Context) error { 102 if err := gcsClient.SetFileContents(ctx, gcsPath, gcs.FILE_WRITE_OPTS_TEXT, resultsJSONBytes); err != nil { 103 return skerr.Wrapf(err, "while uploading %q", gcsURL) 104 } 105 return nil 106 }) 107} 108 109// Based on 110// https://skia.googlesource.com/skia/+/5096ed4deb06171700c74273b685713bec0ae597/infra/bots/task_drivers/codesize/codesize.go#504 111// and 112// https://skia.googlesource.com/skia/+/5096ed4deb06171700c74273b685713bec0ae597/infra/bots/task_drivers/codesize/codesize.go#517. 113func computePerfJSONFileGCSPath(ctx context.Context, benchmarkInfo BenchmarkInfo) string { 114 timePrefix := now.Now(ctx).UTC().Format("2006/01/02/15") // YYYY/MM/DD/HH. 115 // Example: nano-json-v1/2022/01/31/01/033ccea12c0949d0f712471bfcb4ed6daf69aaff/BazelTest-Foo-Bar/results_CkPp9ElAaEXyYWNHpXHU.json. 116 return fmt.Sprintf("nano-json-v1/%s/%s/%s/results_%s.json", timePrefix, benchmarkInfo.GitCommit, benchmarkInfo.TaskName, benchmarkInfo.TaskID) 117} 118