aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/declextract
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/declextract
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/declextract')
-rw-r--r--pkg/declextract/declextract.go7
-rw-r--r--pkg/declextract/entity.go6
-rw-r--r--pkg/declextract/interface.go48
3 files changed, 47 insertions, 14 deletions
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