[bisection] implement benchmark interface
This creates an interface for all benchmarks to implement.
We start with telemetry, which deprecates telemetry_exp.go.
It implements waterfall enabled gtests and story tags.
All of pinpoint.go is updated to get the appropriate benchmark
and Run() accordingly.
Bug: b/318863812, b/318863812, b/321299939
Change-Id: Ie267f07f31a15df54dde1633711fc959e36b3e1b
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/814037
Reviewed-by: Hao Wu <haowoo@google.com>
Commit-Queue: Jeff Yoon <jeffyoon@google.com>
diff --git a/pinpoint/go/bot_configs/bot_configs.go b/pinpoint/go/bot_configs/bot_configs.go
index 91e2ebf..95e76f7 100644
--- a/pinpoint/go/bot_configs/bot_configs.go
+++ b/pinpoint/go/bot_configs/bot_configs.go
@@ -38,8 +38,11 @@
// Browser is the name of the Chrome browser to run
// when testing benchmarks
Browser string `json:"browser"`
- // Builder refers to the LUCI pool to build Chrome,
+ // Bucket refers to the LUCI pool to build Chrome,
// typically luci.chrome.try
+ Bucket string `json:"bucket"`
+ // Builder refers to the LUCI builder used to build Chrome,
+ // usually a compile builder.
Builder string `json:"builder"`
// Dimensions are used by Swarming to find the
// device pool to test benchmarks
@@ -50,6 +53,8 @@
// SwarmingServer is almost always
// https://chrome-swarming.appspot.com
SwarmingServer string `json:"swarming_server"`
+ // Bot is the original key used for this config.
+ Bot string
}
// GetBotConfig gets the config for a bot. Will only check internal
@@ -77,9 +82,14 @@
}
return BotConfig{}, fmt.Errorf(errMsg)
}
- if cfg.Alias != "" {
- return botConfigs[cfg.Alias], nil
+ alias := cfg.Alias
+ if alias != "" {
+ cfg = botConfigs[cfg.Alias]
+ cfg.Bot = alias
+ return cfg, nil
}
+
+ cfg.Bot = bot
return cfg, nil
}
diff --git a/pinpoint/go/bot_configs/bot_configs_test.go b/pinpoint/go/bot_configs/bot_configs_test.go
index 23428a0..0358b6c 100644
--- a/pinpoint/go/bot_configs/bot_configs_test.go
+++ b/pinpoint/go/bot_configs/bot_configs_test.go
@@ -40,12 +40,6 @@
externalOnly: false,
expectedError: true,
},
- {
- name: "check alias",
- bot: "Android Go",
- externalOnly: true,
- expectedError: false,
- },
} {
t.Run(fmt.Sprintf("[%d] %s", i, test.name), func(t *testing.T) {
bot, err := GetBotConfig(test.bot, test.externalOnly)
@@ -62,3 +56,9 @@
func TestValidateBotConfigs(t *testing.T) {
assert.NoError(t, validate())
}
+
+func TestGetBotConfig_AliasedBuilder_CorrectReference(t *testing.T) {
+ cfg, err := GetBotConfig("Android Go", true)
+ assert.NoError(t, err)
+ assert.Equal(t, "android-go-perf", cfg.Bot)
+}
diff --git a/pinpoint/go/pinpoint/pinpoint.go b/pinpoint/go/pinpoint/pinpoint.go
index 6a6319d..56aff75 100644
--- a/pinpoint/go/pinpoint/pinpoint.go
+++ b/pinpoint/go/pinpoint/pinpoint.go
@@ -193,7 +193,7 @@
c.tests = &testMetadata{
req: c.createRunBenchmarkRequest(jobID, cfg, target, req),
}
- tasks, err := c.scheduleRunBenchmark(ctx, pp.sc)
+ tasks, err := c.scheduleRunBenchmark(ctx, pp.sc, req)
if err != nil {
return resp, err
}
@@ -242,7 +242,7 @@
}
if res != nil {
spew.Dump(res)
- culprit, err := cdl.updateCommitsByResult(ctx, pp.sc, pp.mc, res, left, right)
+ culprit, err := cdl.updateCommitsByResult(ctx, pp.sc, pp.mc, res, left, right, req)
if err != nil {
return resp, skerr.Wrapf(err, "could not update commitDataList after compare")
}
@@ -259,7 +259,7 @@
}
if res != nil {
spew.Dump(res)
- culprit, err := cdl.updateCommitsByResult(ctx, pp.sc, pp.mc, res, left, right)
+ culprit, err := cdl.updateCommitsByResult(ctx, pp.sc, pp.mc, res, left, right, req)
if err != nil {
return resp, skerr.Wrapf(err, "could not update commitDataList after compare")
}
@@ -370,7 +370,7 @@
}
// scheduleRunBenchmark schedules run benchmark tests to swarming and returns the task IDs
-func (c *commitData) scheduleRunBenchmark(ctx context.Context, sc backends.SwarmingClient) ([]string, error) {
+func (c *commitData) scheduleRunBenchmark(ctx context.Context, sc backends.SwarmingClient, req *ppb.ScheduleBisectRequest) ([]string, error) {
if c.tests == nil || c.tests.req == nil {
return nil, skerr.Fmt("Cannot schedule benchmark runs without request")
}
@@ -381,11 +381,11 @@
}
if len(tasks) < maxSampleSize {
for i := 0; i < interval; i++ {
- task, err := run_benchmark.Run(ctx, sc, *c.tests.req)
+ task, err := run_benchmark.Run(ctx, sc, req, c.tests.req.Commit, c.tests.req.JobID, c.tests.req.Build, 1)
if err != nil {
return nil, skerr.Wrapf(err, "Could not start run benchmark task for request %v", c.tests.req)
}
- tasks = append(tasks, task)
+ tasks = append(tasks, task[0].TaskId)
}
}
return tasks, nil
@@ -513,8 +513,7 @@
// updateCommitsByResult takes the compare results and determines the next
// steps in the workflow. Changes are made to CommitDataList depending
// on what the compare verdict is.
-func (cdl *commitDataList) updateCommitsByResult(ctx context.Context, sc backends.SwarmingClient, mh midpoint.MidpointHandler, res *compare.CompareResults,
- left, right int) (*midpoint.Commit, error) {
+func (cdl *commitDataList) updateCommitsByResult(ctx context.Context, sc backends.SwarmingClient, mh midpoint.MidpointHandler, res *compare.CompareResults, left, right int, req *ppb.ScheduleBisectRequest) (*midpoint.Commit, error) {
if left < 0 || right >= len(cdl.commits) {
return nil, skerr.Fmt("cannot update commitDataList with left %d and right %d index out of bounds", left, right)
}
@@ -522,7 +521,7 @@
return nil, skerr.Fmt("cannot update commitDataList with left %d index >= right %d", left, right)
}
if res.Verdict == compare.Unknown {
- return nil, cdl.runMoreTestsIfNeeded(ctx, sc, left, right)
+ return nil, cdl.runMoreTestsIfNeeded(ctx, sc, left, right, req)
} else if res.Verdict == compare.Different {
return cdl.findMidpointOrCulprit(ctx, mh, left, right)
}
@@ -555,9 +554,9 @@
}
// runMoreTestsIfNeeded adds more run_benchmark tasks to the left and right commit
-func (cdl *commitDataList) runMoreTestsIfNeeded(ctx context.Context, sc backends.SwarmingClient, left, right int) error {
+func (cdl *commitDataList) runMoreTestsIfNeeded(ctx context.Context, sc backends.SwarmingClient, left, right int, req *ppb.ScheduleBisectRequest) error {
c := cdl.commits[left]
- tasks, err := c.scheduleRunBenchmark(ctx, sc)
+ tasks, err := c.scheduleRunBenchmark(ctx, sc, req)
if err != nil {
return skerr.Wrapf(err, "could not schedule more tasks for left commit [%d] %s", left, c.commit.GitHash[:7])
}
@@ -566,7 +565,7 @@
c.tests.isRunning = true
}
c = cdl.commits[right]
- tasks, err = c.scheduleRunBenchmark(ctx, sc)
+ tasks, err = c.scheduleRunBenchmark(ctx, sc, req)
if err != nil {
return skerr.Wrapf(err, "could not schedule more tasks for right commit [%d] %s", right, c.commit.GitHash[:7])
}
diff --git a/pinpoint/go/pinpoint/pinpoint_test.go b/pinpoint/go/pinpoint/pinpoint_test.go
index 624b4f1..b953c7a 100644
--- a/pinpoint/go/pinpoint/pinpoint_test.go
+++ b/pinpoint/go/pinpoint/pinpoint_test.go
@@ -352,7 +352,7 @@
sc := &backends.SwarmingClientImpl{
ApiClient: msc,
}
- tasks, err := c.scheduleRunBenchmark(ctx, sc)
+ tasks, err := c.scheduleRunBenchmark(ctx, sc, req)
So(err, ShouldBeNil)
So(tasks[0], ShouldEqual, "new_task")
So(len(tasks), ShouldEqual, interval)
@@ -364,7 +364,7 @@
sc := &backends.SwarmingClientImpl{
ApiClient: msc,
}
- tasks, err := c.scheduleRunBenchmark(ctx, sc)
+ tasks, err := c.scheduleRunBenchmark(ctx, sc, req)
So(err, ShouldErrLike, "Cannot schedule benchmark runs without request")
So(tasks, ShouldBeNil)
})
@@ -375,7 +375,7 @@
sc := &backends.SwarmingClientImpl{
ApiClient: msc,
}
- tasks, err := c.scheduleRunBenchmark(ctx, sc)
+ tasks, err := c.scheduleRunBenchmark(ctx, sc, req)
So(err, ShouldErrLike, "Cannot schedule benchmark runs without request")
So(tasks, ShouldBeNil)
})
@@ -406,7 +406,7 @@
sc := &backends.SwarmingClientImpl{
ApiClient: msc,
}
- tasks, err := c.scheduleRunBenchmark(ctx, sc)
+ tasks, err := c.scheduleRunBenchmark(ctx, sc, req)
So(err, ShouldErrLike, errMsg)
So(tasks, ShouldBeNil)
})
@@ -776,6 +776,7 @@
msc := swarmingMocks.NewApiClient(t)
c := mockhttpclient.NewURLMock().Client()
mmh := midpoint.New(ctx, c)
+ req := defaultRunRequest()
Convey(`Error`, t, func() {
cdl := commitDataList{
@@ -790,7 +791,7 @@
sc := &backends.SwarmingClientImpl{
ApiClient: msc,
}
- mid, err := cdl.updateCommitsByResult(ctx, sc, mmh, res, left, right)
+ mid, err := cdl.updateCommitsByResult(ctx, sc, mmh, res, left, right, req)
So(err, ShouldErrLike, "index out of bounds")
So(mid, ShouldBeNil)
})
@@ -800,7 +801,7 @@
sc := &backends.SwarmingClientImpl{
ApiClient: msc,
}
- mid, err := cdl.updateCommitsByResult(ctx, sc, mmh, res, left, right)
+ mid, err := cdl.updateCommitsByResult(ctx, sc, mmh, res, left, right, req)
So(err, ShouldErrLike, fmt.Sprintf("left %d index >= right %d", left, right))
So(mid, ShouldBeNil)
})
@@ -885,7 +886,7 @@
sc := &backends.SwarmingClientImpl{
ApiClient: msc,
}
- err = cdl.runMoreTestsIfNeeded(ctx, sc, left, right)
+ err = cdl.runMoreTestsIfNeeded(ctx, sc, left, right, req)
So(err, ShouldBeNil)
So(len(lcommit.tests.tasks), ShouldEqual, interval+2)
So(lcommit.tests.tasks[0], ShouldEqual, "old_left_task_1")
diff --git a/pinpoint/go/run_benchmark/BUILD.bazel b/pinpoint/go/run_benchmark/BUILD.bazel
index dd58ff9..2da6950 100644
--- a/pinpoint/go/run_benchmark/BUILD.bazel
+++ b/pinpoint/go/run_benchmark/BUILD.bazel
@@ -4,8 +4,10 @@
go_library(
name = "run_benchmark",
srcs = [
+ "benchmark_test_factory.go",
"run_benchmark.go",
- "telemetry_exp.go",
+ "swarming_helpers.go",
+ "telemetry.go",
],
importpath = "go.skia.org/infra/pinpoint/go/run_benchmark",
visibility = ["//visibility:public"],
@@ -15,6 +17,7 @@
"//go/util",
"//pinpoint/go/backends",
"//pinpoint/go/bot_configs",
+ "//pinpoint/proto/v1:proto",
"@org_chromium_go_luci//common/api/swarming/swarming/v1:swarming",
],
)
@@ -22,8 +25,9 @@
go_test(
name = "run_benchmark_test",
srcs = [
+ "benchmark_test_factory_test.go",
"run_benchmark_test.go",
- "telemetry_exp_test.go",
+ "telemetry_test.go",
],
embed = [":run_benchmark"],
deps = [
@@ -31,11 +35,9 @@
"//go/swarming",
"//go/swarming/mocks",
"//pinpoint/go/backends",
- "//pinpoint/go/bot_configs",
- "@com_github_smartystreets_goconvey//convey",
+ "//pinpoint/proto/v1:proto",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//mock",
"@org_chromium_go_luci//common/api/swarming/swarming/v1:swarming",
- "@org_chromium_go_luci//common/testing/assertions",
],
)
diff --git a/pinpoint/go/run_benchmark/benchmark_test_factory.go b/pinpoint/go/run_benchmark/benchmark_test_factory.go
new file mode 100644
index 0000000..97836ca
--- /dev/null
+++ b/pinpoint/go/run_benchmark/benchmark_test_factory.go
@@ -0,0 +1,54 @@
+package run_benchmark
+
+import (
+ "strings"
+
+ "go.skia.org/infra/go/skerr"
+
+ bc "go.skia.org/infra/pinpoint/go/bot_configs"
+ ppb "go.skia.org/infra/pinpoint/proto/v1"
+)
+
+type BenchmarkTest interface {
+ GetCommand() []string
+}
+
+// NewBenchmarkTest returns a BenchmarkTest based on the request parameters.
+// The Configuration (bot) is used alongside the benchmark to determine the
+// isolate target for that combination. Based on the isolate target,
+func NewBenchmarkTest(req *ppb.ScheduleBisectRequest, commit string) (BenchmarkTest, error) {
+ bot, benchmark := req.Configuration, req.Benchmark
+ config, err := bc.GetBotConfig(bot, false)
+ if err != nil {
+ return nil, skerr.Wrapf(err, "Failed to fetch bot configs to create benchmark test")
+ }
+ target, err := bc.GetIsolateTarget(bot, benchmark)
+ if err != nil {
+ return nil, skerr.Wrapf(err, "Failed to get isolate target to create the benchmark test")
+ }
+
+ // These targets could have suffixes, especially for Android.
+ // For example, 'performance_test_suite_android_clank_monochrome_64_32_bundle'
+ if strings.Contains(target, "performance_test_suite") || strings.Contains(target, "telemetry_perf_tests") {
+ return &telemetryTest{
+ benchmark: req.Benchmark,
+ browser: config.Browser,
+ commit: commit,
+ story: req.Story,
+ storyTags: req.StoryTags,
+ }, nil
+ }
+
+ switch target {
+ case "performance_webview_test_suite":
+ return &telemetryTest{
+ benchmark: req.Benchmark,
+ browser: config.Browser,
+ commit: commit,
+ story: req.Story,
+ storyTags: req.StoryTags,
+ }, nil
+ default:
+ return nil, skerr.Fmt("Unsupported test target %s", target)
+ }
+}
diff --git a/pinpoint/go/run_benchmark/benchmark_test_factory_test.go b/pinpoint/go/run_benchmark/benchmark_test_factory_test.go
new file mode 100644
index 0000000..0c94a74
--- /dev/null
+++ b/pinpoint/go/run_benchmark/benchmark_test_factory_test.go
@@ -0,0 +1,32 @@
+package run_benchmark
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ ppb "go.skia.org/infra/pinpoint/proto/v1"
+)
+
+func TestGetBenchmark_TelemetryTestBuilder_TelemetryTests(t *testing.T) {
+ expect := func(req *ppb.ScheduleBisectRequest, commit, want string) {
+ benchmark, _ := NewBenchmarkTest(req, commit)
+ assert.Equal(t, want, reflect.TypeOf(benchmark).String())
+ }
+
+ commit := "01bfa421eee3c76bbbf32510343e074060051c9f"
+ req := &ppb.ScheduleBisectRequest{
+ Configuration: "android-go-perf",
+ Benchmark: "components_perftests",
+ }
+ expect(req, commit, "*run_benchmark.telemetryTest")
+
+ req = &ppb.ScheduleBisectRequest{
+ Story: "story",
+ StoryTags: "all",
+ Configuration: "android-pixel2_webview-perf",
+ Benchmark: "performance_browser_tests",
+ }
+ expect(req, commit, "*run_benchmark.telemetryTest")
+}
diff --git a/pinpoint/go/run_benchmark/run_benchmark.go b/pinpoint/go/run_benchmark/run_benchmark.go
index 294bdb7..1d0cbc6 100644
--- a/pinpoint/go/run_benchmark/run_benchmark.go
+++ b/pinpoint/go/run_benchmark/run_benchmark.go
@@ -8,23 +8,24 @@
import (
"context"
- "fmt"
"slices"
- swarmingV1 "go.chromium.org/luci/common/api/swarming/swarming/v1"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/swarming"
"go.skia.org/infra/pinpoint/go/backends"
"go.skia.org/infra/pinpoint/go/bot_configs"
+
+ spb "go.chromium.org/luci/common/api/swarming/swarming/v1"
+ ppb "go.skia.org/infra/pinpoint/proto/v1"
)
-// A RunBenchmarkRequest defines the request arguments of the performance test
-// to swarming.
+// A RunBenchmarkRequest defines the request arguments of the performance test to swarming.
+// Note: This is being used in workflows/internal/run_benchmark.go.
type RunBenchmarkRequest struct {
// the Pinpoint job id
JobID string
// the swarming instance and cas digest hash and bytes location for the build
- Build *swarmingV1.SwarmingRpcsCASReference
+ Build *spb.SwarmingRpcsCASReference
// commit hash
Commit string
// device configuration
@@ -37,80 +38,6 @@
Target string
}
-// swarming request to run task
-// define static, constant fields here
-var swarmingReq = swarmingV1.SwarmingRpcsNewTaskRequest{
- BotPingToleranceSecs: 1200,
- ExpirationSecs: 86400,
- // EvaluateOnly omitted
- Name: "Pinpoint bisection run benchmark task",
- // ParentTaskId omitted
- // PoolTaskTemplate omitted
- Priority: 100,
- // define properties later
- PubsubTopic: "projects/chromeperf/topics/pinpoint-swarming-updates",
- PubsubUserdata: "UNUSED", // can populate later, see example swarming call log
- Realm: "chrome:pinpoint",
- // RequestUuid: omitted
- // Resultdb: omitted
- ServiceAccount: "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
- // define tags later
- // TaskSlices optional if properties defined
- User: "Pinpoint",
- // ForceSendFields: omitted
- // NullFields: omitted
-}
-
-func createSwarmingReq(req RunBenchmarkRequest) (
- *swarmingV1.SwarmingRpcsNewTaskRequest, error) {
- // TODO(b/318863812): add mapping from device + benchmark to the specific run test
- // currently catapult maps the device + benchmark to the target and then
- // the target dictates what test to run. We can map to target if that info
- // is useful on the UI, but for this, it's not relevant.
- // see GetIsolateTarget and _GenerateQuests here:
- // https://source.chromium.org/chromium/chromium/src/+/main:third_party/catapult/dashboard/dashboard/pinpoint/handlers/new.py;drc=8fe602e47f11cfdd79225696f1f6a5556b57c58c;l=466
- // TODO(b/321299939): create an interface for different runBenchmark types
- // and refactor telemetryExp to use that interface
- exp := telemetryExp{}
- cmd, err := exp.createCmd(req)
- if err != nil {
- return nil, skerr.Fmt("Unable to create run benchmark command due to %s\n", err)
- }
- dim := make([]*swarmingV1.SwarmingRpcsStringPair,
- len(req.Config.Dimensions))
- for i, kv := range req.Config.Dimensions {
- dim[i] = &swarmingV1.SwarmingRpcsStringPair{
- Key: kv["key"],
- Value: kv["value"],
- }
- }
-
- swarmingReq.Properties = &swarmingV1.SwarmingRpcsTaskProperties{
- CasInputRoot: &swarmingV1.SwarmingRpcsCASReference{
- CasInstance: req.Build.CasInstance,
- Digest: &swarmingV1.SwarmingRpcsDigest{
- Hash: req.Build.Digest.Hash,
- SizeBytes: req.Build.Digest.SizeBytes,
- },
- },
- // TODO(b/318863812): support user submitted extra_args.
- // This support is needed for pairwise executions, not bisection.
- Command: cmd,
- Dimensions: dim,
- ExecutionTimeoutSecs: 2700,
- IoTimeoutSecs: 2700,
- RelativeCwd: "out/Release",
- }
-
- // TODO(b/318863812): update swarming task tags to more appropriate tags.
- swarmingReq.Tags = []string{
- fmt.Sprintf("pinpoint_job_id:%s", req.JobID),
- fmt.Sprintf("build_cas:%s/%d", req.Build.Digest.Hash, req.Build.Digest.SizeBytes),
- }
-
- return &swarmingReq, nil
-}
-
var runningStates = []string{
swarming.TASK_STATE_PENDING,
swarming.TASK_STATE_RUNNING,
@@ -130,16 +57,29 @@
}
// Run schedules a swarming task to run the RunBenchmarkRequest.
-func Run(ctx context.Context, sc backends.SwarmingClient, req RunBenchmarkRequest) (string, error) {
- swarmingReq, err := createSwarmingReq(req)
+func Run(ctx context.Context, sc backends.SwarmingClient, req *ppb.ScheduleBisectRequest, commit string, jobID string, buildArtifact *spb.SwarmingRpcsCASReference, iter int) ([]*spb.SwarmingRpcsTaskRequestMetadata, error) {
+ test, err := NewBenchmarkTest(req, commit)
if err != nil {
- return "", skerr.Wrapf(err, "Could not create run test request")
+ return nil, skerr.Wrapf(err, "Failed to prepare benchmark test for execution")
}
- metadataResp, err := sc.TriggerTask(ctx, swarmingReq)
+ bot := req.Configuration
+ botConfig, err := bot_configs.GetBotConfig(bot, false)
if err != nil {
- return "", skerr.Fmt("trigger task %v\ncaused error: %s", req, err)
+ return nil, skerr.Wrapf(err, "Failed to create benchmark test object")
}
- return metadataResp.TaskId, nil
+ swarmingRequest := createSwarmingRequest(jobID, test.GetCommand(), buildArtifact, botConfig.Dimensions)
+
+ resp := make([]*spb.SwarmingRpcsTaskRequestMetadata, 0)
+ for i := 0; i < iter; i++ {
+ r, err := sc.TriggerTask(ctx, swarmingRequest)
+ if err != nil {
+ return nil, skerr.Wrapf(err, "benchmark task %d with request %v failed", i, r)
+ }
+
+ resp = append(resp, r)
+ }
+
+ return resp, nil
}
diff --git a/pinpoint/go/run_benchmark/run_benchmark_test.go b/pinpoint/go/run_benchmark/run_benchmark_test.go
index f825764..8f06981 100644
--- a/pinpoint/go/run_benchmark/run_benchmark_test.go
+++ b/pinpoint/go/run_benchmark/run_benchmark_test.go
@@ -4,16 +4,16 @@
"context"
"testing"
- . "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
- swarmingV1 "go.chromium.org/luci/common/api/swarming/swarming/v1"
- . "go.chromium.org/luci/common/testing/assertions"
+
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/swarming"
"go.skia.org/infra/go/swarming/mocks"
"go.skia.org/infra/pinpoint/go/backends"
- "go.skia.org/infra/pinpoint/go/bot_configs"
+
+ swarmingV1 "go.chromium.org/luci/common/api/swarming/swarming/v1"
+ ppb "go.skia.org/infra/pinpoint/proto/v1"
)
var req = RunBenchmarkRequest{
@@ -31,37 +31,38 @@
}
var expectedErr = skerr.Fmt("some error")
-func TestRun(t *testing.T) {
+func TestRun_TelemetryTest_ValidExecution(t *testing.T) {
ctx := context.Background()
mockClient := mocks.NewApiClient(t)
sc := &backends.SwarmingClientImpl{
ApiClient: mockClient,
}
- Convey(`OK`, t, func() {
- cfg, err := bot_configs.GetBotConfig("linux-perf", true)
- So(err, ShouldBeNil)
- req.Config = cfg
- mockClient.On("TriggerTask", ctx, mock.Anything).
- Return(&swarmingV1.SwarmingRpcsTaskRequestMetadata{
- TaskId: "123",
- }, nil).Once()
- taskId, err := Run(ctx, sc, req)
- So(err, ShouldBeNil)
- So(taskId, ShouldEqual, "123")
- })
- Convey(`Return error`, t, func() {
- cfg, err := bot_configs.GetBotConfig("linux-perf", true)
- So(err, ShouldBeNil)
- req.Config = cfg
- mockClient.On("TriggerTask", ctx, mock.Anything).
- Return(&swarmingV1.SwarmingRpcsTaskRequestMetadata{
- TaskId: "123",
- }, expectedErr).Once()
- taskId, err := Run(ctx, sc, req)
- So(taskId, ShouldBeEmpty)
- So(err, ShouldErrLike, expectedErr)
- })
+ bisectReq := &ppb.ScheduleBisectRequest{
+ Story: "story",
+ StoryTags: "all",
+ Configuration: "android-pixel2_webview-perf",
+ Benchmark: "performance_browser_tests",
+ }
+
+ buildArtifact := &swarmingV1.SwarmingRpcsCASReference{
+ CasInstance: "instance",
+ Digest: &swarmingV1.SwarmingRpcsDigest{
+ Hash: "hash",
+ SizeBytes: 0,
+ },
+ }
+
+ commit := "64893ca6294946163615dcf23b614afe0419bfa3"
+
+ mockClient.On("TriggerTask", ctx, mock.Anything).
+ Return(&swarmingV1.SwarmingRpcsTaskRequestMetadata{
+ TaskId: "123",
+ }, nil).Once()
+ taskIds, err := Run(ctx, sc, bisectReq, commit, "id", buildArtifact, 1)
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(taskIds))
+ assert.Equal(t, "123", taskIds[0].TaskId)
}
func TestIsTaskStateFinished_GivenCompleteStates_ReturnsTrue(t *testing.T) {
diff --git a/pinpoint/go/run_benchmark/swarming_helpers.go b/pinpoint/go/run_benchmark/swarming_helpers.go
new file mode 100644
index 0000000..78b2ef3
--- /dev/null
+++ b/pinpoint/go/run_benchmark/swarming_helpers.go
@@ -0,0 +1,72 @@
+package run_benchmark
+
+import (
+ "fmt"
+
+ spb "go.chromium.org/luci/common/api/swarming/swarming/v1"
+)
+
+func convertDimensions(dimensions []map[string]string) []*spb.SwarmingRpcsStringPair {
+ // TODO(b/318863812): add mapping from device + benchmark to the specific run test
+ // currently catapult maps the device + benchmark to the target and then
+ // the target dictates what test to run. We can map to target if that info
+ // is useful on the UI, but for this, it's not relevant.
+ // see GetIsolateTarget and _GenerateQuests here:
+ // https://source.chromium.org/chromium/chromium/src/+/main:third_party/catapult/dashboard/dashboard/pinpoint/handlers/new.py;drc=8fe602e47f11cfdd79225696f1f6a5556b57c58c;l=466
+ // TODO(b/321299939): create an interface for different runBenchmark types
+ // and refactor telemetryExp to use that interface
+ dim := make([]*spb.SwarmingRpcsStringPair, len(dimensions))
+ for i, kv := range dimensions {
+ dim[i] = &spb.SwarmingRpcsStringPair{
+ Key: kv["key"],
+ Value: kv["value"],
+ }
+ }
+ return dim
+}
+
+func generateProperties(command []string, casRef *spb.SwarmingRpcsCASReference, dim []*spb.SwarmingRpcsStringPair) *spb.SwarmingRpcsTaskProperties {
+ return &spb.SwarmingRpcsTaskProperties{
+ CasInputRoot: casRef,
+ Command: command,
+ Dimensions: dim,
+ ExecutionTimeoutSecs: 2700,
+ IoTimeoutSecs: 2700,
+ RelativeCwd: "out/Release",
+ }
+}
+
+func generateTags(jobID string, hash string, sizeBytes int64) []string {
+ // TODO(b/318863812): update swarming task tags to more appropriate tags.
+ return []string{
+ fmt.Sprintf("pinpoint_job_id:%s", jobID),
+ fmt.Sprintf("build_cas:%s/%d", hash, sizeBytes),
+ }
+}
+
+func createSwarmingRequest(jobID string, command []string, casRef *spb.SwarmingRpcsCASReference, dimensions []map[string]string) *spb.SwarmingRpcsNewTaskRequest {
+ return &spb.SwarmingRpcsNewTaskRequest{
+ BotPingToleranceSecs: 1200,
+ ExpirationSecs: 86400,
+ // EvaluateOnly omitted
+ Name: "Pinpoint bisection run benchmark task",
+ // ParentTaskId omitted
+ // PoolTaskTemplate omitted
+ Priority: 100,
+ // define properties later
+ PubsubTopic: "projects/chromeperf/topics/pinpoint-swarming-updates",
+ // can populate later, see example swarming call log
+ PubsubUserdata: "UNUSED",
+ Realm: "chrome:pinpoint",
+ // RequestUuid: omitted
+ // Resultdb: omitted
+ ServiceAccount: "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
+ // define tags later
+ // TaskSlices optional if properties defined
+ User: "Pinpoint",
+ // ForceSendFields: omitted
+ // NullFields: omitted
+ Properties: generateProperties(command, casRef, convertDimensions(dimensions)),
+ Tags: generateTags(jobID, casRef.Digest.Hash, casRef.Digest.SizeBytes),
+ }
+}
diff --git a/pinpoint/go/run_benchmark/telemetry.go b/pinpoint/go/run_benchmark/telemetry.go
new file mode 100644
index 0000000..c59084e
--- /dev/null
+++ b/pinpoint/go/run_benchmark/telemetry.go
@@ -0,0 +1,116 @@
+package run_benchmark
+
+import (
+ "fmt"
+
+ "go.skia.org/infra/go/util"
+)
+
+var defaultExtraArgs = []string{
+ "-v",
+ "--upload-results",
+ "--output-format",
+ "histograms",
+ "--isolated-script-test-output",
+ "${ISOLATED_OUTDIR}/output.json",
+}
+
+// Please keep this executable-argument mapping synced with perf waterfall:
+// https://chromium.googlesource.com/chromium/src/+/main/tools/perf/core/bot_platforms.py
+var waterfallEnabledGtests = util.NewStringSet([]string{
+ "base_perftests",
+ "components_perftests",
+ "dawn_perf_tests",
+ "gpu_perftests",
+ "load_library_perf_tests",
+ "performance_browser_tests",
+ "sync_performance_tests",
+ "tracing_perftests",
+ "views_perftests",
+})
+
+type telemetryTest struct {
+ benchmark string
+ browser string
+ commit string
+ story string
+ storyTags string
+}
+
+// getCommand generates the command needed to execute Telemetry benchmark tests.
+// This function assumes that the request object has been validated, and that
+// required fields have been verified for.
+// TODO(b/318863812): support user submitted extra_args.
+// This support is needed for pairwise executions, not bisection.
+func (t *telemetryTest) GetCommand() []string {
+ cmd := []string{
+ "luci-auth",
+ "context",
+ "--",
+ "vpython3",
+ "../../testing/test_env.py",
+ "../../testing/scripts/run_performance_tests.py",
+ }
+
+ // Note(jeffyoon@): The logic below can likely be made more efficient, but
+ // for parity sake we retain the same order for arguments as Catapult's Pinpoint.
+ // Ref: https://source.chromium.org/chromium/chromium/src/+/main:third_party/catapult/dashboard/dashboard/pinpoint/models/quest/run_telemetry_test.py;drc=10f23074bdab2b425cb29a8224b63298fdac42b7;l=127
+ if _, ok := waterfallEnabledGtests[t.benchmark]; ok {
+ if t.benchmark == "performance_browser_tests" {
+ cmd = append(cmd, "browser_tests")
+ } else {
+ cmd = append(cmd, t.benchmark)
+ }
+ } else {
+ cmd = append(cmd, "../../tools/perf/run_benchmark")
+ }
+
+ if t.storyTags == "" {
+ cmd = append(cmd, "-d")
+ }
+
+ if _, ok := waterfallEnabledGtests[t.benchmark]; ok {
+ cmd = append(cmd, "--gtest-benchmark-name", t.benchmark, "--non-telemetry", "true")
+ switch t.benchmark {
+ case "base_perftests", "dawn_perf_tests", "sync_performance_tests":
+ cmd = append(cmd, "--test-launcher-jobs=1", "--test-launcher-retry-limit=0")
+ case "components_perftests", "views_perftests":
+ cmd = append(cmd, "--xvfb")
+ case "performance_browser_tests":
+ // Allow the full performance runs to take up to 60 seconds (rather
+ // than the default of 30 for normal CQ browser test runs).
+ cmd = append(cmd, "--full-performance-run", "--test-launcher-jobs=1",
+ "--test-launcher-retry-limit=0", "--ui-test-action-timeout=60000",
+ "--ui-test-action-max-timeout=60000", "--test-launcher-timeout=60000",
+ "--gtest_filter=*/TabCapturePerformanceTest.*:*/CastV2PerformanceTest.*")
+ default:
+ break
+ }
+ } else {
+ cmd = append(cmd, "--benchmarks", t.benchmark)
+ }
+
+ if t.story != "" {
+ // TODO(b/40635221): Note that usage of "--run-full-story-set" and "--story-filter"
+ // can be replaced with --story=<story> (no regex needed). See crrev/c/1869800.
+ cmd = append(cmd, "--story-filter", fmt.Sprintf("^%s$", t.story))
+ }
+
+ if t.storyTags != "" {
+ cmd = append(cmd, "--story-tag-filter", t.storyTags)
+ }
+
+ cmd = append(cmd, "--pageset-repeat", "1", "--browser", t.browser)
+ cmd = append(cmd, defaultExtraArgs...)
+
+ // For results2 to differentiate between runs, we need to add the
+ // Telemetry parameter `--results-label <change>` to the runs.
+ // TODO(jeffyoon@) deprecate this label once the UI is no longer dependant on this.
+ cmd = append(cmd, "--results-label", t.commit[:7])
+
+ // Note: Appending "--run-full-story-set" last per comment above to retain
+ // argument order. This is always appended in catapult regardless of whether
+ // a story is defined or not.
+ cmd = append(cmd, "--run-full-story-set")
+ return cmd
+}
diff --git a/pinpoint/go/run_benchmark/telemetry_exp.go b/pinpoint/go/run_benchmark/telemetry_exp.go
deleted file mode 100644
index f57ee7d..0000000
--- a/pinpoint/go/run_benchmark/telemetry_exp.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package run_benchmark
-
-import (
- "fmt"
-
- "go.skia.org/infra/go/skerr"
- "go.skia.org/infra/go/util"
-)
-
-var defaultExtraArgs = []string{
- "-v",
- "--upload-results",
- "--output-format",
- "histograms",
- "--isolated-script-test-output",
- "${ISOLATED_OUTDIR}/output.json",
-}
-
-// TODO(b/318863812): implement benchmarks in _WATERFALL_ENABLED_GTEST_NAMES
-// crbug/1146949
-// Please keep this executable-argument mapping synced with perf waterfall:
-// https://chromium.googlesource.com/chromium/src/+/main/tools/perf/core/bot_platforms.py
-var waterfallEnabledGtests = util.NewStringSet([]string{
- "base_perftests",
- "components_perftests",
- "dawn_perf_tests",
- "gpu_perftests",
- "load_library_perf_tests",
- "performance_browser_tests",
- "sync_performance_tests",
- "tracing_perftests",
- "views_perftests",
-})
-
-// A telemetryExp contains the command for the test device
-// to run a telemetry benchmark on Chrome browser.
-// telemetryExp will support most devices.
-type telemetryExp struct{}
-
-// createCmd creates the command for the test device.
-func (t *telemetryExp) createCmd(req RunBenchmarkRequest) ([]string, error) {
- // TODO(b/318863812): implement benchmarks in _WATERFALL_ENABLED_GTEST_NAMES
- // Benchmarks in _WATERFALL_ENABLED_GTEST_NAMES are rarely used.
- // see: https://source.chromium.org/chromium/chromium/src/+/main:third_party/catapult/dashboard/dashboard/pinpoint/models/quest/run_telemetry_test.py;drc=10f23074bdab2b425cb29a8224b63298fdac42b7;l=96
- _, ok := waterfallEnabledGtests[req.Benchmark]
- if ok {
- return nil, skerr.Fmt("Benchmark %s is not yet implemented", req.Benchmark)
- }
- cmd := []string{
- "luci-auth",
- "context",
- "--",
- "vpython3",
- "../../testing/test_env.py",
- "../../testing/scripts/run_performance_tests.py",
- "../../tools/perf/run_benchmark",
- // TODO(b/318863812): Implement story_tags. Some of our entrenched users
- // are only familiar with running Pinpoint jobs with story tags. Although
- // some of this behavior is due to UI confusion, we should still support it
- // and ensure the UI makes it clear what the differences are.
- // see: https://source.chromium.org/chromium/chromium/src/+/main:third_party/catapult/dashboard/dashboard/pinpoint/models/quest/run_telemetry_test.py;drc=10f23074bdab2b425cb29a8224b63298fdac42b7;l=127
- "-d",
- }
- if req.Benchmark == "" {
- return nil, skerr.Fmt("Missing 'Benchmark' argument.")
- }
- cmd = append(cmd, "--benchmarks", req.Benchmark)
- if req.Story == "" {
- return nil, skerr.Fmt("Missing 'Story' argument.")
- }
- cmd = append(cmd, "--story-filter", fmt.Sprintf("^%s$", req.Story))
- cmd = append(cmd, "--pageset-repeat", "1")
- cmd = append(cmd, "--browser", req.Config.Browser)
- cmd = append(cmd, defaultExtraArgs...)
-
- // TODO(b/318863812): Add support for non-chromium commits
- // append sha for results2
- if req.Commit == "" {
- return nil, skerr.Fmt("Missing 'Commit' argument.")
- }
- cmd = append(cmd,
- "--results-label",
- fmt.Sprintf("chromium@%s", req.Commit[:7]))
-
- // unclear what happens if you write the flags in
- // a different order
- // see: https://source.chromium.org/chromium/chromium/src/+/main:third_party/catapult/dashboard/dashboard/pinpoint/models/quest/run_telemetry_test.py;drc=10f23074bdab2b425cb29a8224b63298fdac42b7;l=80
- cmd = append(cmd, "--run-full-story-set")
-
- return cmd, nil
-}
diff --git a/pinpoint/go/run_benchmark/telemetry_exp_test.go b/pinpoint/go/run_benchmark/telemetry_exp_test.go
deleted file mode 100644
index d582f55..0000000
--- a/pinpoint/go/run_benchmark/telemetry_exp_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package run_benchmark
-
-import (
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestCreateCmd(t *testing.T) {
- for i, test := range []struct {
- name string
- req RunBenchmarkRequest
- expectedErr string
- }{
- {
- name: "create command works",
- req: RunBenchmarkRequest{
- Benchmark: "benchmark",
- Story: "story",
- Commit: "64893ca6294946163615dcf23b614afe0419bfa3",
- },
- expectedErr: "",
- },
- {
- name: "benchmark error",
- req: RunBenchmarkRequest{
- Story: "story",
- Commit: "64893ca6294946163615dcf23b614afe0419bfa3",
- },
- expectedErr: "Benchmark",
- },
- {
- name: "story error",
- req: RunBenchmarkRequest{
- Benchmark: "benchmark",
- Commit: "64893ca6294946163615dcf23b614afe0419bfa3",
- },
- expectedErr: "Story",
- },
- {
- name: "commit error",
- req: RunBenchmarkRequest{
- Benchmark: "benchmark",
- Story: "story",
- },
- expectedErr: "Commit",
- },
- {
- name: "base_perftests not yet implemented",
- req: RunBenchmarkRequest{
- Benchmark: "base_perftests",
- Story: "story",
- Commit: "64893ca6294946163615dcf23b614afe0419bfa3",
- },
- expectedErr: "base_perftests is not yet implemented",
- },
- } {
- t.Run(fmt.Sprintf("[%d] %s", i, test.name), func(t *testing.T) {
- e := telemetryExp{}
- cmd, err := e.createCmd(test.req)
- if test.expectedErr == "" {
- assert.Contains(t, cmd, test.req.Benchmark)
- assert.Contains(t, cmd, fmt.Sprintf("^%s$", test.req.Story))
- } else {
- assert.ErrorContains(t, err, test.expectedErr)
- }
- })
- }
-}
diff --git a/pinpoint/go/run_benchmark/telemetry_test.go b/pinpoint/go/run_benchmark/telemetry_test.go
new file mode 100644
index 0000000..f915fb1
--- /dev/null
+++ b/pinpoint/go/run_benchmark/telemetry_test.go
@@ -0,0 +1,71 @@
+package run_benchmark
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ ppb "go.skia.org/infra/pinpoint/proto/v1"
+)
+
+func TestGetCommand_WaterfallGTest_TestCommand(t *testing.T) {
+ commit := "01bfa421eee3c76bbbf32510343e074060051c9f"
+
+ req := &ppb.ScheduleBisectRequest{
+ Configuration: "android-go-perf",
+ Benchmark: "components_perftests",
+ }
+
+ b, err := NewBenchmarkTest(req, commit)
+ assert.NoError(t, err)
+
+ cmd := b.GetCommand()
+ // stories w/o story tags should have -d set
+ assert.Contains(t, cmd, "-d")
+ assert.NotContains(t, cmd, "--story-tag-filter")
+
+ // Should not contain run_benchmark because it's a waterfall
+ // enabled GTest.
+ assert.NotContains(t, cmd, "../../tools/perf/run_benchmark")
+}
+
+func TestGetCommand_PerfBrowserTestWithStory_StoryTestCommand(t *testing.T) {
+ commit := "01bfa421eee3c76bbbf32510343e074060051c9f"
+
+ req := &ppb.ScheduleBisectRequest{
+ Story: "story",
+ StoryTags: "all",
+ Configuration: "android-pixel2_webview-perf",
+ Benchmark: "performance_browser_tests",
+ }
+
+ b, err := NewBenchmarkTest(req, commit)
+ assert.NoError(t, err)
+
+ cmd := b.GetCommand()
+ // stories w/o story tags should have -d set
+ assert.Contains(t, cmd, "--story-filter")
+ assert.Contains(t, cmd, "--story-tag-filter")
+
+ // Special gtest filter for performance_browser_tests
+ assert.Contains(t, cmd, "--gtest_filter=*/TabCapturePerformanceTest.*:*/CastV2PerformanceTest.*")
+}
+
+func TestGetCommand_NonWaterfallEnabledGTest_TestCommand(t *testing.T) {
+ commit := "01bfa421eee3c76bbbf32510343e074060051c9f"
+
+ req := &ppb.ScheduleBisectRequest{
+ Configuration: "android-pixel2_webview-perf",
+ Benchmark: "random_test",
+ }
+
+ b, err := NewBenchmarkTest(req, commit)
+ assert.NoError(t, err)
+
+ cmd := b.GetCommand()
+
+ // Non waterfall enabled gtest should have run_benchmark
+ assert.Contains(t, cmd, "../../tools/perf/run_benchmark")
+ assert.Contains(t, cmd, "--benchmarks")
+ assert.Contains(t, cmd, "random_test")
+}
diff --git a/pinpoint/go/workflows/internal/BUILD.bazel b/pinpoint/go/workflows/internal/BUILD.bazel
index 1fd88ec..fb1270c 100644
--- a/pinpoint/go/workflows/internal/BUILD.bazel
+++ b/pinpoint/go/workflows/internal/BUILD.bazel
@@ -15,6 +15,7 @@
"//pinpoint/go/build_chrome",
"//pinpoint/go/run_benchmark",
"//pinpoint/go/workflows",
+ "//pinpoint/proto/v1:proto",
"@io_temporal_go_sdk//activity",
"@io_temporal_go_sdk//temporal",
"@io_temporal_go_sdk//workflow",
diff --git a/pinpoint/go/workflows/internal/run_benchmark.go b/pinpoint/go/workflows/internal/run_benchmark.go
index aa12df2..e7efe90 100644
--- a/pinpoint/go/workflows/internal/run_benchmark.go
+++ b/pinpoint/go/workflows/internal/run_benchmark.go
@@ -10,6 +10,7 @@
"go.skia.org/infra/pinpoint/go/backends"
"go.skia.org/infra/pinpoint/go/run_benchmark"
"go.skia.org/infra/pinpoint/go/workflows"
+ ppb "go.skia.org/infra/pinpoint/proto/v1"
"go.temporal.io/sdk/activity"
"go.temporal.io/sdk/temporal"
"go.temporal.io/sdk/workflow"
@@ -68,7 +69,6 @@
}
resp.CAS = cas
-
return &resp, nil
}
@@ -82,7 +82,23 @@
return "", skerr.Wrap(err)
}
- return run_benchmark.Run(ctx, sc, params.Request)
+ // TODO(jeffyoon@) - this is a workaround to generate the request object s.t. the refactor
+ // does not obstruct the current run_benchmark workflow. Once the request spec defined
+ // by the proto is in full use, this can be deprecated.
+ //
+ // This only fills in the minimum set required to run the benchmark.
+ req := &ppb.ScheduleBisectRequest{
+ // run_benchmark.RunBenchmarkRequest doesn't support story tags
+ Configuration: params.Request.Config.Bot,
+ Benchmark: params.Request.Benchmark,
+ Story: params.Request.Story,
+ }
+
+ taskIds, err := run_benchmark.Run(ctx, sc, req, params.Request.Commit, params.Request.JobID, params.Request.Build, 1)
+ if err != nil {
+ return "", err
+ }
+ return taskIds[0].TaskId, nil
}
// WaitTaskFinishedActivity polls the task until it finishes or errors. Returns the status