aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/cover
diff options
context:
space:
mode:
authorJoey Jiaojg <joeyjiaojg@qq.com>2021-03-03 00:22:57 +0800
committerGitHub <noreply@github.com>2021-03-02 17:22:57 +0100
commitaff16d3fed742e27093858a70f8bd5cbf6f0dedd (patch)
treeeca728c55e7c17709db46c1832bfe75649bd3a1d /pkg/cover
parente1b9a5704d15b1f26f7cae9142c34544768175b2 (diff)
pkg/cover, syz-manager: show coverage summary
* pkg/cover, syz-manager: show coverage summary The funccover or cover page is not easy for statistic purpose. So add /cover?type=rawfiles to show coverage based on each file. And /cover?type=table page to show coverage for group of components. If driver_path_map.json exists, /cover?type=table can show component coverage. Format example: { "all": [ "/" ], "audio": [ "/techpack/audio/asoc", "/techpack/audio/dsp", "/techpack/audio/ipc", "/sound/core" ] } If driver_path_map.json not exist, it will show one line summary. * pkg/cover: use subsystem naming * syz-manager: use /subsystemcover and /filecover * pkg/cover: use subsystem from config * pkg/mgrconfig: add kernel_subsystem * pkg/cover, tools/syz-cover: fix make test * all: fix presumit errors * pkg/cover, syz-manager: fix subsystem
Diffstat (limited to 'pkg/cover')
-rw-r--r--pkg/cover/cover.go5
-rw-r--r--pkg/cover/html.go219
-rw-r--r--pkg/cover/report.go27
-rw-r--r--pkg/cover/report_test.go22
4 files changed, 262 insertions, 11 deletions
diff --git a/pkg/cover/cover.go b/pkg/cover/cover.go
index 814ae4fb6..572d5e5da 100644
--- a/pkg/cover/cover.go
+++ b/pkg/cover/cover.go
@@ -6,6 +6,11 @@ package cover
type Cover map[uint32]struct{}
+type Subsystem struct {
+ Name string `json:"name"`
+ Paths []string `json:"path"`
+}
+
func (cov *Cover) Merge(raw []uint32) {
c := *cov
if c == nil {
diff --git a/pkg/cover/html.go b/pkg/cover/html.go
index b353029a3..1757964b5 100644
--- a/pkg/cover/html.go
+++ b/pkg/cover/html.go
@@ -103,6 +103,165 @@ func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog) error {
return coverTemplate.Execute(w, d)
}
+type fileStats struct {
+ Name string
+ CoveredLines int
+ TotalLines int
+ CoveredPCs int
+ TotalPCs int
+ TotalFunctions int
+ CoveredPCsInFunctions int
+ TotalPCsInFunctions int
+}
+
+var csvFilesHeader = []string{
+ "Filename",
+ "CoveredLines",
+ "TotalLines",
+ "CoveredPCs",
+ "TotalPCs",
+ "TotalFunctions",
+ "CoveredPCsInFunctions",
+ "TotalPCsInFunctions",
+}
+
+func (rg *ReportGenerator) convertToStats(progs []Prog) ([]fileStats, error) {
+ files, err := rg.prepareFileMap(progs)
+ if err != nil {
+ return nil, err
+ }
+
+ var data []fileStats
+ for fname, file := range files {
+ lines, err := parseFile(file.filename)
+ if err != nil {
+ fmt.Printf("failed to open/locate %s\n", file.filename)
+ continue
+ }
+ totalFuncs := len(file.functions)
+ var coveredInFunc int
+ var pcsInFunc int
+ for _, function := range file.functions {
+ coveredInFunc += function.covered
+ pcsInFunc += function.pcs
+ }
+ totalLines := len(lines)
+ var coveredLines int
+ for _, line := range file.lines {
+ if len(line.progCount) != 0 {
+ coveredLines++
+ }
+ }
+ data = append(data, fileStats{
+ Name: fname,
+ CoveredLines: coveredLines,
+ TotalLines: totalLines,
+ CoveredPCs: file.coveredPCs,
+ TotalPCs: file.totalPCs,
+ TotalFunctions: totalFuncs,
+ CoveredPCsInFunctions: coveredInFunc,
+ TotalPCsInFunctions: pcsInFunc,
+ })
+ }
+
+ return data, nil
+}
+
+func (rg *ReportGenerator) DoCSVFiles(w io.Writer, progs []Prog) error {
+ data, err := rg.convertToStats(progs)
+ if err != nil {
+ return err
+ }
+
+ sort.SliceStable(data, func(i, j int) bool {
+ return data[i].Name < data[j].Name
+ })
+
+ writer := csv.NewWriter(w)
+ defer writer.Flush()
+ if err := writer.Write(csvFilesHeader); err != nil {
+ return err
+ }
+
+ var d [][]string
+ for _, dt := range data {
+ d = append(d, []string{
+ dt.Name,
+ strconv.Itoa(dt.CoveredLines),
+ strconv.Itoa(dt.TotalLines),
+ strconv.Itoa(dt.CoveredPCs),
+ strconv.Itoa(dt.TotalPCs),
+ strconv.Itoa(dt.TotalFunctions),
+ strconv.Itoa(dt.CoveredPCsInFunctions),
+ strconv.Itoa(dt.TotalPCsInFunctions),
+ })
+ }
+ return writer.WriteAll(d)
+}
+
+func groupCoverByFilePrefixes(datas []fileStats, subsystems []Subsystem) map[string]map[string]string {
+ d := make(map[string]map[string]string)
+
+ for _, subsystem := range subsystems {
+ var coveredLines int
+ var totalLines int
+ var coveredPCsInFile int
+ var totalPCsInFile int
+ var totalFuncs int
+ var coveredPCsInFuncs int
+ var pcsInFuncs int
+ var percentLines float64
+ var percentPCsInFile float64
+ var percentPCsInFunc float64
+
+ for _, path := range subsystem.Paths {
+ for _, data := range datas {
+ if !strings.HasPrefix(data.Name, path) {
+ continue
+ }
+ coveredLines += data.CoveredLines
+ totalLines += data.TotalLines
+ coveredPCsInFile += data.CoveredPCs
+ totalPCsInFile += data.TotalPCs
+ totalFuncs += data.TotalFunctions
+ coveredPCsInFuncs += data.CoveredPCsInFunctions
+ pcsInFuncs += data.TotalPCsInFunctions
+ }
+ }
+
+ if totalLines != 0 {
+ percentLines = 100.0 * float64(coveredLines) / float64(totalLines)
+ }
+ if totalPCsInFile != 0 {
+ percentPCsInFile = 100.0 * float64(coveredPCsInFile) / float64(totalPCsInFile)
+ }
+ if pcsInFuncs != 0 {
+ percentPCsInFunc = 100.0 * float64(coveredPCsInFuncs) / float64(pcsInFuncs)
+ }
+
+ d[subsystem.Name] = map[string]string{
+ "subsystem": subsystem.Name,
+ "lines": fmt.Sprintf("%v / %v / %.2f%%", coveredLines, totalLines, percentLines),
+ "PCsInFiles": fmt.Sprintf("%v / %v / %.2f%%", coveredPCsInFile, totalPCsInFile, percentPCsInFile),
+ "totalFuncs": strconv.Itoa(totalFuncs),
+ "PCsInFuncs": fmt.Sprintf("%v / %v / %.2f%%", coveredPCsInFuncs, pcsInFuncs, percentPCsInFunc),
+ }
+ }
+
+ return d
+}
+
+func (rg *ReportGenerator) DoHTMLTable(w io.Writer, progs []Prog) error {
+ data, err := rg.convertToStats(progs)
+ if err != nil {
+ return err
+ }
+
+ d := groupCoverByFilePrefixes(data, rg.subsystem)
+
+ return coverTableTemplate.Execute(w, d)
+}
+
var csvHeader = []string{
"Filename",
"Function",
@@ -557,3 +716,63 @@ var coverTemplate = template.Must(template.New("").Parse(`
{{end}}
{{end}}
`))
+
+var coverTableTemplate = template.Must(template.New("coverTable").Parse(`
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <style>
+ body {
+ background: white;
+ }
+ #content {
+ color: rgb(70, 70, 70);
+ margin-top: 50px;
+ }
+ th, td {
+ text-align: left;
+ border: 1px solid black;
+ }
+ th {
+ background: gray;
+ }
+ tr:nth-child(2n+1) {
+ background: #CCC
+ }
+ table {
+ border-collapse: collapse;
+ border: 1px solid black;
+ margin-bottom: 20px;
+ }
+ </style>
+ </head>
+ <body>
+ <div>
+ <table>
+ <thead>
+ <tr>
+ <th>Subsystem</th>
+ <th>Covered / Total Lines / %</th>
+ <th>Covered / Total PCs in File / %</th>
+ <th>Covered / Total PCs in Function / %</th>
+ <th>Covered Functions</th>
+ </tr>
+ </thead>
+ <tbody id="content">
+ {{range $i, $p := .}}
+ <tr>
+ <td>{{$p.subsystem}}</td>
+ <td>{{$p.lines}}</td>
+ <td>{{$p.PCsInFiles}}</td>
+ <td>{{$p.PCsInFuncs}}</td>
+ <td>{{$p.totalFuncs}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ </div>
+ </body>
+</html>
+
+`))
diff --git a/pkg/cover/report.go b/pkg/cover/report.go
index f4e88954e..d8f2115e3 100644
--- a/pkg/cover/report.go
+++ b/pkg/cover/report.go
@@ -12,10 +12,11 @@ import (
)
type ReportGenerator struct {
- target *targets.Target
- srcDir string
- objDir string
- buildDir string
+ target *targets.Target
+ srcDir string
+ objDir string
+ buildDir string
+ subsystem []Subsystem
*backend.Impl
}
@@ -26,17 +27,23 @@ type Prog struct {
var RestorePC = backend.RestorePC
-func MakeReportGenerator(target *targets.Target, vm, objDir, srcDir, buildDir string) (*ReportGenerator, error) {
+func MakeReportGenerator(target *targets.Target, vm, objDir, srcDir, buildDir string,
+ subsystem []Subsystem) (*ReportGenerator, error) {
impl, err := backend.Make(target, vm, objDir, srcDir, buildDir)
if err != nil {
return nil, err
}
+ subsystem = append(subsystem, Subsystem{
+ Name: "all",
+ Paths: []string{""},
+ })
rg := &ReportGenerator{
- target: target,
- srcDir: srcDir,
- objDir: objDir,
- buildDir: buildDir,
- Impl: impl,
+ target: target,
+ srcDir: srcDir,
+ objDir: objDir,
+ buildDir: buildDir,
+ subsystem: subsystem,
+ Impl: impl,
}
return rg, nil
}
diff --git a/pkg/cover/report_test.go b/pkg/cover/report_test.go
index 63f1ff50a..0707eab64 100644
--- a/pkg/cover/report_test.go
+++ b/pkg/cover/report_test.go
@@ -180,7 +180,17 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, []
}
defer os.RemoveAll(dir)
bin := buildTestBinary(t, target, test, dir)
- rg, err := MakeReportGenerator(target, "", dir, dir, dir)
+ subsystem := []Subsystem{
+ {
+ Name: "sound",
+ Paths: []string{
+ "sound",
+ "techpack/audio",
+ },
+ },
+ }
+
+ rg, err := MakeReportGenerator(target, "", dir, dir, dir, subsystem)
if err != nil {
return nil, nil, err
}
@@ -216,10 +226,20 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, []
if err := rg.DoHTML(html, test.Progs); err != nil {
return nil, nil, err
}
+ htmlTable := new(bytes.Buffer)
+ if err := rg.DoHTMLTable(htmlTable, test.Progs); err != nil {
+ return nil, nil, err
+ }
+ _ = htmlTable
csv := new(bytes.Buffer)
if err := rg.DoCSV(csv, test.Progs); err != nil {
return nil, nil, err
}
+ csvFiles := new(bytes.Buffer)
+ if err := rg.DoCSVFiles(csvFiles, test.Progs); err != nil {
+ return nil, nil, err
+ }
+ _ = csvFiles
return html.Bytes(), csv.Bytes(), nil
}