xref: /aosp_15_r20/external/skia/infra/bots/task_drivers/common/perf_steps.go (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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