summaryrefslogtreecommitdiff
path: root/internal/generate/classify.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 11:51:01 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 11:51:01 +0200
commit6c912a9d72ae2a43923c638538d320e6bf585952 (patch)
tree727f66d158210e01abf8c18a83ef4db6066e0c1a /internal/generate/classify.go
parent32136b8cb18944157ff1f361bc0755f6b627fd47 (diff)
Migrate make targets to mage
Amp-Thread-ID: https://ampcode.com/threads/T-019c7f4e-cc5f-76f1-aaf0-dd7cbaabbb18 Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'internal/generate/classify.go')
-rw-r--r--internal/generate/classify.go214
1 files changed, 214 insertions, 0 deletions
diff --git a/internal/generate/classify.go b/internal/generate/classify.go
new file mode 100644
index 0000000..75a12fe
--- /dev/null
+++ b/internal/generate/classify.go
@@ -0,0 +1,214 @@
+package generate
+
+import "strings"
+
+type TracepointKind int
+
+const (
+ KindNone TracepointKind = iota
+ KindFd
+ KindOpen
+ KindPathname
+ KindName
+ KindRet
+ KindFcntl
+ KindNull
+ KindDup3
+ KindOpenByHandleAt
+)
+
+type RetClassification string
+
+const (
+ Unclassified RetClassification = "UNCLASSIFIED"
+ ReadClassified RetClassification = "READ_CLASSIFIED"
+ WriteClassified RetClassification = "WRITE_CLASSIFIED"
+ TransferClassified RetClassification = "TRANSFER_CLASSIFIED"
+)
+
+type ClassificationResult struct {
+ Kind TracepointKind
+ PathnameField string // for KindPathname: "pathname", "path", or "filename"
+}
+
+// ClassifyFormat determines the tracepoint kind for a parsed format section.
+// It mirrors the Raku multi-dispatch: name-based ignores take priority,
+// then name-only mappings, then each external field is tried in order until
+// one matches a name+field or generic field pattern.
+func ClassifyFormat(f *Format) ClassificationResult {
+ if len(f.ExternalFields) == 0 {
+ return ClassificationResult{Kind: KindNone}
+ }
+
+ if shouldIgnore(f.Name) {
+ return ClassificationResult{Kind: KindNone}
+ }
+
+ if r, ok := classifyNameOnly(f.Name); ok {
+ return r
+ }
+
+ for _, field := range f.ExternalFields {
+ if field.Name == "__syscall_nr" {
+ continue
+ }
+ if r, ok := classifyNameAndField(f.Name, field.Type, field.Name); ok {
+ return r
+ }
+ if r, ok := classifyByField(field.Type, field.Name); ok {
+ return r
+ }
+ }
+
+ return ClassificationResult{Kind: KindNone}
+}
+
+func shouldIgnore(name string) bool {
+ prefixIgnores := []string{
+ "sys_enter_mknod",
+ "sys_enter_execve",
+ "sys_enter_accept",
+ "sys_enter_listen",
+ "sys_enter_epoll",
+ }
+ for _, p := range prefixIgnores {
+ if strings.HasPrefix(name, p) {
+ return true
+ }
+ }
+
+ if strings.HasPrefix(name, "sys_enter_") {
+ containsIgnores := []string{"recv", "send", "sock", "inotify", "pidfd"}
+ for _, sub := range containsIgnores {
+ if strings.Contains(name, sub) {
+ return true
+ }
+ }
+ }
+
+ exactIgnores := map[string]bool{
+ "sys_enter_bind": true,
+ "sys_enter_setns": true,
+ "sys_enter_shutdown": true,
+ "sys_enter_connect": true,
+ "sys_enter_fanotify_init": true,
+ "sys_enter_getpeername": true,
+ }
+ return exactIgnores[name]
+}
+
+// classifyNameOnly handles tracepoints classified by name alone,
+// independent of any field.
+func classifyNameOnly(name string) (ClassificationResult, bool) {
+ switch name {
+ case "sys_enter_open_by_handle_at":
+ return ClassificationResult{Kind: KindOpenByHandleAt}, true
+ case "sys_enter_fcntl":
+ return ClassificationResult{Kind: KindFcntl}, true
+ case "sys_enter_syslog":
+ return ClassificationResult{Kind: KindNull}, true
+ case "sys_enter_sync":
+ return ClassificationResult{Kind: KindNull}, true
+ }
+ if strings.HasPrefix(name, "sys_enter_io_") {
+ return ClassificationResult{Kind: KindNull}, true
+ }
+ return ClassificationResult{}, false
+}
+
+// classifyNameAndField handles tracepoints that need both the name and
+// a specific field to classify.
+func classifyNameAndField(name, fieldType, fieldName string) (ClassificationResult, bool) {
+ switch name {
+ case "sys_enter_dup":
+ if fieldType == "unsigned int" && fieldName == "fildes" {
+ return ClassificationResult{Kind: KindFd}, true
+ }
+ case "sys_enter_dup2":
+ if fieldType == "unsigned int" && fieldName == "oldfd" {
+ return ClassificationResult{Kind: KindFd}, true
+ }
+ case "sys_enter_dup3":
+ if fieldType == "unsigned int" && fieldName == "oldfd" {
+ return ClassificationResult{Kind: KindDup3}, true
+ }
+ }
+
+ if strings.HasPrefix(name, "sys_enter") &&
+ strings.Contains(name, "open") &&
+ fieldType == "const char *" && fieldName == "filename" {
+ return ClassificationResult{Kind: KindOpen}, true
+ }
+
+ return ClassificationResult{}, false
+}
+
+func classifyByField(fieldType, fieldName string) (ClassificationResult, bool) {
+ switch {
+ case fieldName == "fd" && isFdType(fieldType):
+ return ClassificationResult{Kind: KindFd}, true
+ case fieldType == "const char *" && fieldName == "newname":
+ return ClassificationResult{Kind: KindName}, true
+ case fieldType == "const char *" && fieldName == "pathname":
+ return ClassificationResult{Kind: KindPathname, PathnameField: "pathname"}, true
+ case fieldType == "const char *" && fieldName == "path":
+ return ClassificationResult{Kind: KindPathname, PathnameField: "path"}, true
+ case fieldType == "const char *" && fieldName == "filename":
+ return ClassificationResult{Kind: KindPathname, PathnameField: "filename"}, true
+ case fieldType == "long" && fieldName == "ret":
+ return ClassificationResult{Kind: KindRet}, true
+ }
+ return ClassificationResult{}, false
+}
+
+func isFdType(t string) bool {
+ return t == "unsigned int" || t == "unsigned long" || t == "int"
+}
+
+// ClassifyRet returns the RetClassification for a syscall exit name.
+func ClassifyRet(name string) RetClassification {
+ syscall := strings.ToLower(strings.TrimPrefix(name, "sys_exit_"))
+ if c, ok := retClassifications[syscall]; ok {
+ return c
+ }
+ return Unclassified
+}
+
+var retClassifications = map[string]RetClassification{
+ "fgetxattr": ReadClassified,
+ "flistxattr": ReadClassified,
+ "getdents": ReadClassified,
+ "getdents64": ReadClassified,
+ "getxattr": ReadClassified,
+ "lgetxattr": ReadClassified,
+ "listxattr": ReadClassified,
+ "llistxattr": ReadClassified,
+ "pread64": ReadClassified,
+ "preadv": ReadClassified,
+ "preadv2": ReadClassified,
+ "process_vm_readv": ReadClassified,
+ "read": ReadClassified,
+ "readlink": ReadClassified,
+ "readlinkat": ReadClassified,
+ "readv": ReadClassified,
+ "recvmmsg": ReadClassified,
+ "recvmsg": ReadClassified,
+ "recvfrom": ReadClassified,
+ "syslog": ReadClassified,
+
+ "copy_file_range": TransferClassified,
+ "sendfile64": TransferClassified,
+ "splice": TransferClassified,
+ "tee": TransferClassified,
+ "vmsplice": TransferClassified,
+
+ "process_vm_writev": WriteClassified,
+ "pwrite64": WriteClassified,
+ "pwritev": WriteClassified,
+ "pwritev2": WriteClassified,
+ "sendmmsg": WriteClassified,
+ "sendmsg": WriteClassified,
+ "sendto": WriteClassified,
+ "write": WriteClassified,
+ "writev": WriteClassified,
+}