aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-07-17 18:08:56 +0200
committerAleksandr Nogikh <nogikh@google.com>2023-07-25 08:23:29 +0000
commite06c669f49a06146914b04a1fbbdd21a0bf1d7b1 (patch)
treedc4a1b32a67606741f49d7cbf6a07055b919b86c
parent67db6ccb7357714a0299fe9f3d192a26bd32ac4e (diff)
all: support cross-tree fix bisection
If tree origin assessment code has identified that the bug is not reproducible in a tree from which we merge/cherry-pick commits, use fix bisection to identify that commit. This can e.g. be used to find fixing commits that were not backported from Linux kernel mainline into LTS branches. In case of bisection errors, re-do such jobs every 30 days. Remember in the Bug structure whether there's a fix candidate and return the details in the full bug info API query.
-rw-r--r--dashboard/app/config.go2
-rw-r--r--dashboard/app/entities.go14
-rw-r--r--dashboard/app/jobs.go82
-rw-r--r--dashboard/app/reporting.go19
-rw-r--r--dashboard/app/tree.go153
-rw-r--r--dashboard/app/tree_test.go116
-rw-r--r--dashboard/dashapi/dashapi.go28
-rw-r--r--syz-ci/jobs.go31
8 files changed, 404 insertions, 41 deletions
diff --git a/dashboard/app/config.go b/dashboard/app/config.go
index 1ee059b80..88230f9c6 100644
--- a/dashboard/app/config.go
+++ b/dashboard/app/config.go
@@ -263,6 +263,8 @@ type KernelRepoLink struct {
Alias string
// Whether commits from the other repository merged or cherry-picked.
Merge bool
+ // Whether syzbot should try to fix bisect the bug in the Alias tree.
+ BisectFixes bool
}
type CCConfig struct {
diff --git a/dashboard/app/entities.go b/dashboard/app/entities.go
index 812d31e59..b64e66c03 100644
--- a/dashboard/app/entities.go
+++ b/dashboard/app/entities.go
@@ -123,6 +123,8 @@ type Bug struct {
Labels []BugLabel
DiscussionInfo []BugDiscussionInfo
TreeTests BugTreeTestInfo
+ // FixCandidateJob holds the key of the latest successful cross-tree fix bisection job.
+ FixCandidateJob string
}
type BugTreeTestInfo struct {
@@ -559,6 +561,10 @@ type Job struct {
MergeBaseRepo string
MergeBaseBranch string
+ // By default, bisection starts from the revision of the associated crash.
+ // The BisectFrom field can override this.
+ BisectFrom string
+
// Result of execution:
CrashTitle string // if empty, we did not hit crash during testing
CrashLog int64 // reference to CrashLog text entity
@@ -573,6 +579,10 @@ type Job struct {
InvalidatedBy string // user who marked this bug as invalid, empty by default
}
+func (job *Job) IsBisection() bool {
+ return job.Type == JobBisectCause || job.Type == JobBisectFix
+}
+
func (job *Job) IsFinished() bool {
return !job.Finished.IsZero()
}
@@ -610,6 +620,10 @@ func (job *Job) isUnreliableBisect() bool {
job.Flags&dashapi.BisectResultIgnore != 0
}
+func (job *Job) IsCrossTree() bool {
+ return job.MergeBaseRepo != "" && job.IsBisection()
+}
+
// Text holds text blobs (crash logs, reports, reproducers, etc).
type Text struct {
Namespace string
diff --git a/dashboard/app/jobs.go b/dashboard/app/jobs.go
index 2567cb30e..cf5698e99 100644
--- a/dashboard/app/jobs.go
+++ b/dashboard/app/jobs.go
@@ -435,6 +435,7 @@ func jobFromBugSample(c context.Context, managers map[string]dashapi.ManagerJobs
map[string]dashapi.ManagerJobs) (*Job, *db.Key, error){
createPatchRetestingJobs,
createTreeTestJobs,
+ createTreeBisectionJobs,
}
r.Shuffle(len(funcs), func(i, j int) { funcs[i], funcs[j] = funcs[j], funcs[i] })
for _, f := range funcs {
@@ -446,6 +447,35 @@ func jobFromBugSample(c context.Context, managers map[string]dashapi.ManagerJobs
return nil, nil, nil
}
+func createTreeBisectionJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key,
+ managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) {
+ log.Infof(c, "createTreeBisectionJobs is called for %d bugs", len(bugs))
+ const maxProcess = 3
+ processed := 0
+ for _, bug := range bugs {
+ if bug.FixCandidateJob != "" {
+ continue
+ }
+ if processed >= maxProcess {
+ break
+ }
+ any := false
+ for _, mgr := range bug.HappenedOn {
+ newMgr, _ := activeManager(mgr, bug.Namespace)
+ any = any || managers[newMgr].BisectFix
+ }
+ if !any {
+ continue
+ }
+ processed++
+ job, key, err := crossTreeBisection(c, bug, managers)
+ if job != nil || err != nil {
+ return job, key, err
+ }
+ }
+ return nil, nil, nil
+}
+
func createTreeTestJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key,
managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) {
takeBugs := 5
@@ -814,20 +844,23 @@ func createJobResp(c context.Context, job *Job, jobKey *db.Key) (*dashapi.JobPol
return nil, true, nil
}
resp := &dashapi.JobPollResp{
- ID: jobID,
- Manager: job.Manager,
- KernelRepo: job.KernelRepo,
- KernelBranch: job.KernelBranch,
- MergeBaseRepo: job.MergeBaseRepo,
- MergeBaseBranch: job.MergeBaseBranch,
- KernelCommit: build.KernelCommit,
- KernelCommitTitle: build.KernelCommitTitle,
- KernelConfig: kernelConfig,
- SyzkallerCommit: build.SyzkallerCommit,
- Patch: patch,
- ReproOpts: crash.ReproOpts,
- ReproSyz: reproSyz,
- ReproC: reproC,
+ ID: jobID,
+ Manager: job.Manager,
+ KernelRepo: job.KernelRepo,
+ KernelBranch: job.KernelBranch,
+ MergeBaseRepo: job.MergeBaseRepo,
+ MergeBaseBranch: job.MergeBaseBranch,
+ KernelCommit: job.BisectFrom,
+ KernelConfig: kernelConfig,
+ SyzkallerCommit: build.SyzkallerCommit,
+ Patch: patch,
+ ReproOpts: crash.ReproOpts,
+ ReproSyz: reproSyz,
+ ReproC: reproC,
+ }
+ if resp.KernelCommit == "" {
+ resp.KernelCommit = build.KernelCommit
+ resp.KernelCommitTitle = build.KernelCommitTitle
}
switch job.Type {
case JobTestPatch:
@@ -1057,12 +1090,20 @@ func doneJob(c context.Context, req *dashapi.JobDoneReq) error {
if err != nil {
return err
}
+ return postJob(c, jobKey, job)
+}
+
+func postJob(c context.Context, jobKey *db.Key, job *Job) error {
if job.TreeOrigin {
- err = treeOriginJobDone(c, jobKey, job)
+ err := treeOriginJobDone(c, jobKey, job)
if err != nil {
- return fmt.Errorf("job %v: failed to execute tree origin handlers: %w", jobID, err)
+ return fmt.Errorf("job %v: failed to execute tree origin handlers: %w", jobKey, err)
}
}
+ err := doneCrossTreeBisection(c, jobKey, job)
+ if err != nil {
+ return fmt.Errorf("job %s: cross tree bisection handlers failed: %w", jobKey, err)
+ }
return nil
}
@@ -1155,6 +1196,11 @@ func pollCompletedJobs(c context.Context, typ string) ([]*dashapi.BugReport, err
if job.Type == JobBisectFix && len(job.Commits) != 1 {
continue
}
+ // Don't report cross-tree bisection results for now as we need to first understand
+ // how reliable their results are.
+ if job.IsCrossTree() {
+ continue
+ }
// If the bug is already known to be fixed, invalid or duplicate, do not report the bisection results.
if job.Type == JobBisectCause || job.Type == JobBisectFix {
bug := new(Bug)
@@ -1619,6 +1665,10 @@ func (b *bugJobs) bestBisection() *bugJob {
if j.job.InvalidatedBy != "" {
continue
}
+ if j.job.MergeBaseRepo != "" {
+ // It was a cross-tree bisection.
+ continue
+ }
return j
}
return nil
diff --git a/dashboard/app/reporting.go b/dashboard/app/reporting.go
index 599eb1a79..3917adb56 100644
--- a/dashboard/app/reporting.go
+++ b/dashboard/app/reporting.go
@@ -1358,6 +1358,13 @@ func loadFullBugInfo(c context.Context, bug *Bug, bugKey *db.Key,
return nil, err
}
}
+ // Query the fix candidate.
+ if bug.FixCandidateJob != "" {
+ ret.FixCandidate, err = prepareFixCandidateReport(c, bug, reporting)
+ if err != nil {
+ return nil, err
+ }
+ }
// Query similar bugs.
similar, err := loadSimilarBugs(c, bug)
if err != nil {
@@ -1402,6 +1409,18 @@ func loadFullBugInfo(c context.Context, bug *Bug, bugKey *db.Key,
return ret, nil
}
+func prepareFixCandidateReport(c context.Context, bug *Bug, reporting *Reporting) (*dashapi.BugReport, error) {
+ job, jobKey, err := fetchJob(c, bug.FixCandidateJob)
+ if err != nil {
+ return nil, err
+ }
+ ret, err := createBugReportForJob(c, job, jobKey, reporting.Config)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create the job bug report: %w", err)
+ }
+ return ret, nil
+}
+
func prepareBisectionReport(c context.Context, bug *Bug, jobType JobType,
reporting *Reporting) (*dashapi.BugReport, error) {
bisectionJob, err := queryBestBisection(c, bug, jobType)
diff --git a/dashboard/app/tree.go b/dashboard/app/tree.go
index 2331ab9d2..07c289ea7 100644
--- a/dashboard/app/tree.go
+++ b/dashboard/app/tree.go
@@ -713,6 +713,128 @@ func treeTestJobs(c context.Context, bug *Bug) ([]*dashapi.JobInfo, error) {
return ret, nil
}
+// Create a cross-tree bisection job (if needed).
+func crossTreeBisection(c context.Context, bug *Bug,
+ managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) {
+ repoGraph, err := makeRepoGraph(getKernelRepos(c, bug.Namespace))
+ if err != nil {
+ return nil, nil, err
+ }
+ bugJobs := &lazyJobList{
+ c: c,
+ bug: bug,
+ jobType: JobBisectFix,
+ }
+ var job *Job
+ var jobKey *db.Key
+ err = repoGraph.forEachEdge(func(from, to *repoNode, info KernelRepoLink) error {
+ if jobKey != nil {
+ return nil
+ }
+ if !info.BisectFixes {
+ return nil
+ }
+ log.Infof(c, "%s: considering cross-tree bisection %s/%s",
+ bug.displayTitle(), from.repo.Alias, to.repo.Alias)
+ _, crashJob := bug.findResult(c, to.repo, wantNewAny{}, runOnHEAD{})
+ if crashJob.CrashTitle == "" {
+ // The bug is already fixed on the target tree.
+ return nil
+ }
+ crashBuild, err := loadBuild(c, bug.Namespace, crashJob.BuildID)
+ if err != nil {
+ return err
+ }
+ manager, _ := activeManager(crashJob.Manager, crashJob.Namespace)
+ if !managers[manager].BisectFix {
+ return nil
+ }
+ _, successJob := bug.findResult(c, from.repo, wantNewAny{}, runOnHEAD{})
+ if successJob.CrashTitle != "" {
+ // The kernel tree is still crashed by the repro.
+ return nil
+ }
+ newJob := &Job{
+ Type: JobBisectFix,
+ Created: timeNow(c),
+ Namespace: bug.Namespace,
+ Manager: crashJob.Manager,
+ BisectFrom: crashBuild.KernelCommit,
+ KernelRepo: from.repo.URL,
+ KernelBranch: from.repo.Branch,
+ MergeBaseRepo: to.repo.URL,
+ MergeBaseBranch: to.repo.Branch,
+ BugTitle: bug.displayTitle(),
+ CrashID: crashJob.CrashID,
+ }
+ // It's expected that crossTreeBisection is not concurrently called with the same
+ // manager list.
+ prevJob, err := bugJobs.lastMatch(newJob)
+ if err != nil {
+ return err
+ }
+ const repeatPeriod = time.Hour * 24 * 30
+ if prevJob != nil && (prevJob.Error == 0 ||
+ prevJob.Finished.After(timeNow(c).Add(-repeatPeriod))) {
+ // The job is already pending or failed recently. Skip.
+ return nil
+ }
+ job = newJob
+ jobKey, err = saveJob(c, newJob, bug.key(c))
+ return err
+ })
+ return job, jobKey, err
+}
+
+type lazyJobList struct {
+ c context.Context
+ bug *Bug
+ jobType JobType
+ jobs *bugJobs
+}
+
+func (list *lazyJobList) lastMatch(job *Job) (*Job, error) {
+ if list.jobs == nil {
+ var err error
+ list.jobs, err = queryBugJobs(list.c, list.bug, list.jobType)
+ if err != nil {
+ return nil, err
+ }
+ }
+ var best *Job
+ for _, item := range list.jobs.all() {
+ otherJob := item.job
+ same := otherJob.Manager == job.Manager &&
+ otherJob.KernelRepo == job.KernelRepo &&
+ otherJob.KernelBranch == job.KernelBranch &&
+ otherJob.CrashID == job.CrashID &&
+ otherJob.MergeBaseRepo == job.MergeBaseRepo &&
+ otherJob.MergeBaseBranch == job.MergeBaseBranch
+ if !same {
+ continue
+ }
+ if best == nil || best.Created.Before(otherJob.Created) {
+ best = otherJob
+ }
+ }
+ return best, nil
+}
+
+func doneCrossTreeBisection(c context.Context, jobKey *db.Key, job *Job) error {
+ if job.Type != JobBisectFix || job.MergeBaseRepo == "" {
+ // Not a cross tree bisection.
+ return nil
+ }
+ if job.Error != 0 || job.isUnreliableBisect() || len(job.Commits) != 1 {
+ // The result is not interesting.
+ return nil
+ }
+ return updateSingleBug(c, jobKey.Parent(), func(bug *Bug) error {
+ bug.FixCandidateJob = jobKey.Encode()
+ return nil
+ })
+}
+
type repoNode struct {
repo KernelRepo
edges []repoEdge
@@ -720,7 +842,7 @@ type repoNode struct {
type repoEdge struct {
in bool
- merge bool
+ info KernelRepoLink
other *repoNode
}
@@ -743,8 +865,8 @@ func makeRepoGraph(repos []KernelRepo) (*repoGraph, error) {
if g.nodes[link.Alias] == nil {
return nil, fmt.Errorf("no repo with alias %q", link.Alias)
}
- g.nodes[repo.Alias].addEdge(true, link.Merge, g.nodes[link.Alias])
- g.nodes[link.Alias].addEdge(false, link.Merge, g.nodes[repo.Alias])
+ g.nodes[repo.Alias].addEdge(true, link, g.nodes[link.Alias])
+ g.nodes[link.Alias].addEdge(false, link, g.nodes[repo.Alias])
}
}
for alias, node := range g.nodes {
@@ -774,6 +896,21 @@ func (g *repoGraph) nodeByAlias(alias string) *repoNode {
return nil
}
+func (g *repoGraph) forEachEdge(cb func(from, to *repoNode, info KernelRepoLink) error) error {
+ for _, node := range g.nodes {
+ for _, e := range node.edges {
+ if !e.in {
+ continue
+ }
+ err := cb(e.other, node, e.info)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
// reachable returns a map *repoNode -> bool (whether commits are merged).
func (n *repoNode) reachable(in bool) map[*repoNode]bool {
ret := map[*repoNode]bool{}
@@ -787,14 +924,14 @@ func (n *repoNode) reachableMerged(in, onlyMerge bool, ret map[*repoNode]bool) {
var dfs func(*repoNode, bool)
dfs = func(node *repoNode, merge bool) {
for _, edge := range node.edges {
- if edge.in != in || onlyMerge && !edge.merge {
+ if edge.in != in || onlyMerge && !edge.info.Merge {
continue
}
if _, ok := ret[edge.other]; ok {
continue
}
- ret[edge.other] = merge && edge.merge
- dfs(edge.other, merge && edge.merge)
+ ret[edge.other] = merge && edge.info.Merge
+ dfs(edge.other, merge && edge.info.Merge)
}
}
dfs(n, true)
@@ -808,10 +945,10 @@ func (n *repoNode) allReachable() map[*repoNode]bool {
return ret
}
-func (n *repoNode) addEdge(in, merge bool, other *repoNode) {
+func (n *repoNode) addEdge(in bool, info KernelRepoLink, other *repoNode) {
n.edges = append(n.edges, repoEdge{
in: in,
- merge: merge,
+ info: info,
other: other,
})
}
diff --git a/dashboard/app/tree_test.go b/dashboard/app/tree_test.go
index 79f2dfe08..8fef51865 100644
--- a/dashboard/app/tree_test.go
+++ b/dashboard/app/tree_test.go
@@ -153,6 +153,117 @@ func TestTreeOriginLts(t *testing.T) {
ctx.ctx.expectNoEmail()
}
+func TestTreeOriginLtsBisection(t *testing.T) {
+ c := NewCtx(t)
+ defer c.Close()
+
+ ctx := setUpTreeTest(c, downstreamUpstreamRepos)
+ ctx.uploadBug(`https://downstream.repo/repo`, `master`, dashapi.ReproLevelC)
+ ctx.entries = []treeTestEntry{
+ {
+ alias: `downstream`,
+ results: []treeTestEntryPeriod{{fromDay: 0, result: treeTestCrash}},
+ },
+ {
+ alias: `lts`,
+ mergeAlias: `downstream`,
+ results: []treeTestEntryPeriod{
+ {
+ fromDay: 0,
+ result: treeTestCrash,
+ commit: "badc0ffee",
+ },
+ },
+ },
+ {
+ alias: `upstream`,
+ results: []treeTestEntryPeriod{{fromDay: 0, result: treeTestOK}},
+ },
+ }
+ ctx.jobTestDays = []int{10}
+ ctx.moveToDay(10)
+ ctx.ensureLabels(`origin:lts`)
+ ctx.reportToEmail()
+ ctx.ctx.advanceTime(time.Hour)
+
+ // Expect a cross tree bisection request.
+ job := ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true})
+ assert.Equal(t, dashapi.JobBisectFix, job.Type)
+ assert.Equal(t, "https://upstream.repo/repo", job.KernelRepo)
+ assert.Equal(t, "upstream-master", job.KernelBranch)
+ assert.Equal(t, "https://lts.repo/repo", job.MergeBaseRepo)
+ assert.Equal(t, "lts-master", job.MergeBaseBranch)
+ assert.Equal(t, "badc0ffee", job.KernelCommit)
+ ctx.ctx.advanceTime(time.Hour)
+
+ // Make sure we don't create the same job twice.
+ job2 := ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true})
+ assert.Equal(t, "", job2.ID)
+ ctx.ctx.advanceTime(time.Hour)
+
+ // Let the bisection fail.
+ done := &dashapi.JobDoneReq{
+ ID: job.ID,
+ Log: []byte("bisect log"),
+ Error: []byte("bisect error"),
+ }
+ c.expectOK(ctx.client.JobDone(done))
+ ctx.ctx.advanceTime(time.Hour)
+
+ // Ensure there are no new bisection requests.
+ job = ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true})
+ assert.Equal(t, job.ID, "")
+
+ // Wait for the cooldown and request the job once more.
+ ctx.ctx.advanceTime(15 * 24 * time.Hour)
+ ctx.uploadBug(`https://downstream.repo/repo`, `master`, dashapi.ReproLevelC)
+ ctx.ctx.advanceTime(15 * 24 * time.Hour)
+ job = ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true})
+ assert.Equal(t, job.KernelRepo, "https://upstream.repo/repo")
+ assert.Equal(t, job.KernelCommit, "badc0ffee")
+
+ // This time pretend we have found the commit.
+ build := testBuild(2)
+ build.KernelRepo = job.KernelRepo
+ build.KernelBranch = job.KernelBranch
+ build.KernelCommit = "deadf00d"
+ done = &dashapi.JobDoneReq{
+ ID: job.ID,
+ Build: *build,
+ Log: []byte("bisect log 2"),
+ CrashTitle: "bisect crash title",
+ CrashLog: []byte("bisect crash log"),
+ CrashReport: []byte("bisect crash report"),
+ Commits: []dashapi.Commit{
+ {
+ Hash: "deadf00d",
+ Title: "kernel: fix a bug",
+ },
+ },
+ }
+ done.Build.ID = job.ID
+ ctx.ctx.advanceTime(time.Hour)
+ c.expectOK(ctx.client.JobDone(done))
+
+ // Ensure the job is no longer created.
+ ctx.ctx.advanceTime(time.Hour)
+ job = ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true})
+ assert.Equal(t, job.ID, "")
+
+ // No emails are to be sent (for now).
+ ctx.ctx.expectNoEmail()
+
+ info := ctx.fullBugInfo()
+ assert.NotNil(t, info.FixCandidate)
+ fix := info.FixCandidate
+ assert.Equal(t, "upstream", fix.KernelRepoAlias)
+ assert.NotNil(t, fix.BisectFix)
+ assert.NotNil(t, fix.BisectFix.Commit)
+ commit := fix.BisectFix.Commit
+ assert.Equal(t, "deadf00d", commit.Hash)
+ assert.Equal(t, "kernel: fix a bug", commit.Title)
+}
+
func TestTreeOriginErrors(t *testing.T) {
c := NewCtx(t)
defer c.Close()
@@ -220,8 +331,9 @@ var downstreamUpstreamRepos = []KernelRepo{
LabelIntroduced: `lts`,
CommitInflow: []KernelRepoLink{
{
- Alias: `upstream`,
- Merge: false,
+ Alias: `upstream`,
+ Merge: false,
+ BisectFixes: true,
},
},
},
diff --git a/dashboard/dashapi/dashapi.go b/dashboard/dashapi/dashapi.go
index af4b89e32..28a805edd 100644
--- a/dashboard/dashapi/dashapi.go
+++ b/dashboard/dashapi/dashapi.go
@@ -176,13 +176,16 @@ func (m ManagerJobs) Any() bool {
}
type JobPollResp struct {
- ID string
- Type JobType
- Manager string
- KernelRepo string
- KernelBranch string
- MergeBaseRepo string
- MergeBaseBranch string
+ ID string
+ Type JobType
+ Manager string
+ KernelRepo string
+ // KernelBranch is used for patch testing and serves as the current HEAD
+ // for bisections.
+ KernelBranch string
+ MergeBaseRepo string
+ MergeBaseBranch string
+ // Bisection starts from KernelCommit.
KernelCommit string
KernelCommitTitle string
KernelConfig []byte
@@ -785,11 +788,12 @@ type LoadFullBugReq struct {
}
type FullBugInfo struct {
- SimilarBugs []*SimilarBugInfo
- BisectCause *BugReport
- BisectFix *BugReport
- Crashes []*BugReport
- TreeJobs []*JobInfo
+ SimilarBugs []*SimilarBugInfo
+ BisectCause *BugReport
+ BisectFix *BugReport
+ Crashes []*BugReport
+ TreeJobs []*JobInfo
+ FixCandidate *BugReport
}
type SimilarBugInfo struct {
diff --git a/syz-ci/jobs.go b/syz-ci/jobs.go
index a0f838e7b..cc7c12908 100644
--- a/syz-ci/jobs.go
+++ b/syz-ci/jobs.go
@@ -397,7 +397,10 @@ func (jp *JobProcessor) process(job *Job) *dashapi.JobDoneReq {
jp.Errorf("%s", err)
return job.resp
}
-
+ if req.KernelRepo == "" {
+ req.KernelRepo = mgr.mgrcfg.Repo
+ req.KernelRepo = mgr.mgrcfg.Branch
+ }
required := []struct {
name string
ok bool
@@ -455,6 +458,10 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error {
return fmt.Errorf("failed to read baseline config: %w", err)
}
}
+ err := jp.prepareBisectionRepo(mgrcfg, req)
+ if err != nil {
+ return err
+ }
trace := new(bytes.Buffer)
cfg := &bisect.Config{
Trace: &debugtracer.GenericTracer{
@@ -485,8 +492,8 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error {
Linker: mgr.mgrcfg.Linker,
Ccache: jp.cfg.Ccache,
Kernel: bisect.KernelConfig{
- Repo: mgr.mgrcfg.Repo,
- Branch: mgr.mgrcfg.Branch,
+ Repo: req.KernelRepo,
+ Branch: req.KernelBranch,
Commit: req.KernelCommit,
CommitTitle: req.KernelCommitTitle,
Cmdline: mgr.mgrcfg.KernelCmdline,
@@ -505,6 +512,7 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error {
Syz: req.ReproSyz,
C: req.ReproC,
},
+ CrossTree: req.MergeBaseRepo != "",
Manager: mgrcfg,
BuildSemaphore: buildSem,
TestSemaphore: testSem,
@@ -656,6 +664,23 @@ func (jp *JobProcessor) testPatch(job *Job, mgrcfg *mgrconfig.Config) error {
return nil
}
+func (jp *JobProcessor) prepareBisectionRepo(mgrcfg *mgrconfig.Config, req *dashapi.JobPollResp) error {
+ if req.MergeBaseRepo == "" {
+ // No need to.
+ return nil
+ }
+ repo, err := vcs.NewRepo(mgrcfg.TargetOS, mgrcfg.Type, mgrcfg.KernelSrc)
+ if err != nil {
+ return fmt.Errorf("failed to create kernel repo: %w", err)
+ }
+ _, err = checkoutKernelOrCommit(repo, req.MergeBaseRepo, req.MergeBaseBranch)
+ if err != nil {
+ return fmt.Errorf("failed to checkout the merge base repo %v on %v: %w",
+ req.MergeBaseRepo, req.MergeBaseBranch, err)
+ }
+ return nil
+}
+
func (jp *JobProcessor) checkoutJobCommit(job *Job, repo vcs.Repo) (*vcs.Commit, error) {
req, resp := job.req, job.resp
var kernelCommit *vcs.Commit