github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/covermerger/provider_monorepo.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 covermerger 5 6 import ( 7 "fmt" 8 "path/filepath" 9 "sync" 10 11 "github.com/google/syzkaller/pkg/log" 12 "github.com/google/syzkaller/pkg/vcs" 13 "github.com/google/syzkaller/sys/targets" 14 ) 15 16 type FileVersProvider interface { 17 GetFileVersions(targetFilePath string, repoCommits ...RepoCommit, 18 ) (FileVersions, error) 19 } 20 21 type monoRepo struct { 22 repoCommits map[RepoCommit]struct{} 23 mu sync.RWMutex 24 repo vcs.Repo 25 } 26 27 type FileVersions map[RepoCommit]string 28 29 func (mr *monoRepo) GetFileVersions(targetFilePath string, repoCommits ...RepoCommit, 30 ) (FileVersions, error) { 31 mr.mu.RLock() 32 if !mr.allRepoCommitsPresent(repoCommits) { 33 mr.mu.RUnlock() 34 mr.cloneCommits(repoCommits) 35 mr.mu.RLock() 36 } 37 defer mr.mu.RUnlock() 38 res := make(FileVersions) 39 for _, repoCommit := range repoCommits { 40 fileBytes, err := mr.repo.Object(targetFilePath, repoCommit.Commit) 41 // It is ok if some file doesn't exist. It means we have repo FS diff. 42 // Or the upstream commit doesn't exist anymore 43 if err != nil { 44 log.Logf(1, "repo.Object(%s, %s) error: %s", targetFilePath, repoCommit.Commit, err.Error()) 45 continue 46 } 47 res[repoCommit] = string(fileBytes) 48 } 49 return res, nil 50 } 51 52 func (mr *monoRepo) allRepoCommitsPresent(repoCommits []RepoCommit) bool { 53 for _, repoCommit := range repoCommits { 54 if _, exists := mr.repoCommits[repoCommit]; !exists { 55 return false 56 } 57 } 58 return true 59 } 60 61 func (mr *monoRepo) addRepoCommit(repoCommit RepoCommit) { 62 log.Logf(0, "cloning repo: %s, commit %s", repoCommit.Repo, repoCommit.Commit) 63 mr.repoCommits[repoCommit] = struct{}{} 64 repo, commit := repoCommit.Repo, repoCommit.Commit 65 if repo == "" || commit == "" { 66 panic("repo and commit are needed") 67 } 68 if _, err := mr.repo.CheckoutCommit(repo, commit); err != nil { 69 log.Logf(0, "failed to CheckoutCommit(repo %s, commit %s): %s", repo, commit, err.Error()) 70 } 71 } 72 73 func MakeMonoRepo(workdir string) FileVersProvider { 74 repoPath := filepath.Join(workdir, "repos", "linux_kernels") 75 mr := &monoRepo{ 76 repoCommits: map[RepoCommit]struct{}{}, 77 } 78 var err error 79 if mr.repo, err = vcs.NewRepo(targets.Linux, "none", repoPath); err != nil { 80 panic(fmt.Sprintf("failed to create/open repo at %s: %s", repoPath, err.Error())) 81 } 82 return mr 83 } 84 85 func (mr *monoRepo) cloneCommits(repoCommits []RepoCommit) { 86 mr.mu.Lock() 87 defer mr.mu.Unlock() 88 for _, repoCommit := range repoCommits { 89 if _, exists := mr.repoCommits[repoCommit]; exists { 90 continue 91 } 92 commitExistsInRepo, err := mr.repo.CommitExists(repoCommit.Commit) 93 if err != nil { 94 log.Logf(0, "can't check CommitExists: %s", err.Error()) 95 } 96 if commitExistsInRepo { 97 mr.repoCommits[repoCommit] = struct{}{} 98 continue 99 } 100 mr.addRepoCommit(repoCommit) 101 } 102 }