diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2025-04-10 11:34:39 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2025-04-10 17:07:48 +0000 |
| commit | 1bc60a19f725612590947493424fef2e314bd044 (patch) | |
| tree | 3be2c55ec8612d430845327602d832d6eb01b479 /pkg | |
| parent | 2d9fa31cf7abaddf944824814053e67b4d5c478e (diff) | |
tools/syz-declextract: add interface coverage info
Add coverage percent for kernel interfaces.
The current data is generated with Mar coverage report
on kernel commit 1e7857b28020ba57ca7fdafae7ac855ba326c697.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/cover/heatmap.go | 2 | ||||
| -rw-r--r-- | pkg/cover/html.go | 13 | ||||
| -rw-r--r-- | pkg/declextract/declextract.go | 7 | ||||
| -rw-r--r-- | pkg/declextract/entity.go | 6 | ||||
| -rw-r--r-- | pkg/declextract/interface.go | 48 |
5 files changed, 56 insertions, 20 deletions
diff --git a/pkg/cover/heatmap.go b/pkg/cover/heatmap.go index 20c7ac4c1..efde4fed4 100644 --- a/pkg/cover/heatmap.go +++ b/pkg/cover/heatmap.go @@ -91,7 +91,7 @@ func (thm *templateHeatmapRow) prepareDataFor(pageColumns []pageColumnTarget, sk var dateCoverage int64 tp := pageColumn.TimePeriod if thm.instrumented[tp] != 0 { - dateCoverage = percent(thm.covered[tp], thm.instrumented[tp]) + dateCoverage = Percent(thm.covered[tp], thm.instrumented[tp]) } thm.Coverage = append(thm.Coverage, dateCoverage) thm.Tooltips = append(thm.Tooltips, fmt.Sprintf("Instrumented:\t%d blocks\nCovered:\t%d blocks", diff --git a/pkg/cover/html.go b/pkg/cover/html.go index 1b2d4d024..88f30abb1 100644 --- a/pkg/cover/html.go +++ b/pkg/cover/html.go @@ -877,7 +877,7 @@ func addFunctionCoverage(file *file, data *templateData) { percentage := "" coveredTotal += function.covered if function.covered > 0 { - percentage = fmt.Sprintf("%v%%", percent(function.covered, function.pcs)) + percentage = fmt.Sprintf("%v%%", Percent(function.covered, function.pcs)) TotalInCoveredFunc += function.pcs } else { percentage = "---" @@ -891,7 +891,7 @@ func addFunctionCoverage(file *file, data *templateData) { buf.WriteString("<span class='hover'>SUMMARY") percentInCoveredFunc := "" if TotalInCoveredFunc > 0 { - percentInCoveredFunc = fmt.Sprintf("%v%%", percent(coveredTotal, TotalInCoveredFunc)) + percentInCoveredFunc = fmt.Sprintf("%v%%", Percent(coveredTotal, TotalInCoveredFunc)) } else { percentInCoveredFunc = "---" } @@ -915,21 +915,24 @@ func processDir(dir *templateDir) { for _, f := range dir.Files { dir.Total += f.Total dir.Covered += f.Covered - f.Percent = percent(f.Covered, f.Total) + f.Percent = Percent(f.Covered, f.Total) } for _, child := range dir.Dirs { processDir(child) dir.Total += child.Total dir.Covered += child.Covered } - dir.Percent = percent(dir.Covered, dir.Total) + dir.Percent = Percent(dir.Covered, dir.Total) if dir.Covered == 0 { dir.Dirs = nil dir.Files = nil } } -func percent[T int | int64](covered, total T) T { +func Percent[T int | int64](covered, total T) T { + if total == 0 { + return 0 + } f := math.Ceil(float64(covered) / float64(total) * 100) if f == 100 && covered < total { f = 99 diff --git a/pkg/declextract/declextract.go b/pkg/declextract/declextract.go index 4f3c66173..0bf63f2e6 100644 --- a/pkg/declextract/declextract.go +++ b/pkg/declextract/declextract.go @@ -12,6 +12,7 @@ import ( "slices" "strings" + "github.com/google/syzkaller/pkg/cover" "github.com/google/syzkaller/pkg/ifaceprobe" ) @@ -27,11 +28,12 @@ type StructInfo struct { Align int } -func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string, trace io.Writer) ( - *Result, error) { +func Run(out *Output, probe *ifaceprobe.Info, coverage []*cover.FileCoverage, + syscallRename map[string][]string, trace io.Writer) (*Result, error) { ctx := &context{ Output: out, probe: probe, + coverage: coverage, syscallRename: syscallRename, structs: make(map[string]*Struct), funcs: make(map[string]*Function), @@ -64,6 +66,7 @@ func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string, type context struct { *Output probe *ifaceprobe.Info + coverage []*cover.FileCoverage syscallRename map[string][]string // syscall function -> syscall names structs map[string]*Struct funcs map[string]*Function diff --git a/pkg/declextract/entity.go b/pkg/declextract/entity.go index 25c688cd4..eb3bc4d7c 100644 --- a/pkg/declextract/entity.go +++ b/pkg/declextract/entity.go @@ -48,8 +48,10 @@ type FunctionScope struct { Calls []string `json:"calls,omitempty"` Facts []*TypingFact `json:"facts,omitempty"` - fn *Function - calls []*Function + fn *Function + calls []*Function + coveredBlocks int + totalBlocks int } type ConstInfo struct { diff --git a/pkg/declextract/interface.go b/pkg/declextract/interface.go index e7a859da9..dd5bfa1d4 100644 --- a/pkg/declextract/interface.go +++ b/pkg/declextract/interface.go @@ -6,6 +6,8 @@ package declextract import ( "slices" "strings" + + "github.com/google/syzkaller/pkg/cover" ) type Interface struct { @@ -19,6 +21,8 @@ type Interface struct { ManualDescriptions bool AutoDescriptions bool ReachableLOC int + CoveredBlocks int + TotalBlocks int scopeArg int scopeVal string @@ -41,7 +45,7 @@ func (ctx *context) noteInterface(iface *Interface) { func (ctx *context) finishInterfaces() { for _, iface := range ctx.interfaces { - iface.ReachableLOC = ctx.reachableLOC(iface.Func, iface.Files[0], iface.scopeArg, iface.scopeVal) + ctx.calculateLOC(iface) slices.Sort(iface.Files) iface.Files = slices.Compact(iface.Files) if iface.Access == "" { @@ -60,7 +64,29 @@ func (ctx *context) processFunctions() { ctx.funcs[fn.Name] = fn } } + coverBlocks := make(map[string][]*cover.Block) + for _, file := range ctx.coverage { + for _, fn := range file.Functions { + coverBlocks[file.FilePath+fn.FuncName] = fn.Blocks + } + } for _, fn := range ctx.Functions { + for _, block := range coverBlocks[fn.File+fn.Name] { + var match *FunctionScope + for _, scope := range fn.Scopes { + if scope.Arg == -1 { + match = scope + } + if block.FromLine >= scope.StartLine && block.FromLine <= scope.EndLine { + match = scope + break + } + } + match.totalBlocks++ + if block.HitCount != 0 { + match.coveredBlocks++ + } + } for _, scope := range fn.Scopes { for _, callee := range scope.Calls { called := ctx.findFunc(callee, fn.File) @@ -74,19 +100,19 @@ func (ctx *context) processFunctions() { } } -func (ctx *context) reachableLOC(name, file string, scopeArg int, scopeVal string) int { - fn := ctx.findFunc(name, file) +func (ctx *context) calculateLOC(iface *Interface) { + fn := ctx.findFunc(iface.Func, iface.Files[0]) if fn == nil { - ctx.warn("can't find function %v called in %v", name, file) - return 0 + ctx.warn("can't find function %v called in %v", iface.Func, iface.Files[0]) + return } - scopeFnArgs := ctx.inferArgFlow(fnArg{fn, scopeArg}) + scopeFnArgs := ctx.inferArgFlow(fnArg{fn, iface.scopeArg}) visited := make(map[*Function]bool) - return ctx.collectLOC(fn, scopeFnArgs, scopeVal, visited) + iface.ReachableLOC = ctx.collectLOC(iface, fn, scopeFnArgs, iface.scopeVal, visited) } -func (ctx *context) collectLOC(fn *Function, scopeFnArgs map[fnArg]bool, scopeVal string, - visited map[*Function]bool) int { +func (ctx *context) collectLOC(iface *Interface, fn *Function, scopeFnArgs map[fnArg]bool, + scopeVal string, visited map[*Function]bool) int { // Ignore very common functions when computing reachability for complexity analysis. // Counting kmalloc/printk against each caller is not useful (they have ~10K calls). // There are also subsystem common functions (e.g. functions called in some parts of fs/net). @@ -104,11 +130,13 @@ func (ctx *context) collectLOC(fn *Function, scopeFnArgs map[fnArg]bool, scopeVa loc -= max(0, scope.EndLine-scope.StartLine) continue } + iface.TotalBlocks += scope.totalBlocks + iface.CoveredBlocks += scope.coveredBlocks for _, callee := range scope.calls { if visited[callee] || callee.callers >= commonFuncThreshold { continue } - loc += ctx.collectLOC(callee, scopeFnArgs, scopeVal, visited) + loc += ctx.collectLOC(iface, callee, scopeFnArgs, scopeVal, visited) } } return loc |
