diff options
| author | Zubin Mithra <zsm@google.com> | 2023-03-07 16:21:41 -0800 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2023-03-10 07:45:18 +0100 |
| commit | 5205ef306e8b4217fc49cb8d8bd18670b7d08c3c (patch) | |
| tree | b91b96f1402ae75c5c6a86cff16b40100ef3ce0b | |
| parent | f08b59ac0d8759f409d594ddca4f08c920e23237 (diff) | |
tools/syz-cover: allow for exporting source line coverage info
Add a `json` CLI flag that allows for writing out a JSON file with the
following coverage information.
* Module
* Filename
* Covered source lines
* Uncovered source lines
* Both source lines
This can be used to view syzkaller coverage information on other source
browsing/viewing tools.
Usage:
$ ./syz-cover -kernel_obj <path/to/vmlinux> -json <output_json> rawcover
| -rw-r--r-- | docs/coverage.md | 6 | ||||
| -rw-r--r-- | pkg/cover/html.go | 61 | ||||
| -rw-r--r-- | tools/syz-cover/syz-cover.go | 10 |
3 files changed, 77 insertions, 0 deletions
diff --git a/docs/coverage.md b/docs/coverage.md index a2d8defec..42cba097c 100644 --- a/docs/coverage.md +++ b/docs/coverage.md @@ -85,3 +85,9 @@ You can also export CSV file containing function coverage by: ``` bash ./bin/syz-cover --kernel_obj <directory where vmlinux is located> --csv <filename where to export> rawcover ``` + +You can export a JSON file containing line coverage info by: + +```bash +./bin/syz-cover --kernel_obj <directory where vmlinux is located> --json <filename where to export> rawcover +``` diff --git a/pkg/cover/html.go b/pkg/cover/html.go index c5c18c698..874160c8c 100644 --- a/pkg/cover/html.go +++ b/pkg/cover/html.go @@ -7,6 +7,7 @@ import ( "bufio" "bytes" "encoding/csv" + "encoding/json" "fmt" "html" "html/template" @@ -118,6 +119,66 @@ func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog, coverFilter map[uin return coverTemplate.Execute(w, d) } +type lineCoverExport struct { + Module string `json:",omitempty"` + Filename string + Covered []int `json:",omitempty"` + Uncovered []int `json:",omitempty"` + Both []int `json:",omitempty"` +} + +func (rg *ReportGenerator) DoLineJSON(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) + files, err := rg.prepareFileMap(progs) + if err != nil { + return err + } + var entries []lineCoverExport + for _, file := range files { + lines, err := parseFile(file.filename) + if err != nil { + // Ignore and continue onto the next file. + continue + } + entries = append(entries, fileLineContents(file, lines)) + } + encoder := json.NewEncoder(w) + encoder.SetIndent("", "\t") + if err := encoder.Encode(entries); err != nil { + return fmt.Errorf("encoding [%v] entries failed: %v", len(entries), err) + } + return nil +} + +func fileLineContents(file *file, lines [][]byte) lineCoverExport { + lce := lineCoverExport{ + Module: file.module, + Filename: file.filename, + } + lineCover := perLineCoverage(file.covered, file.uncovered) + for i, ln := range lines { + start := 0 + cover := append(lineCover[i+1], lineCoverChunk{End: backend.LineEnd}) + for _, cov := range cover { + end := cov.End - 1 + if end > len(ln) { + end = len(ln) + } + if end == start { + continue + } + if cov.Covered && cov.Uncovered { + lce.Both = append(lce.Both, i+1) + } else if cov.Covered { + lce.Covered = append(lce.Covered, i+1) + } else if cov.Uncovered { + lce.Uncovered = append(lce.Uncovered, i+1) + } + } + } + return lce +} + func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) error { progs = fixUpPCs(rg.target.Arch, progs, coverFilter) if err := rg.lazySymbolize(progs); err != nil { diff --git a/tools/syz-cover/syz-cover.go b/tools/syz-cover/syz-cover.go index e161eac07..80c58ffc5 100644 --- a/tools/syz-cover/syz-cover.go +++ b/tools/syz-cover/syz-cover.go @@ -42,6 +42,7 @@ func main() { 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") flagExportCSV = flag.String("csv", "", "export coverage data in csv format (optional)") + flagExportLineJSON = flag.String("json", "", "export coverage data with source line info in json format (optional)") flagExportHTML = flag.String("html", "", "save coverage HTML report to file (optional)") ) defer tool.Init()() @@ -87,6 +88,15 @@ func main() { } return } + if *flagExportLineJSON != "" { + if err := rg.DoLineJSON(buf, progs, nil); err != nil { + tool.Fail(err) + } + if err := osutil.WriteFile(*flagExportLineJSON, buf.Bytes()); err != nil { + tool.Fail(err) + } + return + } if err := rg.DoHTML(buf, progs, nil); err != nil { tool.Fail(err) } |
