github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/pkg/triage/commit.go (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package triage 5 6 import ( 7 "time" 8 9 "github.com/google/syzkaller/pkg/debugtracer" 10 "github.com/google/syzkaller/pkg/vcs" 11 "github.com/google/syzkaller/syz-cluster/pkg/api" 12 ) 13 14 // TODO: Some further improvements: 15 // 1. Consider the blob hashes incorporated into the git diff. These may restrict the set of base commits. 16 // 2. Add support for experimental sessions: these may be way behind the current HEAD. 17 18 type TreeOps interface { 19 HeadCommit(tree *api.Tree) (*vcs.Commit, error) 20 ApplySeries(commit string, patches [][]byte) error 21 } 22 23 type CommitSelector struct { 24 ops TreeOps 25 tracer debugtracer.DebugTracer 26 } 27 28 func NewCommitSelector(ops TreeOps, tracer debugtracer.DebugTracer) *CommitSelector { 29 return &CommitSelector{ops: ops, tracer: tracer} 30 } 31 32 type SelectResult struct { 33 Commit string 34 Reason string // Set if Commit is empty. 35 } 36 37 const ( 38 reasonSeriesTooOld = "series lags behind the current HEAD too much" 39 reasonNotApplies = "series does not apply" 40 ) 41 42 // Select returns the best matching commit hash. 43 func (cs *CommitSelector) Select(series *api.Series, tree *api.Tree, lastBuild *api.Build) (SelectResult, error) { 44 head, err := cs.ops.HeadCommit(tree) 45 if err != nil || head == nil { 46 return SelectResult{}, err 47 } 48 cs.tracer.Log("current HEAD: %q (commit date: %v)", head.Hash, head.CommitDate) 49 // If the series is already too old, it may be incompatible even if it applies cleanly. 50 const seriesLagsBehind = time.Hour * 24 * 7 51 if diff := head.CommitDate.Sub(series.PublishedAt); series.PublishedAt.Before(head.CommitDate) && 52 diff > seriesLagsBehind { 53 cs.tracer.Log("the series is too old: %v before the HEAD", diff) 54 return SelectResult{Reason: reasonSeriesTooOld}, nil 55 } 56 57 // Algorithm: 58 // 1. If the last successful build is sufficiently new, prefer it over the last master. 59 // We should it be renewing it regularly, so the commit should be quite up to date. 60 // 2. If the last build is too old / the series does not apply, give a chance to the 61 // current HEAD. 62 63 var hashes []string 64 if lastBuild != nil { 65 // Check if the commit is still good enough. 66 if diff := head.CommitDate.Sub(lastBuild.CommitDate); diff > seriesLagsBehind { 67 cs.tracer.Log("the last successful build is already too old: %v, skipping", diff) 68 } else { 69 hashes = append(hashes, lastBuild.CommitHash) 70 } 71 } 72 for _, hash := range append(hashes, head.Hash) { 73 cs.tracer.Log("considering %q", hash) 74 err := cs.ops.ApplySeries(hash, series.PatchBodies()) 75 if err == nil { 76 cs.tracer.Log("series can be applied to %q", hash) 77 return SelectResult{Commit: hash}, nil 78 } else { 79 cs.tracer.Log("failed to apply to %q: %v", hash, err) 80 } 81 } 82 return SelectResult{Reason: reasonNotApplies}, nil 83 }