aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/vcs/linux.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-03-12 12:22:13 +0100
committerDmitry Vyukov <dvyukov@google.com>2019-03-17 18:06:44 +0100
commit5ed211ca96c6e6ab96fad7b5a495d81178ee98eb (patch)
tree352711e48e64791cda1615ca2bd6611588a23209 /pkg/vcs/linux.go
parent5958caeafcf3d467009ae984a9be8302bf852a50 (diff)
pkg/vcs: refactor bisection support
In preparation for syz-ci bisection: - move bisection function into a separate interface they look out of place in vcs.Repo because most OSes don't implement it and most users don't case - extract author name and more CC emails for commits - move linux-specific PreviousReleaseTags into linux.go - fix inconclusive bisection (more than 1 potential commits) - add tests fr bisection - add maintainers returned from get_maintainers.pl for commits that don't have enough emails (e.g. only author email) Update #501
Diffstat (limited to 'pkg/vcs/linux.go')
-rw-r--r--pkg/vcs/linux.go154
1 files changed, 154 insertions, 0 deletions
diff --git a/pkg/vcs/linux.go b/pkg/vcs/linux.go
new file mode 100644
index 000000000..f8d97d60e
--- /dev/null
+++ b/pkg/vcs/linux.go
@@ -0,0 +1,154 @@
+// Copyright 2019 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package vcs
+
+import (
+ "bytes"
+ "io"
+ "net/mail"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/google/syzkaller/pkg/email"
+ "github.com/google/syzkaller/pkg/osutil"
+)
+
+type linux struct {
+ *git
+}
+
+func newLinux(dir string) *linux {
+ ignoreCC := map[string]bool{
+ "stable@vger.kernel.org": true,
+ }
+ return &linux{
+ git: newGit(dir, ignoreCC),
+ }
+}
+
+func (ctx *linux) Bisect(bad, good string, trace io.Writer, pred func() (BisectResult, error)) ([]*Commit, error) {
+ commits, err := ctx.git.Bisect(bad, good, trace, pred)
+ if len(commits) == 1 {
+ ctx.addMaintainers(commits[0])
+ }
+ return commits, err
+}
+
+func (ctx *linux) addMaintainers(com *Commit) {
+ if len(com.CC) > 3 {
+ return
+ }
+ list := ctx.getMaintainers(com.Hash, false)
+ if len(list) < 3 {
+ list = ctx.getMaintainers(com.Hash, true)
+ }
+ com.CC = email.MergeEmailLists(com.CC, list)
+}
+
+func (ctx *linux) getMaintainers(hash string, blame bool) []string {
+ args := "git show " + hash + " | " +
+ filepath.FromSlash("scripts/get_maintainer.pl") + " --no-n --no-rolestats"
+ if blame {
+ args += " --git-blame"
+ }
+ output, err := osutil.RunCmd(time.Minute, ctx.git.dir, "bash", "-c", args)
+ if err != nil {
+ return nil
+ }
+ var list []string
+ for _, line := range strings.Split(string(output), "\n") {
+ addr, err := mail.ParseAddress(line)
+ if err != nil {
+ continue
+ }
+ list = append(list, strings.ToLower(addr.Address))
+ }
+ return list
+}
+
+func (ctx *linux) PreviousReleaseTags(commit string) ([]string, error) {
+ output, err := runSandboxed(ctx.dir, "git", "tag", "--no-contains", commit, "--merged", commit, "v*.*")
+ if err != nil {
+ return nil, err
+ }
+ tags, err := gitParseReleaseTags(output)
+ if err != nil {
+ return nil, err
+ }
+ for i, tag := range tags {
+ if tag == "v3.8" {
+ // v3.8 does not work with modern perl, and as we go further in history
+ // make stops to work, then binutils, glibc, etc. So we stop at v3.8.
+ // Up to that point we only need an ancient gcc.
+ tags = tags[:i]
+ break
+ }
+ }
+ return tags, nil
+}
+
+func gitParseReleaseTags(output []byte) ([]string, error) {
+ var tags []string
+ for _, tag := range bytes.Split(output, []byte{'\n'}) {
+ if releaseTagRe.Match(tag) && gitReleaseTagToInt(string(tag)) != 0 {
+ tags = append(tags, string(tag))
+ }
+ }
+ sort.Slice(tags, func(i, j int) bool {
+ return gitReleaseTagToInt(tags[i]) > gitReleaseTagToInt(tags[j])
+ })
+ return tags, nil
+}
+
+func gitReleaseTagToInt(tag string) uint64 {
+ matches := releaseTagRe.FindStringSubmatchIndex(tag)
+ v1, err := strconv.ParseUint(tag[matches[2]:matches[3]], 10, 64)
+ if err != nil {
+ return 0
+ }
+ v2, err := strconv.ParseUint(tag[matches[4]:matches[5]], 10, 64)
+ if err != nil {
+ return 0
+ }
+ var v3 uint64
+ if matches[6] != -1 {
+ v3, err = strconv.ParseUint(tag[matches[6]:matches[7]], 10, 64)
+ if err != nil {
+ return 0
+ }
+ }
+ return v1*1e6 + v2*1e3 + v3
+}
+
+func (ctx *linux) EnvForCommit(commit string, kernelConfig []byte) (*BisectEnv, error) {
+ tagList, err := ctx.PreviousReleaseTags(commit)
+ if err != nil {
+ return nil, err
+ }
+ tags := make(map[string]bool)
+ for _, tag := range tagList {
+ tags[tag] = true
+ }
+ env := &BisectEnv{
+ Compiler: "gcc-" + linuxCompilerVersion(tags),
+ KernelConfig: kernelConfig,
+ }
+ return env, nil
+}
+
+func linuxCompilerVersion(tags map[string]bool) string {
+ switch {
+ case tags["v4.12"]:
+ return "8.1.0"
+ case tags["v4.11"]:
+ return "7.3.0"
+ case tags["v3.19"]:
+ return "5.5.0"
+ default:
+ return "4.9.4"
+ }
+}