From 64d0e756ef01c499a6bd2ca2e8b2eb073330c010 Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Tue, 13 Aug 2024 12:57:24 +0200 Subject: tools/syz-cover: add toolFileCover --- pkg/cover/file.go | 73 +++++++++++++++++++++++++++++++++++++++++ pkg/covermerger/provider_web.go | 11 +++++++ tools/syz-cover/syz-cover.go | 49 +++++++++++++++++++++++---- 3 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 pkg/cover/file.go diff --git a/pkg/cover/file.go b/pkg/cover/file.go new file mode 100644 index 000000000..3e04e3d2d --- /dev/null +++ b/pkg/cover/file.go @@ -0,0 +1,73 @@ +// Copyright 2024 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package cover + +import ( + "context" + "fmt" + "strings" + + "cloud.google.com/go/civil" + "github.com/google/syzkaller/pkg/covermerger" +) + +type lineRender func(string, bool, int, int) string + +func RendFileCoverage(c context.Context, ns, repo, commit, filePath string, + fromDate, toDate civil.Date, render lineRender) (string, error) { + fileContent, err := covermerger.GetFileVersion(filePath, repo, commit) + if err != nil { + return "", fmt.Errorf("failed to GetFileVersion for file %s, commit %s from repo %s: %w", + filePath, commit, repo, err) + } + config := &covermerger.Config{ + Jobs: 1, + Base: covermerger.RepoBranchCommit{ + Repo: repo, + Commit: commit, + }, + FileVersProvider: covermerger.MakeWebGit(), + } + + dbReader := covermerger.MakeBQCSVReader() + if err := dbReader.InitNsRecords(c, + ns, + filePath, + fromDate, + toDate, + ); err != nil { + return "", fmt.Errorf("failed to dbReader.InitNsRecords: %w", err) + } + defer dbReader.Close() + csvReader, err := dbReader.Reader() + if err != nil { + return "", fmt.Errorf("failed to dbReader.Reader: %w", err) + } + + mergeResult, err := covermerger.MergeCSVData(config, csvReader) + if err != nil { + return "", fmt.Errorf("error merging coverage: %w", err) + } + + return rendResult(fileContent, mergeResult[filePath], render), nil +} + +func rendResult(content string, coverage *covermerger.MergeResult, render lineRender) string { + srclines := strings.Split(content, "\n") + var htmlLines []string + for i, srcLine := range srclines { + lineNum := i + 1 + covered, instrumented := coverage.HitCounts[lineNum] + htmlLines = append(htmlLines, render(srcLine, instrumented, covered, lineNum)) + } + return strings.Join(htmlLines, "\n") +} + +func RendTextLine(code string, instrumented bool, covered, num int) string { + covStr := fmt.Sprintf("%6d", covered) + if !instrumented { + covStr = strings.Repeat(" ", 6) + } + return fmt.Sprintf("%s %6d %s", covStr, num, code) +} diff --git a/pkg/covermerger/provider_web.go b/pkg/covermerger/provider_web.go index 72933f280..3347f7931 100644 --- a/pkg/covermerger/provider_web.go +++ b/pkg/covermerger/provider_web.go @@ -63,3 +63,14 @@ func loadFile(filePath, repo, commit string) ([]byte, error) { func MakeWebGit() FileVersProvider { return &webGit{} } + +func GetFileVersion(filePath, repo, commit string) (string, error) { + rbc := RepoBranchCommit{repo, "", commit} + files, err := MakeWebGit().GetFileVersions(nil, + filePath, + []RepoBranchCommit{rbc}) + if err != nil { + return "", fmt.Errorf("failed to GetFileVersions: %w", err) + } + return files[rbc], nil +} diff --git a/tools/syz-cover/syz-cover.go b/tools/syz-cover/syz-cover.go index 9446dffd8..6832596e5 100644 --- a/tools/syz-cover/syz-cover.go +++ b/tools/syz-cover/syz-cover.go @@ -23,8 +23,10 @@ package main import ( "bufio" "bytes" + "context" "encoding/json" "flag" + "fmt" "os" "os/exec" "strconv" @@ -54,18 +56,29 @@ var ( flagDateTo = flag.String("to", civil.DateOf(time.Now()).String(), "heatmap date to(optional)") flagProjectID = flag.String("project", "syzkaller", "spanner db project name") + flagForFile = flag.String("for-file", "", "[optional]show file coverage") + flagRepo = flag.String("repo", "git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git", + "[optional] repo to be used by -for-file") + flagCommit = flag.String("commit", "", "commit to be used by -for-file") + flagNamespace = flag.String("namespace", "upstream", "[optional] used by -for-file") ) +func parseDates() (civil.Date, civil.Date) { + dateFrom, errDateFrom := civil.ParseDate(*flagDateFrom) + if errDateFrom != nil { + tool.Failf("failed to parse date from: %v", errDateFrom.Error()) + } + dateTo, errDateTo := civil.ParseDate(*flagDateTo) + if errDateTo != nil { + tool.Failf("failed to parse date to: %v", errDateTo.Error()) + } + return dateFrom, dateTo +} + func toolBuildNsHeatmap() { buf := new(bytes.Buffer) - var dateFrom, dateTo civil.Date + dateFrom, dateTo := parseDates() var err error - if dateFrom, err = civil.ParseDate(*flagDateFrom); err != nil { - tool.Failf("failed to parse date from: %v", err.Error()) - } - if dateTo, err = civil.ParseDate(*flagDateTo); err != nil { - tool.Failf("failed to parse date to: %v", err.Error()) - } switch *flagNsHeatmapGroupBy { case "dir": if err = cover.DoDirHeatMap(buf, *flagProjectID, *flagNsHeatmap, dateFrom, dateTo); err != nil { @@ -83,8 +96,30 @@ func toolBuildNsHeatmap() { } } +func toolFileCover() { + dateFrom, dateTo := parseDates() + details, err := cover.RendFileCoverage( + context.Background(), + *flagNamespace, + *flagRepo, + *flagCommit, + *flagForFile, + dateFrom, + dateTo, + cover.RendTextLine, + ) + if err != nil { + tool.Fail(err) + } + fmt.Println(details) +} + func main() { defer tool.Init()() + if *flagForFile != "" { + toolFileCover() + return + } if *flagNsHeatmap != "" { toolBuildNsHeatmap() return -- cgit mrf-deployment