aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-12-11 12:45:28 +0100
committerDmitry Vyukov <dvyukov@google.com>2024-12-11 15:22:17 +0000
commit66960613387e4e69dece4f03f72c65d4a081b43f (patch)
treedbb7f9559a7de53f98950e365a994118871eb463 /pkg
parentbaa3289d16dccd357662a0d20b98aa62837d147b (diff)
pkg/declextract: reduce cyclomatic complexity
Linter points to very large cyclomatic complexity/length of some functions. Fix that.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/declextract/declextract.go45
-rw-r--r--pkg/declextract/fileops.go204
2 files changed, 134 insertions, 115 deletions
diff --git a/pkg/declextract/declextract.go b/pkg/declextract/declextract.go
index c3c6d705f..30caeaa2d 100644
--- a/pkg/declextract/declextract.go
+++ b/pkg/declextract/declextract.go
@@ -174,24 +174,7 @@ func (ctx *context) fieldTypeInt(f, counts *Field, needBase bool) string {
if t.Enum != "" && counts != nil {
ctx.error("field %v is both enum %v and counts field %v", f.Name, t.Enum, counts.Name)
}
- baseType := fmt.Sprintf("int%v", t.ByteSize*8)
- // Note: we make all 8-byte syscall arguments intptr b/c for 64-bit arches it does not matter,
- // but for 32-bit arches int64 as syscall argument won't work. IIUC the ABI is that these
- // are split into 2 32-bit arguments.
- intptr := t.ByteSize == 8 && (!needBase || strings.Contains(t.Base, "long") &&
- !strings.Contains(t.Base, "long long"))
- if intptr {
- baseType = "intptr"
- }
- if t.isBigEndian && t.ByteSize != 1 {
- baseType += "be"
- }
- if f.BitWidth == t.ByteSize*8 {
- f.BitWidth = 0
- }
- if f.BitWidth != 0 {
- baseType += fmt.Sprintf(":%v", f.BitWidth)
- }
+ baseType, isIntptr := ctx.baseIntType(f, needBase)
constType := fmt.Sprintf("const[%v %v]", t.MinValue, maybeBaseType(baseType, needBase))
if f.IsAnonymous || t.IsConst {
return constType
@@ -213,7 +196,7 @@ func (ctx *context) fieldTypeInt(f, counts *Field, needBase bool) string {
case 4:
special = ctx.specialInt4(f.Name, t.Name, needBase)
case 8:
- if intptr {
+ if isIntptr {
special = ctx.specialIntptr(f.Name, t.Name, needBase)
}
}
@@ -241,6 +224,29 @@ func (ctx *context) fieldTypeInt(f, counts *Field, needBase bool) string {
return baseType
}
+func (ctx *context) baseIntType(f *Field, needBase bool) (string, bool) {
+ t := f.Type.Int
+ baseType := fmt.Sprintf("int%v", t.ByteSize*8)
+ // Note: we make all 8-byte syscall arguments intptr b/c for 64-bit arches it does not matter,
+ // but for 32-bit arches int64 as syscall argument won't work. IIUC the ABI is that these
+ // are split into 2 32-bit arguments.
+ isIntptr := t.ByteSize == 8 && (!needBase || strings.Contains(t.Base, "long") &&
+ !strings.Contains(t.Base, "long long"))
+ if isIntptr {
+ baseType = "intptr"
+ }
+ if t.isBigEndian && t.ByteSize != 1 {
+ baseType += "be"
+ }
+ if f.BitWidth == t.ByteSize*8 {
+ f.BitWidth = 0
+ }
+ if f.BitWidth != 0 {
+ baseType += fmt.Sprintf(":%v", f.BitWidth)
+ }
+ return baseType, isIntptr
+}
+
func (ctx *context) specialInt2(field, typ string, needBase bool) string {
switch {
case strings.Contains(field, "port"):
@@ -249,6 +255,7 @@ func (ctx *context) specialInt2(field, typ string, needBase bool) string {
return ""
}
+// nolint: gocyclo
func (ctx *context) specialInt4(field, typ string, needBase bool) string {
switch {
case strings.Contains(field, "ipv4") || strings.Contains(field, "ip4") ||
diff --git a/pkg/declextract/fileops.go b/pkg/declextract/fileops.go
index da1a74dc0..78016d57b 100644
--- a/pkg/declextract/fileops.go
+++ b/pkg/declextract/fileops.go
@@ -119,113 +119,125 @@ func (ctx *context) mapFopsToFiles() map[*FileOps][]string {
fopsToFiles := make(map[*FileOps][]string)
for _, file := range ctx.probe.Files {
// For each file figure out the potential file_operations that match this file best.
- funcs := fileToFuncs[file.Name]
- // First collect all candidates (all file_operations for which at least 1 callback was triggered).
- candidates := make(map[*FileOps]int)
- for fn := range funcs {
- for _, fops := range funcToFops[fn] {
- if fops.Open != "" && len(fops.ops()) == 1 {
- // If it has only open, it's not very interesting
- // (we will use generic for it below).
- continue
- }
- hasUnique := false
- for _, fn := range fops.ops() {
- if uniqueFuncs[fn] == 1 {
- hasUnique = true
- }
- }
- // If we've triggered at least one unique callback, we take this
- // file_operations in any case. Otherwise check if file_operations
- // has open/ioctl that we haven't triggered.
- // Note that it may have open/ioctl, and this is the right file_operations
- // for the file, yet we haven't triggered them for reasons described
- // in the beginning of the function.
- if !hasUnique {
- if fops.Open != "" && !funcs[fops.Open] {
- continue
- }
- if fops.Ioctl != "" && !funcs[fops.Ioctl] {
- continue
- }
- }
- candidates[fops] = 0
+ best := ctx.mapFileToFops(fileToFuncs[file.Name], funcToFops, uniqueFuncs, generic)
+ for _, fops := range best {
+ fopsToFiles[fops] = append(fopsToFiles[fops], file.Name)
+ }
+ }
+ for fops, files := range fopsToFiles {
+ slices.Sort(files)
+ fopsToFiles[fops] = files
+ }
+ return fopsToFiles
+}
+
+func (ctx *context) mapFileToFops(funcs map[string]bool, funcToFops map[string][]*FileOps,
+ uniqueFuncs map[string]int, generic *FileOps) []*FileOps {
+ // First collect all candidates (all file_operations for which at least 1 callback was triggered).
+ candidates := ctx.fileCandidates(funcs, funcToFops, uniqueFuncs)
+ if len(candidates) == 0 {
+ candidates[generic] = 0
+ }
+ // Now find the best set of candidates.
+ // There are lots of false positives due to common callback functions.
+ maxScore := 0
+ for fops := range candidates {
+ ops := fops.ops()
+ // All else being equal prefer file_operations with more callbacks defined.
+ score := len(ops)
+ for _, fn := range ops {
+ if !funcs[fn] {
+ continue
+ }
+ // Matched callbacks increase the score.
+ score += 10
+ // If we matched ioctl, bump score by a lot.
+ // We do want to emit ioctl's b/c they the only non-trivial
+ // operations we emit at the moment.
+ if fn == fops.Ioctl {
+ score += 100
+ }
+ // Unique callbacks are the strongest prioritization signal.
+ // Besides some corner cases there is no way we can reach a unique callback
+ // from a wrong file (a corner case would be in one callback calls another
+ // callback directly).
+ if uniqueFuncs[fn] == 1 {
+ score += 1000
}
}
- if len(candidates) == 0 {
- candidates[generic] = 0
+ candidates[fops] = score
+ maxScore = max(maxScore, score)
+ }
+ // Now, take the candidates with the highest score (there still may be several of them).
+ var best []*FileOps
+ for fops, score := range candidates {
+ if score == maxScore {
+ best = append(best, fops)
}
- // Now find the best set of candidates.
- // There are lots of false positives due to common callback functions.
- maxScore := 0
- for fops := range candidates {
- ops := fops.ops()
- // All else being equal prefer file_operations with more callbacks defined.
- score := len(ops)
- for _, fn := range ops {
- if !funcs[fn] {
- continue
- }
- // Matched callbacks increase the score.
- score += 10
- // If we matched ioctl, bump score by a lot.
- // We do want to emit ioctl's b/c they the only non-trivial
- // operations we emit at the moment.
- if fn == fops.Ioctl {
- score += 100
- }
- // Unique callbacks are the strongest prioritization signal.
- // Besides some corner cases there is no way we can reach a unique callback
- // from a wrong file (a corner case would be in one callback calls another
- // callback directly).
- if uniqueFuncs[fn] == 1 {
- score += 1000
- }
+ }
+ best = sortAndDedupSlice(best)
+ // Now, filter out some excessive file_operations.
+ // An example of an excessive case is if we have 2 file_operations with just read+write,
+ // currently we emit generic read/write operations, so we would emit completly equal
+ // descriptions for both. Ioctl commands is the only non-generic descriptions we emit now,
+ // so if a file_operations has any commands, it won't be considered excessive.
+ // Note that if we generate specialized descriptions for read/write/mmap in future,
+ // then these won't be considered excessive as well.
+ excessive := make(map[*FileOps]bool)
+ for i := 0; i < len(best); i++ {
+ for j := i + 1; j < len(best); j++ {
+ a, b := best[i], best[j]
+ if (a.Ioctl == b.Ioctl || len(a.IoctlCmds)+len(b.IoctlCmds) == 0) &&
+ (a.Read == "") == (b.Read == "") &&
+ (a.Write == "") == (b.Write == "") &&
+ (a.Mmap == "") == (b.Mmap == "") &&
+ (a.Ioctl == "") == (b.Ioctl == "") {
+ excessive[b] = true
}
- candidates[fops] = score
- maxScore = max(maxScore, score)
}
- // Now, take the candidates with the highest score (there still may be several of them).
- var best []*FileOps
- for fops, score := range candidates {
- if score == maxScore {
- best = append(best, fops)
+ }
+ // Finally record the file for the best non-excessive file_operations
+ // (there are still can be several of them).
+ best = slices.DeleteFunc(best, func(fops *FileOps) bool {
+ return excessive[fops]
+ })
+ return best
+}
+
+func (ctx *context) fileCandidates(funcs map[string]bool, funcToFops map[string][]*FileOps,
+ uniqueFuncs map[string]int) map[*FileOps]int {
+ candidates := make(map[*FileOps]int)
+ for fn := range funcs {
+ for _, fops := range funcToFops[fn] {
+ if fops.Open != "" && len(fops.ops()) == 1 {
+ // If it has only open, it's not very interesting
+ // (we will use generic for it below).
+ continue
}
- }
- best = sortAndDedupSlice(best)
- // Now, filter out some excessive file_operations.
- // An example of an excessive case is if we have 2 file_operations with just read+write,
- // currently we emit generic read/write operations, so we would emit completly equal
- // descriptions for both. Ioctl commands is the only non-generic descriptions we emit now,
- // so if a file_operations has any commands, it won't be considered excessive.
- // Note that if we generate specialized descriptions for read/write/mmap in future,
- // then these won't be considered excessive as well.
- excessive := make(map[*FileOps]bool)
- for i := 0; i < len(best); i++ {
- for j := i + 1; j < len(best); j++ {
- a, b := best[i], best[j]
- if (a.Ioctl == b.Ioctl || len(a.IoctlCmds)+len(b.IoctlCmds) == 0) &&
- (a.Read == "") == (b.Read == "") &&
- (a.Write == "") == (b.Write == "") &&
- (a.Mmap == "") == (b.Mmap == "") &&
- (a.Ioctl == "") == (b.Ioctl == "") {
- excessive[b] = true
+ hasUnique := false
+ for _, fn := range fops.ops() {
+ if uniqueFuncs[fn] == 1 {
+ hasUnique = true
}
}
- }
- // Finally record the file for the best non-excessive file_operations
- // (there are still can be several of them).
- for _, fops := range best {
- if !excessive[fops] {
- fopsToFiles[fops] = append(fopsToFiles[fops], file.Name)
+ // If we've triggered at least one unique callback, we take this
+ // file_operations in any case. Otherwise check if file_operations
+ // has open/ioctl that we haven't triggered.
+ // Note that it may have open/ioctl, and this is the right file_operations
+ // for the file, yet we haven't triggered them for reasons described
+ // in the beginning of the function.
+ if !hasUnique {
+ if fops.Open != "" && !funcs[fops.Open] {
+ continue
+ }
+ if fops.Ioctl != "" && !funcs[fops.Ioctl] {
+ continue
+ }
}
+ candidates[fops] = 0
}
}
- for fops, files := range fopsToFiles {
- slices.Sort(files)
- fopsToFiles[fops] = files
- }
- return fopsToFiles
+ return candidates
}
func (fops *FileOps) ops() []string {