diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2019-11-27 16:40:04 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2019-11-27 16:40:04 +0100 |
| commit | 0d63f89cabcbc3e57201973370a309b646cc43c9 (patch) | |
| tree | 3eb549e19dc354cf37fa9c735ea2f7cf3b901a5b | |
| parent | 4cea005389a2c907bf947f6f2b79ef2bc9e42ce1 (diff) | |
syz-ci: allow enabling bisect cause and fix jobs separately
Some backport-only kernels may only be interested in fix bisections.
Allow enabling these separately.
| -rw-r--r-- | dashboard/app/api.go | 4 | ||||
| -rw-r--r-- | dashboard/app/bisect_test.go | 1 | ||||
| -rw-r--r-- | dashboard/app/jobs.go | 56 | ||||
| -rw-r--r-- | dashboard/app/util_test.go | 14 | ||||
| -rw-r--r-- | dashboard/dashapi/dashapi.go | 9 | ||||
| -rw-r--r-- | syz-ci/jobs.go | 40 | ||||
| -rw-r--r-- | syz-ci/syz-ci.go | 43 |
7 files changed, 91 insertions, 76 deletions
diff --git a/dashboard/app/api.go b/dashboard/app/api.go index f8a59a3e9..09313b6a6 100644 --- a/dashboard/app/api.go +++ b/dashboard/app/api.go @@ -357,10 +357,10 @@ func apiJobPoll(c context.Context, r *http.Request, payload []byte) (interface{} if err := json.Unmarshal(payload, req); err != nil { return nil, fmt.Errorf("failed to unmarshal request: %v", err) } - if len(req.PatchTestManagers) == 0 && len(req.BisectManagers) == 0 { + if len(req.Managers) == 0 { return nil, fmt.Errorf("no managers") } - return pollPendingJobs(c, req.PatchTestManagers, req.BisectManagers) + return pollPendingJobs(c, req.Managers) } func apiJobDone(c context.Context, r *http.Request, payload []byte) (interface{}, error) { diff --git a/dashboard/app/bisect_test.go b/dashboard/app/bisect_test.go index 88aedc7fb..b385aa655 100644 --- a/dashboard/app/bisect_test.go +++ b/dashboard/app/bisect_test.go @@ -771,6 +771,7 @@ func TestBisectCauseExternal(t *testing.T) { rep := c.client.pollBug() pollResp := c.client.pollJobs(build.Manager) + c.expectNE(pollResp.ID, "") jobID := pollResp.ID done := &dashapi.JobDoneReq{ ID: jobID, diff --git a/dashboard/app/jobs.go b/dashboard/app/jobs.go index 7c7c011c0..4a31d8ff1 100644 --- a/dashboard/app/jobs.go +++ b/dashboard/app/jobs.go @@ -186,17 +186,10 @@ func checkTestJob(c context.Context, bug *Bug, bugReporting *BugReporting, crash } // pollPendingJobs returns the next job to execute for the provided list of managers. -func pollPendingJobs(c context.Context, testMgrs, bisectMgrs []string) (*dashapi.JobPollResp, error) { - testManagers := make(map[string]bool) - for _, mgr := range testMgrs { - testManagers[mgr] = true - } - bisectManagers := make(map[string]bool) - for _, mgr := range bisectMgrs { - bisectManagers[mgr] = true - } +func pollPendingJobs(c context.Context, managers map[string]dashapi.ManagerJobs) ( + *dashapi.JobPollResp, error) { retry: - job, jobKey, err := getNextJob(c, testManagers, bisectManagers) + job, jobKey, err := getNextJob(c, managers) if job == nil || err != nil { return nil, err } @@ -210,35 +203,45 @@ retry: return resp, nil } -func getNextJob(c context.Context, testManagers, bisectManagers map[string]bool) (*Job, *db.Key, error) { - job, jobKey, err := loadPendingJob(c, testManagers, bisectManagers) +func getNextJob(c context.Context, managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) { + job, jobKey, err := loadPendingJob(c, managers) if job != nil || err != nil { return job, jobKey, err } - if len(bisectManagers) == 0 { - return nil, nil, nil - } // We need both C and syz repros, but the crazy datastore query restrictions // do not allow to use ReproLevel>ReproLevelNone in the query. So we do 2 separate queries. // C repros tend to be of higher reliability so maybe it's not bad. - job, jobKey, err = createBisectJob(c, bisectManagers, ReproLevelC) + job, jobKey, err = createBisectJob(c, managers, ReproLevelC) if job != nil || err != nil { return job, jobKey, err } - return createBisectJob(c, bisectManagers, ReproLevelSyz) + return createBisectJob(c, managers, ReproLevelSyz) } -func createBisectJob(c context.Context, managers map[string]bool, reproLevel dashapi.ReproLevel) ( - *Job, *db.Key, error) { - job, jobKey, err := findBugsForBisection(c, managers, reproLevel, JobBisectCause) +func createBisectJob(c context.Context, managers map[string]dashapi.ManagerJobs, + reproLevel dashapi.ReproLevel) (*Job, *db.Key, error) { + causeManagers := make(map[string]bool) + fixManagers := make(map[string]bool) + for mgr, jobs := range managers { + if jobs.BisectCause { + causeManagers[mgr] = true + } + if jobs.BisectFix { + fixManagers[mgr] = true + } + } + job, jobKey, err := findBugsForBisection(c, causeManagers, reproLevel, JobBisectCause) if job != nil || err != nil { return job, jobKey, err } - return findBugsForBisection(c, managers, reproLevel, JobBisectFix) + return findBugsForBisection(c, fixManagers, reproLevel, JobBisectFix) } -func findBugsForBisection(c context.Context, managers map[string]bool, reproLevel dashapi.ReproLevel, jobType JobType) ( - *Job, *db.Key, error) { +func findBugsForBisection(c context.Context, managers map[string]bool, + reproLevel dashapi.ReproLevel, jobType JobType) (*Job, *db.Key, error) { + if len(managers) == 0 { + return nil, nil, nil + } // Note: we could also include len(Commits)==0 but datastore does not work this way. // So we would need an additional HasCommits field or something. // Note: For JobBisectCause, order the bugs from newest to oldest. For JobBisectFix, @@ -762,7 +765,7 @@ func jobReported(c context.Context, jobID string) error { return db.RunInTransaction(c, tx, nil) } -func loadPendingJob(c context.Context, testManagers, bisectManagers map[string]bool) (*Job, *db.Key, error) { +func loadPendingJob(c context.Context, managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) { var jobs []*Job keys, err := db.NewQuery("Job"). Filter("Finished=", time.Time{}). @@ -775,11 +778,12 @@ func loadPendingJob(c context.Context, testManagers, bisectManagers map[string]b for i, job := range jobs { switch job.Type { case JobTestPatch: - if !testManagers[job.Manager] { + if !managers[job.Manager].TestPatches { continue } case JobBisectCause, JobBisectFix: - if !bisectManagers[job.Manager] { + if job.Type == JobBisectCause && !managers[job.Manager].BisectCause || + job.Type == JobBisectFix && !managers[job.Manager].BisectFix { continue } // Don't retry bisection jobs too often. diff --git a/dashboard/app/util_test.go b/dashboard/app/util_test.go index 1d98b0e6d..c80ab6a95 100644 --- a/dashboard/app/util_test.go +++ b/dashboard/app/util_test.go @@ -392,10 +392,16 @@ func (client *apiClient) updateBug(extID string, status dashapi.BugStatus, dup s } func (client *apiClient) pollJobs(manager string) *dashapi.JobPollResp { - resp, err := client.JobPoll(&dashapi.JobPollReq{ - PatchTestManagers: []string{manager}, - BisectManagers: []string{manager}, - }) + req := &dashapi.JobPollReq{ + Managers: map[string]dashapi.ManagerJobs{ + manager: { + TestPatches: true, + BisectCause: true, + BisectFix: true, + }, + }, + } + resp, err := client.JobPoll(req) client.expectOK(err) return resp } diff --git a/dashboard/dashapi/dashapi.go b/dashboard/dashapi/dashapi.go index 833dd099f..8b93f0ae2 100644 --- a/dashboard/dashapi/dashapi.go +++ b/dashboard/dashapi/dashapi.go @@ -122,8 +122,13 @@ func (dash *Dashboard) BuilderPoll(manager string) (*BuilderPollResp, error) { // ID must match JobPollResp.ID. type JobPollReq struct { - PatchTestManagers []string - BisectManagers []string + Managers map[string]ManagerJobs +} + +type ManagerJobs struct { + TestPatches bool + BisectCause bool + BisectFix bool } type JobPollResp struct { diff --git a/syz-ci/jobs.go b/syz-ci/jobs.go index faa53cc25..a90d100f9 100644 --- a/syz-ci/jobs.go +++ b/syz-ci/jobs.go @@ -42,23 +42,17 @@ type JobProcessor struct { } func newJobProcessor(cfg *Config, managers []*Manager, stop, shutdownPending chan struct{}) *JobProcessor { - jp := &JobProcessor{ + return &JobProcessor{ cfg: cfg, name: fmt.Sprintf("%v-job", cfg.Name), managers: managers, knownCommits: make(map[string]bool), stop: stop, shutdownPending: shutdownPending, + dash: dashapi.New(cfg.DashboardClient, cfg.DashboardAddr, cfg.DashboardKey), syzkallerRepo: cfg.SyzkallerRepo, syzkallerBranch: cfg.SyzkallerBranch, } - if cfg.EnableJobs { - if cfg.DashboardAddr == "" || cfg.DashboardClient == "" { - panic("enabled_jobs is set but no dashboard info") - } - jp.dash = dashapi.New(cfg.DashboardClient, cfg.DashboardAddr, cfg.DashboardKey) - } - return jp } func (jp *JobProcessor) loop() { @@ -81,9 +75,7 @@ loop: // Otherwise we claim a job, but can't start it for a while. continue loop } - if jp.cfg.EnableJobs { - jp.pollJobs() - } + jp.pollJobs() if time.Since(lastCommitPoll) > commitPollPeriod { jp.pollCommits() lastCommitPoll = time.Now() @@ -97,7 +89,7 @@ loop: func (jp *JobProcessor) pollCommits() { for _, mgr := range jp.managers { - if !mgr.mgrcfg.PollCommits { + if !mgr.mgrcfg.Jobs.PollCommits { continue } if err := jp.pollManagerCommits(mgr); err != nil { @@ -206,17 +198,25 @@ func (jp *JobProcessor) getCommitInfo(mgr *Manager, URL, branch string, commits } func (jp *JobProcessor) pollJobs() { - var patchTestManagers, bisectManagers []string + poll := &dashapi.JobPollReq{ + Managers: make(map[string]dashapi.ManagerJobs), + } for _, mgr := range jp.managers { - patchTestManagers = append(patchTestManagers, mgr.name) - if mgr.mgrcfg.Bisect { - bisectManagers = append(bisectManagers, mgr.name) + if !mgr.mgrcfg.Jobs.TestPatches && + !mgr.mgrcfg.Jobs.BisectCause && + !mgr.mgrcfg.Jobs.BisectFix { + continue + } + poll.Managers[mgr.name] = dashapi.ManagerJobs{ + TestPatches: mgr.mgrcfg.Jobs.TestPatches, + BisectCause: mgr.mgrcfg.Jobs.BisectCause, + BisectFix: mgr.mgrcfg.Jobs.BisectFix, } } - req, err := jp.dash.JobPoll(&dashapi.JobPollReq{ - PatchTestManagers: patchTestManagers, - BisectManagers: bisectManagers, - }) + if len(poll.Managers) == 0 { + return + } + req, err := jp.dash.JobPoll(poll) if err != nil { jp.Errorf("failed to poll jobs: %v", err) return diff --git a/syz-ci/syz-ci.go b/syz-ci/syz-ci.go index 428856696..b81de31f0 100644 --- a/syz-ci/syz-ci.go +++ b/syz-ci/syz-ci.go @@ -91,11 +91,9 @@ type Config struct { // Dir with additional syscall descriptions (.txt and .const files). SyzkallerDescriptions string `json:"syzkaller_descriptions"` // GCS path to upload coverage reports from managers (optional). - CoverUploadPath string `json:"cover_upload_path"` - // Enable patch testing jobs. - //EnableJobs bool `json:"enable_jobs"` - BisectBinDir string `json:"bisect_bin_dir"` - Managers []*ManagerConfig `json:"managers"` + CoverUploadPath string `json:"cover_upload_path"` + BisectBinDir string `json:"bisect_bin_dir"` + Managers []*ManagerConfig `json:"managers"` } type ManagerConfig struct { @@ -112,21 +110,20 @@ type ManagerConfig struct { // File with kernel cmdline values (optional). KernelCmdline string `json:"kernel_cmdline"` // File with sysctl values (e.g. output of sysctl -a, optional). - KernelSysctl string `json:"kernel_sysctl"` - //PollCommits bool `json:"poll_commits"` - //Bisect bool `json:"bisect"` - - // Comma-separated list of job types to do for this manager: - // - test-patch: test proposed fix patches - // - bisect-cause: do cause bisection - // - bisect-fix: do fix bisection - // - poll-commits: poll info about fix commits - Jobs string `json:"bisect"` + KernelSysctl string `json:"kernel_sysctl"` + Jobs ManagerJobs `json:"jobs"` ManagerConfig json.RawMessage `json:"manager_config"` managercfg *mgrconfig.Config } +type ManagerJobs struct { + TestPatches bool `json:"test_patches"` // enable patch testing jobs + PollCommits bool `json:"poll_commits"` // poll info about fix commits + BisectCause bool `json:"bisect_cause"` // do cause bisection + BisectFix bool `json:"bisect_fix"` // do fix bisection +} + func main() { flag.Parse() log.EnableLogCaching(1000, 1<<20) @@ -251,12 +248,6 @@ func loadConfig(filename string) (*Config, error) { if len(cfg.Managers) == 0 { return nil, fmt.Errorf("no managers specified") } - if cfg.EnableJobs && (cfg.DashboardAddr == "" || cfg.DashboardClient == "") { - return nil, fmt.Errorf("enabled_jobs is set but no dashboard info") - } - if cfg.EnableJobs && cfg.BisectBinDir == "" { - return nil, fmt.Errorf("enabled_jobs is set but no bisect_bin_dir") - } // Manager name must not contain dots because it is used as GCE image name prefix. managerNameRe := regexp.MustCompile("^[a-zA-Z0-9-_]{4,64}$") for i, mgr := range cfg.Managers { @@ -270,9 +261,17 @@ func loadConfig(filename string) (*Config, error) { if err != nil { return nil, fmt.Errorf("manager %v: %v", mgr.Name, err) } - if mgr.PollCommits && (cfg.DashboardAddr == "" || mgr.DashboardClient == "") { + if (mgr.Jobs.TestPatches || mgr.Jobs.PollCommits || + mgr.Jobs.BisectCause || mgr.Jobs.BisectFix) && + (cfg.DashboardAddr == "" || cfg.DashboardClient == "") { + return nil, fmt.Errorf("manager %v: has jobs but no dashboard info", mgr.Name) + } + if mgr.Jobs.PollCommits && (cfg.DashboardAddr == "" || mgr.DashboardClient == "") { return nil, fmt.Errorf("manager %v: commit_poll is set but no dashboard info", mgr.Name) } + if (mgr.Jobs.BisectCause || mgr.Jobs.BisectFix) && cfg.BisectBinDir == "" { + return nil, fmt.Errorf("manager %v: enabled bisection but no bisect_bin_dir", mgr.Name) + } mgr.managercfg = managercfg managercfg.Name = cfg.Name + "-" + mgr.Name managercfg.Syzkaller = filepath.FromSlash("syzkaller/current") |
