aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZubin Mithra <zsm@google.com>2023-03-07 16:21:41 -0800
committerDmitry Vyukov <dvyukov@google.com>2023-03-10 07:45:18 +0100
commit5205ef306e8b4217fc49cb8d8bd18670b7d08c3c (patch)
treeb91b96f1402ae75c5c6a86cff16b40100ef3ce0b
parentf08b59ac0d8759f409d594ddca4f08c920e23237 (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.md6
-rw-r--r--pkg/cover/html.go61
-rw-r--r--tools/syz-cover/syz-cover.go10
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)
}