diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2025-04-09 10:38:03 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2025-04-10 17:07:48 +0000 |
| commit | 2d9fa31cf7abaddf944824814053e67b4d5c478e (patch) | |
| tree | 12c10728542789e81ec4eef0496250933516e217 /pkg/declextract | |
| parent | a6ec893839824d1b080d8c0c02360e5db5c6d39f (diff) | |
pkg/declextract: export syscall variants as separate interfaces
Export each syscall variant (e.g. fcnt$*) as a separate interface.
Effectively these are separate syscalls. We will want this for
ioctl as well (it's not 1 interface).
Diffstat (limited to 'pkg/declextract')
| -rw-r--r-- | pkg/declextract/declextract.go | 19 | ||||
| -rw-r--r-- | pkg/declextract/entity.go | 23 | ||||
| -rw-r--r-- | pkg/declextract/interface.go | 44 | ||||
| -rw-r--r-- | pkg/declextract/typing.go | 39 |
4 files changed, 73 insertions, 52 deletions
diff --git a/pkg/declextract/declextract.go b/pkg/declextract/declextract.go index 3800ad70c..4f3c66173 100644 --- a/pkg/declextract/declextract.go +++ b/pkg/declextract/declextract.go @@ -181,7 +181,7 @@ func (ctx *context) processSyscalls() { if call.Func == "__do_sys_ioctl" { suffix = ctx.uniqualize("ioctl cmd", cmd) } - ctx.emitSyscall(&syscalls, &variant, "_"+suffix) + ctx.emitSyscall(&syscalls, &variant, "_"+suffix, cmd, varArg, cmd) } } call.returnType = ctx.inferReturnType(call.Func, call.SourceFile, -1, "") @@ -189,21 +189,30 @@ func (ctx *context) processSyscalls() { typ := ctx.inferArgType(call.Func, call.SourceFile, i, -1, "") refineFieldType(arg, typ, false) } - ctx.emitSyscall(&syscalls, call, "") + ctx.emitSyscall(&syscalls, call, "", "", -1, "") } ctx.Syscalls = sortAndDedupSlice(syscalls) } -func (ctx *context) emitSyscall(syscalls *[]*Syscall, call *Syscall, suffix string) { +func (ctx *context) emitSyscall(syscalls *[]*Syscall, call *Syscall, + suffix, cmd string, scopeArg int, scopeVal string) { fn := strings.TrimPrefix(call.Func, "__do_sys_") for _, name := range ctx.syscallRename[fn] { + syscallName := name + identifyingConst := "__NR_" + name + if cmd != "" { + syscallName += "$" + cmd + identifyingConst = cmd + } ctx.noteInterface(&Interface{ Type: IfaceSyscall, - Name: name, - IdentifyingConst: "__NR_" + name, + Name: syscallName, + IdentifyingConst: identifyingConst, Files: []string{call.SourceFile}, Func: call.Func, AutoDescriptions: true, + scopeArg: scopeArg, + scopeVal: scopeVal, }) newCall := *call newCall.Func = name + autoSuffix + suffix diff --git a/pkg/declextract/entity.go b/pkg/declextract/entity.go index 740530ca9..25c688cd4 100644 --- a/pkg/declextract/entity.go +++ b/pkg/declextract/entity.go @@ -24,15 +24,16 @@ type Output struct { } type Function struct { - Name string `json:"name,omitempty"` - File string `json:"file,omitempty"` - IsStatic bool `json:"is_static,omitempty"` + Name string `json:"name,omitempty"` + File string `json:"file,omitempty"` + StartLine int `json:"start_line,omitempty"` + EndLine int `json:"end_line,omitempty"` + IsStatic bool `json:"is_static,omitempty"` // Information about function scopes. There is a global scope (with Arg=-1), // and scope for each switch case on the function argument. Scopes []*FunctionScope `json:"scopes,omitempty"` callers int - calls []*Function facts map[string]*typingNode } @@ -41,12 +42,14 @@ type FunctionScope struct { Arg int `json:"arg"` // The set of case values for this scope. // It's empt for the global scope for the default case scope. - Values []string `json:"values,omitempty"` - LOC int `json:"loc,omitempty"` - Calls []string `json:"calls,omitempty"` - Facts []*TypingFact `json:"facts,omitempty"` - - fn *Function + Values []string `json:"values,omitempty"` + StartLine int `json:"start_line,omitempty"` + EndLine int `json:"end_line,omitempty"` + Calls []string `json:"calls,omitempty"` + Facts []*TypingFact `json:"facts,omitempty"` + + fn *Function + calls []*Function } type ConstInfo struct { diff --git a/pkg/declextract/interface.go b/pkg/declextract/interface.go index d891b4899..e7a859da9 100644 --- a/pkg/declextract/interface.go +++ b/pkg/declextract/interface.go @@ -19,6 +19,9 @@ type Interface struct { ManualDescriptions bool AutoDescriptions bool ReachableLOC int + + scopeArg int + scopeVal string } const ( @@ -38,7 +41,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.ReachableLOC = ctx.reachableLOC(iface.Func, iface.Files[0], iface.scopeArg, iface.scopeVal) slices.Sort(iface.Files) iface.Files = slices.Compact(iface.Files) if iface.Access == "" { @@ -57,7 +60,6 @@ func (ctx *context) processFunctions() { ctx.funcs[fn.Name] = fn } } - nocallers := 0 for _, fn := range ctx.Functions { for _, scope := range fn.Scopes { for _, callee := range scope.Calls { @@ -65,34 +67,26 @@ func (ctx *context) processFunctions() { if called == nil || called == fn { continue } - fn.calls = append(fn.calls, called) + scope.calls = append(scope.calls, called) called.callers++ } } - if len(fn.calls) == 0 { - nocallers++ - } } } -func (ctx *context) reachableLOC(name, file string) int { +func (ctx *context) reachableLOC(name, file string, scopeArg int, scopeVal string) int { fn := ctx.findFunc(name, file) if fn == nil { ctx.warn("can't find function %v called in %v", name, file) return 0 } - reachable := make(map[*Function]bool) - ctx.collectRachable(fn, reachable) - loc := 0 - for fn := range reachable { - for _, scope := range fn.Scopes { - loc += scope.LOC - } - } - return loc + scopeFnArgs := ctx.inferArgFlow(fnArg{fn, scopeArg}) + visited := make(map[*Function]bool) + return ctx.collectLOC(fn, scopeFnArgs, scopeVal, visited) } -func (ctx *context) collectRachable(fn *Function, reachable map[*Function]bool) { +func (ctx *context) collectLOC(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). @@ -103,13 +97,21 @@ func (ctx *context) collectRachable(fn *Function, reachable map[*Function]bool) // 3 callers - 16527 functions const commonFuncThreshold = 5 - reachable[fn] = true - for _, callee := range fn.calls { - if reachable[callee] || callee.callers >= commonFuncThreshold { + visited[fn] = true + loc := max(0, fn.EndLine-fn.StartLine-1) + for _, scope := range fn.Scopes { + if !relevantScope(scopeFnArgs, scopeVal, scope) { + loc -= max(0, scope.EndLine-scope.StartLine) continue } - ctx.collectRachable(callee, reachable) + for _, callee := range scope.calls { + if visited[callee] || callee.callers >= commonFuncThreshold { + continue + } + loc += ctx.collectLOC(callee, scopeFnArgs, scopeVal, visited) + } } + return loc } func (ctx *context) findFunc(name, file string) *Function { diff --git a/pkg/declextract/typing.go b/pkg/declextract/typing.go index 3de53ee62..04a11bbc7 100644 --- a/pkg/declextract/typing.go +++ b/pkg/declextract/typing.go @@ -260,7 +260,7 @@ func (ic *inferContext) walk(n *typingNode) { } if len(ic.path) < ic.maxDepth { for e, scopes := range n.flows[ic.flowType] { - if ic.relevantScope(scopes) { + if relevantScopes(ic.scopeFnArgs, ic.scopeVal, scopes) { ic.walk(e) } } @@ -268,26 +268,33 @@ func (ic *inferContext) walk(n *typingNode) { ic.path = ic.path[:len(ic.path)-1] } -func (ic *inferContext) relevantScope(scopes []*FunctionScope) bool { - if ic.scopeFnArgs == nil { - // We are not doing scope-limited walk, so all scopes are relevant. - return true - } +func relevantScopes(scopeFnArgs map[fnArg]bool, scopeVal string, scopes []*FunctionScope) bool { for _, scope := range scopes { - if scope.Arg == -1 { - // Always use global scope. + if relevantScope(scopeFnArgs, scopeVal, scope) { return true } - if !ic.scopeFnArgs[fnArg{scope.fn, scope.Arg}] { - // The scope argument is not related to the current scope. + } + return false +} + +func relevantScope(scopeFnArgs map[fnArg]bool, scopeVal string, scope *FunctionScope) bool { + if scopeFnArgs == nil { + // We are not doing scope-limited walk, so all scopes are relevant. + return true + } + if scope.Arg == -1 { + // Always use global scope. + return true + } + if !scopeFnArgs[fnArg{scope.fn, scope.Arg}] { + // The scope argument is not related to the current scope. + return true + } + // For the scope argument, check that it has the right value. + for _, val := range scope.Values { + if val == scopeVal { return true } - // For the scope argument, check that it has the right value. - for _, val := range scope.Values { - if val == ic.scopeVal { - return true - } - } } return false } |
