syzkaller/tools/syz-cover/syz-cover.go

129 lines
3.3 KiB
Go
Raw Permalink Normal View History

// Copyright 2018 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.
// syz-cover generates coverage HTML report from raw coverage files.
// Raw coverage files are text files with one PC in hex form per line, e.g.:
//
// 0xffffffff8398658d
// 0xffffffff839862fc
// 0xffffffff8398633f
//
// Raw coverage files can be obtained either from /rawcover manager HTTP handler,
// or from syz-execprog with -coverfile flag.
//
// Usage:
// syz-cover [-os=OS -arch=ARCH -kernel_src=. -kernel_obj=.] rawcover.file*
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"github.com/google/syzkaller/pkg/cmdprof"
"github.com/google/syzkaller/pkg/cover"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/sys/targets"
)
func main() {
var (
flagOS = flag.String("os", runtime.GOOS, "target os")
flagArch = flag.String("arch", runtime.GOARCH, "target arch")
flagKernelSrc = flag.String("kernel_src", "", "path to kernel sources")
flagKernelBuildSrc = flag.String("kernel_build_src", "", "path to kernel image's build dir (optional)")
flagKernelObj = flag.String("kernel_obj", "", "path to kernel build/obj dir")
flagExport = flag.String("csv", "", "export coverage data in csv format (optional)")
)
flag.Parse()
defer cmdprof.Install()()
if len(flag.Args()) == 0 {
fmt.Fprintf(os.Stderr, "usage: syz-cover [flags] rawcover.file\n")
flag.PrintDefaults()
os.Exit(1)
}
if *flagKernelSrc == "" {
*flagKernelSrc = "."
}
if *flagKernelObj == "" {
*flagKernelObj = *flagKernelSrc
}
if *flagKernelBuildSrc == "" {
*flagKernelBuildSrc = *flagKernelSrc
}
target := targets.Get(*flagOS, *flagArch)
if target == nil {
failf("unknown target %v/%v", *flagOS, *flagArch)
}
pcs, err := readPCs(flag.Args())
if err != nil {
failf("%v", err)
}
kernelObj := filepath.Join(*flagKernelObj, target.KernelObject)
rg, err := cover.MakeReportGenerator(target, kernelObj, *flagKernelSrc, *flagKernelBuildSrc)
if err != nil {
failf("%v", err)
}
progs := []cover.Prog{{PCs: pcs}}
buf := new(bytes.Buffer)
if *flagExport != "" {
if err := rg.DoCSV(buf, progs); err != nil {
failf("%v", err)
}
if err := osutil.WriteFile(*flagExport, buf.Bytes()); err != nil {
failf("%v", err)
}
return
}
if err := rg.DoHTML(buf, progs); err != nil {
failf("%v", err)
}
fn, err := osutil.TempFile("syz-cover")
if err != nil {
failf("%v", err)
}
fn += ".html"
if err := osutil.WriteFile(fn, buf.Bytes()); err != nil {
failf("%v", err)
}
if err := exec.Command("xdg-open", fn).Start(); err != nil {
failf("failed to start browser: %v", err)
}
}
func readPCs(files []string) ([]uint64, error) {
var pcs []uint64
for _, file := range files {
data, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
for s := bufio.NewScanner(bytes.NewReader(data)); s.Scan(); {
line := strings.TrimSpace(s.Text())
if line == "" {
continue
}
pc, err := strconv.ParseUint(line, 0, 64)
if err != nil {
return nil, err
}
pcs = append(pcs, pc)
}
}
return pcs, nil
}
func failf(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}