Skip to content

Commit

Permalink
pkg/cover, syz-manager: introduce the /cover?debug=1 parameter
Browse files Browse the repository at this point in the history
Debugging coverage point validation warnings may require looking at
specific addresses, which are not printed anywhere. Add a URL parameter
that can be passed to prepareFileMap() to print a more meaningful error
message.

Also factor out the error message code from prepareFileMap() to reduce
its cyclomatic complexity.
  • Loading branch information
ramosian-glider committed Feb 13, 2024
1 parent 77b23aa commit e66542d
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 28 deletions.
38 changes: 22 additions & 16 deletions pkg/cover/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ import (
"github.com/google/syzkaller/pkg/mgrconfig"
)

func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
files, err := rg.prepareFileMap(progs)
type CoverHandlerParams struct {
Progs []Prog
CoverFilter map[uint32]uint32
Debug bool
}

func (rg *ReportGenerator) DoHTML(w io.Writer, params CoverHandlerParams) error {
var progs = fixUpPCs(rg.target.Arch, params.Progs, params.CoverFilter)
files, err := rg.prepareFileMap(progs, params.Debug)
if err != nil {
return err
}
Expand Down Expand Up @@ -127,9 +133,9 @@ type lineCoverExport struct {
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)
func (rg *ReportGenerator) DoLineJSON(w io.Writer, params CoverHandlerParams) error {
var progs = fixUpPCs(rg.target.Arch, params.Progs, params.CoverFilter)
files, err := rg.prepareFileMap(progs, params.Debug)
if err != nil {
return err
}
Expand Down Expand Up @@ -291,7 +297,7 @@ var csvFilesHeader = []string{
}

func (rg *ReportGenerator) convertToStats(progs []Prog) ([]fileStats, error) {
files, err := rg.prepareFileMap(progs)
files, err := rg.prepareFileMap(progs, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -341,8 +347,8 @@ func (rg *ReportGenerator) convertToStats(progs []Prog) ([]fileStats, error) {
return data, nil
}

func (rg *ReportGenerator) DoCSVFiles(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
func (rg *ReportGenerator) DoCSVFiles(w io.Writer, params CoverHandlerParams) error {
var progs = fixUpPCs(rg.target.Arch, params.Progs, params.CoverFilter)
data, err := rg.convertToStats(progs)
if err != nil {
return err
Expand Down Expand Up @@ -441,8 +447,8 @@ func groupCoverByFilePrefixes(datas []fileStats, subsystems []mgrconfig.Subsyste
return d
}

func (rg *ReportGenerator) DoHTMLTable(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
func (rg *ReportGenerator) DoHTMLTable(w io.Writer, params CoverHandlerParams) error {
var progs = fixUpPCs(rg.target.Arch, params.Progs, params.CoverFilter)
data, err := rg.convertToStats(progs)
if err != nil {
return err
Expand Down Expand Up @@ -517,8 +523,8 @@ func groupCoverByModule(datas []fileStats) map[string]map[string]string {
return d
}

func (rg *ReportGenerator) DoModuleCover(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
func (rg *ReportGenerator) DoModuleCover(w io.Writer, params CoverHandlerParams) error {
var progs = fixUpPCs(rg.target.Arch, params.Progs, params.CoverFilter)
data, err := rg.convertToStats(progs)
if err != nil {
return err
Expand All @@ -537,9 +543,9 @@ var csvHeader = []string{
"Total PCs",
}

func (rg *ReportGenerator) DoCSV(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
files, err := rg.prepareFileMap(progs)
func (rg *ReportGenerator) DoCSV(w io.Writer, params CoverHandlerParams) error {
var progs = fixUpPCs(rg.target.Arch, params.Progs, params.CoverFilter)
files, err := rg.prepareFileMap(progs, params.Debug)
if err != nil {
return err
}
Expand Down
19 changes: 15 additions & 4 deletions pkg/cover/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,20 @@ type line struct {
progIndex int // example program index that covers this line
}

func (rg *ReportGenerator) prepareFileMap(progs []Prog) (map[string]*file, error) {
func coverageCallbackMismatch(debug bool, numPCs int, unmatchedProgPCs map[uint64]bool) error {
debugStr := ""
if debug {
debugStr += "\n\nUnmatched PCs:\n"
for pc := range unmatchedProgPCs {
debugStr += fmt.Sprintf("%x\n", pc)
}
}
// nolint: lll
return fmt.Errorf("%d out of %d PCs returned by kcov do not have matching coverage callbacks. Check the discoverModules() code.%s",
len(unmatchedProgPCs), numPCs, debugStr)
}

func (rg *ReportGenerator) prepareFileMap(progs []Prog, debug bool) (map[string]*file, error) {
if err := rg.lazySymbolize(progs); err != nil {
return nil, err
}
Expand Down Expand Up @@ -131,9 +144,7 @@ func (rg *ReportGenerator) prepareFileMap(progs []Prog) (map[string]*file, error
// If the backend provided coverage callback locations for the binaries, use them to
// verify data returned by kcov.
if verifyCoverPoints && (len(unmatchedProgPCs) > 0) {
return nil, fmt.Errorf("%d out of %d PCs returned by kcov do not have matching "+
"coverage callbacks. Check the discoverModules() code",
len(unmatchedProgPCs), len(progPCs))
return nil, coverageCallbackMismatch(debug, len(progPCs), unmatchedProgPCs)
}
for _, unit := range rg.Units {
f := files[unit.Name]
Expand Down
11 changes: 7 additions & 4 deletions pkg/cover/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,20 +365,23 @@ func generateReport(t *testing.T, target *targets.Target, test *Test) ([]byte, [
progs = append(progs, Prog{Data: "main", PCs: pcs})
}
html := new(bytes.Buffer)
if err := rg.DoHTML(html, progs, nil); err != nil {
params := CoverHandlerParams{
Progs: progs,
}
if err := rg.DoHTML(html, params); err != nil {
return nil, nil, err
}
htmlTable := new(bytes.Buffer)
if err := rg.DoHTMLTable(htmlTable, progs, nil); err != nil {
if err := rg.DoHTMLTable(htmlTable, params); err != nil {
return nil, nil, err
}
_ = htmlTable
csv := new(bytes.Buffer)
if err := rg.DoCSV(csv, progs, nil); err != nil {
if err := rg.DoCSV(csv, params); err != nil {
return nil, nil, err
}
csvFiles := new(bytes.Buffer)
if err := rg.DoCSVFiles(csvFiles, progs, nil); err != nil {
if err := rg.DoCSVFiles(csvFiles, params); err != nil {
return nil, nil, err
}
_ = csvFiles
Expand Down
11 changes: 10 additions & 1 deletion syz-manager/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
}

do := rg.DoHTML

if funcFlag == DoHTMLTable {
do = rg.DoHTMLTable
} else if funcFlag == DoModuleCover {
Expand All @@ -359,7 +360,15 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
do = rg.DoCSVFiles
}

if err := do(w, progs, coverFilter); err != nil {
debug := r.FormValue("debug") != ""

params := cover.CoverHandlerParams{
Progs: progs,
CoverFilter: coverFilter,
Debug: debug,
}

if err := do(w, params); err != nil {
http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError)
return
}
Expand Down
11 changes: 8 additions & 3 deletions tools/syz-cover/syz-cover.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,13 @@ func main() {
}
progs := []cover.Prog{{PCs: pcs}}
buf := new(bytes.Buffer)
params := cover.CoverHandlerParams{
Progs: progs,
CoverFilter: nil,
Debug: false,
}
if *flagExportCSV != "" {
if err := rg.DoCSV(buf, progs, nil); err != nil {
if err := rg.DoCSV(buf, params); err != nil {
tool.Fail(err)
}
if err := osutil.WriteFile(*flagExportCSV, buf.Bytes()); err != nil {
Expand All @@ -87,15 +92,15 @@ func main() {
return
}
if *flagExportLineJSON != "" {
if err := rg.DoLineJSON(buf, progs, nil); err != nil {
if err := rg.DoLineJSON(buf, params); 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 {
if err := rg.DoHTML(buf, params); err != nil {
tool.Fail(err)
}
if *flagExportHTML != "" {
Expand Down

0 comments on commit e66542d

Please sign in to comment.