summaryrefslogtreecommitdiff
path: root/internal/generate/codegen.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/generate/codegen.go')
-rw-r--r--internal/generate/codegen.go152
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
+}