From aff16d3fed742e27093858a70f8bd5cbf6f0dedd Mon Sep 17 00:00:00 2001 From: Joey Jiaojg Date: Wed, 3 Mar 2021 00:22:57 +0800 Subject: 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 --- pkg/cover/html.go | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) (limited to 'pkg/cover/html.go') 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(` + + + + + + + +
+ + + + + + + + + + + + {{range $i, $p := .}} + + + + + + + + {{end}} + +
SubsystemCovered / Total Lines / %Covered / Total PCs in File / %Covered / Total PCs in Function / %Covered Functions
{{$p.subsystem}}{{$p.lines}}{{$p.PCsInFiles}}{{$p.PCsInFuncs}}{{$p.totalFuncs}}
+
+ + + +`)) -- cgit mrf-deployment