diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-12-11 12:45:28 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-12-11 15:22:17 +0000 |
| commit | 66960613387e4e69dece4f03f72c65d4a081b43f (patch) | |
| tree | dbb7f9559a7de53f98950e365a994118871eb463 /pkg | |
| parent | baa3289d16dccd357662a0d20b98aa62837d147b (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.go | 45 | ||||
| -rw-r--r-- | pkg/declextract/fileops.go | 204 |
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 { |
