diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2020-12-11 13:33:04 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2020-12-13 18:56:36 +0100 |
| commit | bef888ec76fd133b9689255c652aacd1e306d56e (patch) | |
| tree | b8091704f0c4166d32aa25366c5f312ea13f7568 /pkg | |
| parent | a733efbda5ffc8f76ca1f62b0c777c21d0254653 (diff) | |
pkg/cover: add gvisor support
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/cover/backend/backend.go | 3 | ||||
| -rw-r--r-- | pkg/cover/backend/gvisor.go | 124 |
2 files changed, 127 insertions, 0 deletions
diff --git a/pkg/cover/backend/backend.go b/pkg/cover/backend/backend.go index 13ae3b400..eb9c1a2e5 100644 --- a/pkg/cover/backend/backend.go +++ b/pkg/cover/backend/backend.go @@ -52,5 +52,8 @@ func Make(target *targets.Target, vm, objDir, srcDir, buildDir string) (*Impl, e if objDir == "" { return nil, fmt.Errorf("kernel obj directory is not specified") } + if vm == "gvisor" { + return makeGvisor(target, objDir, srcDir, buildDir) + } return makeELF(target, objDir, srcDir, buildDir) } diff --git a/pkg/cover/backend/gvisor.go b/pkg/cover/backend/gvisor.go new file mode 100644 index 000000000..459ad60e9 --- /dev/null +++ b/pkg/cover/backend/gvisor.go @@ -0,0 +1,124 @@ +// Copyright 2020 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 backend + +import ( + "bufio" + "fmt" + "path/filepath" + "regexp" + "strconv" + + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/sys/targets" +) + +func makeGvisor(target *targets.Target, objDir, srcDir, buildDir string) (*Impl, error) { + // pkg/build stores runsc as 'image', but a local build will have it as 'runsc'. + bin := filepath.Join(objDir, "image") + if !osutil.IsExist(bin) { + bin = filepath.Join(objDir, "runsc") + } + frames, err := gvisorSymbolize(bin, srcDir) + if err != nil { + return nil, err + } + unitMap := make(map[string]*CompileUnit) + for _, frame := range frames { + unit := unitMap[frame.Name] + if unit == nil { + unit = &CompileUnit{ + Name: frame.Name, + Path: frame.Path, + } + unitMap[frame.Name] = unit + } + unit.PCs = append(unit.PCs, frame.PC) + } + var units []*CompileUnit + for _, unit := range unitMap { + units = append(units, unit) + } + impl := &Impl{ + Units: units, + Frames: frames, + RestorePC: func(pc uint32) uint64 { + return uint64(pc) + }, + } + return impl, nil +} + +func gvisorSymbolize(bin, srcDir string) ([]Frame, error) { + cmd := osutil.Command(bin, "symbolize", "-all") + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } + defer stdout.Close() + if err := cmd.Start(); err != nil { + return nil, err + } + defer cmd.Wait() + var frames []Frame + s := bufio.NewScanner(stdout) + for s.Scan() { + frame, err := gvisorParseLine(s) + if err != nil { + return nil, err + } + frame.Path = filepath.Join(srcDir, frame.Name) + if !osutil.IsExist(frame.Path) { + // Try to locate auto-generated files. + // Note: some files are only present under some hashed path, + // e.g. bazel-out/k8-fastbuild-ST-4c64f0b3d5c7/bin/pkg/usermem/addr_range.go, + // it's unclear how we can locate them. In a local run that may be under objDir, + // but this is not the case for syz-ci. + path := filepath.Join(srcDir, "bazel-out", "k8-fastbuild", "bin", frame.Name) + if osutil.IsExist(path) { + frame.Path = path + } + } + frames = append(frames, frame) + } + if err := s.Err(); err != nil { + return nil, err + } + return frames, nil +} + +func gvisorParseLine(s *bufio.Scanner) (Frame, error) { + pc, err := strconv.ParseUint(s.Text(), 0, 64) + if err != nil { + return Frame{}, fmt.Errorf("read pc %q, but no line info", pc) + } + if !s.Scan() { + return Frame{}, fmt.Errorf("read pc %q, but no line info", pc) + } + match := gvisorLineRe.FindStringSubmatch(s.Text()) + if match == nil { + return Frame{}, fmt.Errorf("failed to parse line: %q", s.Text()) + } + var ints [4]int + for i := range ints { + x, err := strconv.ParseUint(match[i+2], 0, 32) + if err != nil { + return Frame{}, fmt.Errorf("failed to parse number %q: %v", match[i+2], err) + } + ints[i] = int(x) + } + frame := Frame{ + PC: pc, + Name: match[1], + Range: Range{ + StartLine: ints[0], + StartCol: ints[1], + EndLine: ints[2], + EndCol: ints[3], + }, + } + return frame, nil +} + +var gvisorLineRe = regexp.MustCompile(`/gvisor/([^:]+):([0-9]+).([0-9]+),([0-9]+).([0-9]+)$`) |
