aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2025-04-10 11:34:39 +0200
committerDmitry Vyukov <dvyukov@google.com>2025-04-10 17:07:48 +0000
commit1bc60a19f725612590947493424fef2e314bd044 (patch)
tree3be2c55ec8612d430845327602d832d6eb01b479 /pkg
parent2d9fa31cf7abaddf944824814053e67b4d5c478e (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.go2
-rw-r--r--pkg/cover/html.go13
-rw-r--r--pkg/declextract/declextract.go7
-rw-r--r--pkg/declextract/entity.go6
-rw-r--r--pkg/declextract/interface.go48
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