diff options
Diffstat (limited to 'internal/generate/codegen.go')
| -rw-r--r-- | internal/generate/codegen.go | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/internal/generate/codegen.go b/internal/generate/codegen.go new file mode 100644 index 0000000..9b9f52c --- /dev/null +++ b/internal/generate/codegen.go @@ -0,0 +1,152 @@ +package generate + +import ( + "fmt" + "sort" + "strings" +) + +// Syscall groups enter+exit formats by syscall name. +type Syscall struct { + Name string + Enter *Format + Exit *Format +} + +// GeneratedTracepoint holds a classified format ready for code generation. +type GeneratedTracepoint struct { + Format *Format + Classification ClassificationResult +} + +// GenerateTracepointsC produces the full generated_tracepoints.c content from +// concatenated sysfs format data parsed into formats. +func GenerateTracepointsC(formats []Format) string { + syscalls := groupBySyscall(formats) + var b strings.Builder + + b.WriteString("// Code generated - don't change manually!\n\n") + + var accepted []GeneratedTracepoint + for _, sc := range syscalls { + tracepoints, reason := classifySyscall(sc) + if reason != "" { + fmt.Fprintf(&b, "/// %s\n", reason) + continue + } + accepted = append(accepted, tracepoints...) + } + + sort.Slice(accepted, func(i, j int) bool { + return accepted[i].Format.ID > accepted[j].Format.ID + }) + + b.WriteString("\n") + for _, tp := range accepted { + fmt.Fprintf(&b, "#define %s %d\n", strings.ToUpper(tp.Format.Name), tp.Format.ID) + } + b.WriteString("\n") + + for _, tp := range accepted { + b.WriteString(generateBPFHandler(tp)) + b.WriteString("\n") + } + + return b.String() +} + +func groupBySyscall(formats []Format) []Syscall { + m := make(map[string]*Syscall) + var order []string + + for i := range formats { + f := &formats[i] + parts := strings.SplitN(f.Name, "_", 3) + if len(parts) < 3 { + continue + } + enterExit := parts[1] + what := parts[2] + + sc, ok := m[what] + if !ok { + sc = &Syscall{Name: what} + m[what] = sc + order = append(order, what) + } + if enterExit == "enter" { + sc.Enter = f + } else { + sc.Exit = f + } + } + + result := make([]Syscall, 0, len(order)) + for _, name := range order { + result = append(result, *m[name]) + } + return result +} + +func classifySyscall(sc Syscall) ([]GeneratedTracepoint, string) { + var enterClass, exitClass ClassificationResult + allCanGenerate := true + + if sc.Enter != nil { + enterClass = ClassifyFormat(sc.Enter) + if enterClass.Kind == KindNone { + allCanGenerate = false + } + } else { + allCanGenerate = false + } + + if sc.Exit != nil { + exitClass = ClassifyFormat(sc.Exit) + if exitClass.Kind == KindNone { + allCanGenerate = false + } + } else { + allCanGenerate = false + } + + if !allCanGenerate { + names := syscallFormatNames(sc) + return nil, fmt.Sprintf("Ignoring %s as possibly not file I/O related", strings.Join(names, " ")) + } + + if isEnterRejected(enterClass.Kind) { + names := syscallFormatNames(sc) + return nil, fmt.Sprintf("Ignoring %s as enter-rejected", strings.Join(names, " ")) + } + + var result []GeneratedTracepoint + if sc.Enter != nil { + result = append(result, GeneratedTracepoint{Format: sc.Enter, Classification: enterClass}) + } + if sc.Exit != nil { + result = append(result, GeneratedTracepoint{Format: sc.Exit, Classification: exitClass}) + } + return result, "" +} + +func isEnterRejected(kind TracepointKind) bool { + switch kind { + case KindFd, KindName, KindOpen, KindPathname, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt: + return false + default: + return true + } +} + +func syscallFormatNames(sc Syscall) []string { + var names []string + if sc.Enter != nil { + names = append(names, sc.Enter.Name) + } + if sc.Exit != nil { + names = append(names, sc.Exit.Name) + } + sort.Strings(names) + return names +} |
