diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-05-17 17:18:24 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-05-17 17:18:24 +0200 |
| commit | be47ebb0eb17e47c9f1f84156e17aa6970d47435 (patch) | |
| tree | c7351c27390bb6eefef00d526dd1c448648c627d /pkg | |
| parent | 28cbff8c7d91cd42ee29ebf80c6e9fc4dfbf50e9 (diff) | |
pkg/kernel: allow to split full make output
Currently kernel build failures are insanely verbose
(contain full kernel build output) and there is no
way to separate short descriptions from full output.
Make it possible.
Also try to extract failure root cause froom build log.
Use this in pkg/bisect to not pollute log on build failures.
Update #501
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/bisect/bisect.go | 7 | ||||
| -rw-r--r-- | pkg/instance/instance.go | 4 | ||||
| -rw-r--r-- | pkg/kernel/kernel.go | 43 | ||||
| -rw-r--r-- | pkg/osutil/osutil.go | 25 |
4 files changed, 72 insertions, 7 deletions
diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go index 5fce43ced..f2770d6ce 100644 --- a/pkg/bisect/bisect.go +++ b/pkg/bisect/bisect.go @@ -227,7 +227,12 @@ func (env *env) test() (git.BisectResult, error) { cfg.Kernel.Cmdline, cfg.Kernel.Sysctl, cfg.Kernel.Config) env.buildTime += time.Since(buildStart) if err != nil { - env.log("kernel build failed: %v", err) + if verr, ok := err.(*osutil.VerboseError); ok { + env.log("%v", verr.Title) + env.saveDebugFile(current.Hash, 0, verr.Output) + } else { + env.log("%v", err) + } return git.BisectSkip, nil } testStart := time.Now() diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index ae94c265f..84e3c518d 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -81,14 +81,14 @@ func (env *Env) BuildSyzkaller(repo, commit string) error { func (env *Env) BuildKernel(compilerBin, userspaceDir, cmdlineFile, sysctlFile string, kernelConfig []byte) error { cfg := env.cfg if err := kernel.Build(cfg.KernelSrc, compilerBin, kernelConfig); err != nil { - return fmt.Errorf("kernel build failed: %v", err) + return osutil.PrependContext("kernel build failed", err) } cfg.Vmlinux = filepath.Join(cfg.KernelSrc, "vmlinux") cfg.Image = filepath.Join(cfg.Workdir, "syz-image") cfg.SSHKey = filepath.Join(cfg.Workdir, "syz-key") if err := kernel.CreateImage(cfg.TargetOS, cfg.TargetVMArch, cfg.Type, cfg.KernelSrc, userspaceDir, cmdlineFile, sysctlFile, cfg.Image, cfg.SSHKey); err != nil { - return fmt.Errorf("image build failed: %v", err) + return osutil.PrependContext("image build failed", err) } return nil } diff --git a/pkg/kernel/kernel.go b/pkg/kernel/kernel.go index 21612f10a..454ed8eb3 100644 --- a/pkg/kernel/kernel.go +++ b/pkg/kernel/kernel.go @@ -12,6 +12,7 @@ package kernel import ( + "bytes" "fmt" "io/ioutil" "os" @@ -50,8 +51,10 @@ func Build(dir, compiler string, config []byte) error { } cmd.Dir = dir // Build of a large kernel can take a while on a 1 CPU VM. - _, err := osutil.Run(3*time.Hour, cmd) - return err + if _, err := osutil.Run(3*time.Hour, cmd); err != nil { + return extractRootCause(err) + } + return nil } func Clean(dir string) error { @@ -122,3 +125,39 @@ func CompilerIdentity(compiler string) (string, error) { } return strings.Split(string(output), "\n")[0], nil } + +func extractRootCause(err error) error { + verr, ok := err.(*osutil.VerboseError) + if !ok { + return err + } + var cause []byte + for _, line := range bytes.Split(verr.Output, []byte{'\n'}) { + for _, pattern := range buildFailureCauses { + if pattern.weak && cause != nil { + continue + } + if bytes.Contains(line, pattern.pattern) { + cause = line + break + } + } + } + if cause != nil { + verr.Title = string(cause) + } + return verr +} + +type buildFailureCause struct { + pattern []byte + weak bool +} + +var buildFailureCauses = [...]buildFailureCause{ + {pattern: []byte(": error: ")}, + {pattern: []byte(": fatal error: ")}, + {pattern: []byte(": undefined reference to")}, + {weak: true, pattern: []byte(": final link failed: ")}, + {weak: true, pattern: []byte("collect2: error: ")}, +} diff --git a/pkg/osutil/osutil.go b/pkg/osutil/osutil.go index a1d65e835..e41ad7ec1 100644 --- a/pkg/osutil/osutil.go +++ b/pkg/osutil/osutil.go @@ -47,8 +47,10 @@ func Run(timeout time.Duration, cmd *exec.Cmd) ([]byte, error) { }() defer close(done) if err := cmd.Wait(); err != nil { - return nil, fmt.Errorf("failed to run %v %+v: %v\n%v", - cmd.Path, cmd.Args, err, output.String()) + return nil, &VerboseError{ + Title: fmt.Sprintf("failed to run %v %+v: %v", cmd.Path, cmd.Args, err), + Output: output.Bytes(), + } } return output.Bytes(), nil } @@ -60,6 +62,25 @@ func Command(bin string, args ...string) *exec.Cmd { return cmd } +type VerboseError struct { + Title string + Output []byte +} + +func (err *VerboseError) Error() string { + return fmt.Sprintf("%v\n%s", err.Title, err.Output) +} + +func PrependContext(ctx string, err error) error { + switch err1 := err.(type) { + case *VerboseError: + err1.Title = fmt.Sprintf("%v: %v", ctx, err1.Title) + return err1 + default: + return fmt.Errorf("%v: %v", ctx, err) + } +} + // IsExist returns true if the file name exists. func IsExist(name string) bool { _, err := os.Stat(name) |
