diff options
| author | Joey Jiaojg <joeyjiaojg@qq.com> | 2021-03-03 00:22:57 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-02 17:22:57 +0100 |
| commit | aff16d3fed742e27093858a70f8bd5cbf6f0dedd (patch) | |
| tree | eca728c55e7c17709db46c1832bfe75649bd3a1d /pkg/cover | |
| parent | e1b9a5704d15b1f26f7cae9142c34544768175b2 (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.go | 5 | ||||
| -rw-r--r-- | pkg/cover/html.go | 219 | ||||
| -rw-r--r-- | pkg/cover/report.go | 27 | ||||
| -rw-r--r-- | pkg/cover/report_test.go | 22 |
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 } |
