summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-18 14:14:33 +0300
committerPaul Buetow <paul@buetow.org>2026-05-18 14:14:33 +0300
commit519cd996b5a7fede23b8b23f3c101d10b26111de (patch)
tree93f14954325e936d459003f334b667d2afec93b6 /internal/generate
parentd68e12c92f2aec9b59a849480e0788ab5d798b2a (diff)
k6: emit tracepoints for all syscall families
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/classify.go38
-rw-r--r--internal/generate/classify_test.go105
-rw-r--r--internal/generate/codegen.go12
-rw-r--r--internal/generate/codegen_test.go54
-rw-r--r--internal/generate/family.go166
-rw-r--r--internal/generate/family_test.go51
-rw-r--r--internal/generate/format.go2
7 files changed, 321 insertions, 107 deletions
diff --git a/internal/generate/classify.go b/internal/generate/classify.go
index f3b9a44..b96ee0d 100644
--- a/internal/generate/classify.go
+++ b/internal/generate/classify.go
@@ -40,10 +40,6 @@ func ClassifyFormat(f *Format) ClassificationResult {
return ClassificationResult{Kind: KindNone}
}
- if shouldIgnore(f.Name) {
- return ClassificationResult{Kind: KindNone}
- }
-
if r, ok := classifyNameOnly(f.Name); ok {
return r
}
@@ -63,40 +59,6 @@ func ClassifyFormat(f *Format) ClassificationResult {
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"}
- 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) {
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index 301d4bc..f02f7de 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -223,77 +223,38 @@ func TestClassifyRetExitSymlink(t *testing.T) {
}
}
-// --- Ignore tests ---
-
-func TestIgnoreMknod(t *testing.T) {
+func TestClassifyPathnameMknod(t *testing.T) {
r := classifyFromData(t, FormatMknod)
- if r.Kind != KindNone {
- t.Errorf("mknod: got kind %d, want KindNone (ignored)", r.Kind)
+ if r.Kind != KindPathname {
+ t.Errorf("mknod: got kind %d, want KindPathname", r.Kind)
}
}
-func TestIgnoreExecve(t *testing.T) {
+func TestClassifyPathnameExecve(t *testing.T) {
r := classifyFromData(t, FormatExecve)
- if r.Kind != KindNone {
- t.Errorf("execve: got kind %d, want KindNone (ignored)", r.Kind)
+ if r.Kind != KindPathname {
+ t.Errorf("execve: got kind %d, want KindPathname", r.Kind)
}
}
-func TestIgnoreAccept(t *testing.T) {
+func TestClassifyFdAccept(t *testing.T) {
r := classifyFromData(t, FormatAccept)
- if r.Kind != KindNone {
- t.Errorf("accept: got kind %d, want KindNone (ignored)", r.Kind)
+ if r.Kind != KindFd {
+ t.Errorf("accept: got kind %d, want KindFd", r.Kind)
}
}
-func TestIgnoreSocket(t *testing.T) {
+func TestClassifySocketRequiresGenerationFallback(t *testing.T) {
r := classifyFromData(t, FormatSocket)
if r.Kind != KindNone {
- t.Errorf("socket: got kind %d, want KindNone (ignored)", r.Kind)
+ t.Errorf("socket: got kind %d, want KindNone before generation fallback", r.Kind)
}
}
-func TestIgnoreKill(t *testing.T) {
+func TestClassifyKillRequiresGenerationFallback(t *testing.T) {
r := classifyFromData(t, FormatKill)
if r.Kind != KindNone {
- t.Errorf("kill: got kind %d, want KindNone (no matching type)", r.Kind)
- }
-}
-
-func TestShouldIgnorePatterns(t *testing.T) {
- ignoreNames := []string{
- "sys_enter_mknod", "sys_enter_mknodat",
- "sys_enter_execve", "sys_enter_execveat",
- "sys_enter_accept", "sys_enter_accept4",
- "sys_enter_listen",
- "sys_enter_epoll_ctl", "sys_enter_epoll_pwait",
- "sys_enter_recvfrom", "sys_enter_recvmsg", "sys_enter_recvmmsg",
- "sys_enter_sendto", "sys_enter_sendmsg", "sys_enter_sendmmsg",
- "sys_enter_socket", "sys_enter_socketpair", "sys_enter_getsockname",
- "sys_enter_inotify_init", "sys_enter_inotify_add_watch",
- "sys_enter_bind", "sys_enter_setns", "sys_enter_shutdown",
- "sys_enter_connect", "sys_enter_fanotify_init", "sys_enter_getpeername",
- }
- for _, name := range ignoreNames {
- if !shouldIgnore(name) {
- t.Errorf("shouldIgnore(%q) = false, want true", name)
- }
- }
-}
-
-func TestShouldNotIgnore(t *testing.T) {
- noIgnore := []string{
- "sys_enter_read", "sys_enter_write", "sys_enter_openat",
- "sys_enter_close", "sys_enter_rename", "sys_enter_unlink",
- "sys_enter_copy_file_range",
- "sys_enter_msync",
- "sys_enter_pidfd_getfd",
- "sys_exit_read", "sys_exit_openat",
- }
- for _, name := range noIgnore {
- if shouldIgnore(name) {
- t.Errorf("shouldIgnore(%q) = true, want false", name)
- }
+ t.Errorf("kill: got kind %d, want KindNone before generation fallback", r.Kind)
}
}
@@ -324,6 +285,11 @@ func TestClassifySyscallPairAccepted(t *testing.T) {
{"io_uring_register", FormatIoUringRegister, FormatExitIoUringRegister, KindFd},
{"pread64", FormatPread64, FormatExitPread64, KindFd},
{"symlink", FormatSymlink, FormatExitSymlink, KindName},
+ {"mknod", FormatMknod, FormatExitMknod, KindPathname},
+ {"execve", FormatExecve, FormatExitExecve, KindPathname},
+ {"accept", FormatAccept, FormatExitAccept, KindFd},
+ {"socket", FormatSocket, FormatExitSocket, KindNull},
+ {"kill", FormatKill, FormatExitKill, KindNull},
}
for _, tt := range tests {
@@ -337,25 +303,36 @@ func TestClassifySyscallPairAccepted(t *testing.T) {
}
}
-func TestClassifySyscallPairIgnored(t *testing.T) {
+func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) {
tests := []struct {
- name string
- enter string
- exit string
+ name string
+ enter string
+ exit string
+ family SyscallFamily
}{
- {"mknod", FormatMknod, FormatExitMknod},
- {"execve", FormatExecve, FormatExitExecve},
- {"accept", FormatAccept, FormatExitAccept},
- {"socket", FormatSocket, FormatExitSocket},
- {"kill", FormatKill, FormatExitKill},
+ {"mknod", FormatMknod, FormatExitMknod, FamilyFS},
+ {"execve", FormatExecve, FormatExitExecve, FamilyProcess},
+ {"accept", FormatAccept, FormatExitAccept, FamilyNetwork},
+ {"socket", FormatSocket, FormatExitSocket, FamilyNetwork},
+ {"kill", FormatKill, FormatExitKill, FamilySignals},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
input := tt.enter + "\n" + tt.exit
- output := GenerateTracepointsC(mustParseAll(t, input))
- if !strings.Contains(output, "Ignoring") {
- t.Errorf("syscall %s was accepted, expected ignored", tt.name)
+ formats := mustParseAll(t, input)
+ if formats[0].Family != tt.family {
+ t.Fatalf("%s family = %s, want %s", tt.name, formats[0].Family, tt.family)
+ }
+ output := GenerateTracepointsC(formats)
+ if strings.Contains(output, "Ignoring") {
+ t.Errorf("syscall %s was ignored, expected accepted", tt.name)
+ }
+ if !strings.Contains(output, `SEC("tracepoint/syscalls/sys_enter_`+tt.name+`")`) {
+ t.Errorf("syscall %s missing enter handler", tt.name)
+ }
+ if !strings.Contains(output, `SEC("tracepoint/syscalls/sys_exit_`+tt.name+`")`) {
+ t.Errorf("syscall %s missing exit handler", tt.name)
}
})
}
diff --git a/internal/generate/codegen.go b/internal/generate/codegen.go
index e3ec0ef..3848b93 100644
--- a/internal/generate/codegen.go
+++ b/internal/generate/codegen.go
@@ -94,7 +94,7 @@ func classifySyscall(sc Syscall) ([]GeneratedTracepoint, string) {
allCanGenerate := true
if sc.Enter != nil {
- enterClass = ClassifyFormat(sc.Enter)
+ enterClass = classifyEnterForGeneration(sc.Enter)
if enterClass.Kind == KindNone {
allCanGenerate = false
}
@@ -113,7 +113,7 @@ func classifySyscall(sc Syscall) ([]GeneratedTracepoint, string) {
if !allCanGenerate {
names := syscallFormatNames(sc)
- return nil, fmt.Sprintf("Ignoring %s as possibly not file I/O related", strings.Join(names, " "))
+ return nil, fmt.Sprintf("Skipping %s as incomplete or unclassifiable", strings.Join(names, " "))
}
if isEnterRejected(enterClass.Kind) {
@@ -131,6 +131,14 @@ func classifySyscall(sc Syscall) ([]GeneratedTracepoint, string) {
return result, ""
}
+func classifyEnterForGeneration(f *Format) ClassificationResult {
+ classification := ClassifyFormat(f)
+ if classification.Kind != KindNone || len(f.ExternalFields) == 0 {
+ return classification
+ }
+ return ClassificationResult{Kind: KindNull}
+}
+
// isEnterRejected reports whether kind must not appear on a syscall-enter
// tracepoint. The answer comes from the kindRegistry so no switch statement
// needs updating when a new TracepointKind is added.
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index 7a7d469..a448162 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -195,10 +195,50 @@ func TestGenerateNameToHandleAtHandler(t *testing.T) {
requireContains(t, output, "bpf_probe_read_user_str(ev->pathname, sizeof(ev->pathname), (void*)ctx->args[1]);")
}
-func TestGenerateIgnoredComment(t *testing.T) {
+func TestGenerateFallbackNullHandler(t *testing.T) {
output := generateFromPair(t, FormatKill, FormatExitKill)
- requireContains(t, output, "/// Ignoring sys_enter_kill sys_exit_kill as possibly not file I/O related")
+ requireContains(t, output, `SEC("tracepoint/syscalls/sys_enter_kill")`)
+ requireContains(t, output, "struct null_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_NULL_EVENT;")
+ requireContains(t, output, `SEC("tracepoint/syscalls/sys_exit_kill")`)
+ requireContains(t, output, "ev->event_type = EXIT_RET_EVENT;")
+}
+
+func TestGenerateHandlersForEverySyscallFamily(t *testing.T) {
+ tests := []struct {
+ syscall string
+ family SyscallFamily
+ }{
+ {"accept", FamilyNetwork},
+ {"pipe2", FamilyIPC},
+ {"munmap", FamilyMemory},
+ {"execve", FamilyProcess},
+ {"kill", FamilySignals},
+ {"nanosleep", FamilyTime},
+ {"sched_yield", FamilySched},
+ {"mknod", FamilyFS},
+ {"epoll_wait", FamilyPolling},
+ {"io_setup", FamilyAIO},
+ {"bpf", FamilySecurity},
+ {"sysinfo", FamilyMisc},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.syscall, func(t *testing.T) {
+ input := syntheticPair(tt.syscall)
+ formats := mustParseAll(t, input)
+ if formats[0].Family != tt.family {
+ t.Fatalf("%s family = %s, want %s", tt.syscall, formats[0].Family, tt.family)
+ }
+ output := GenerateTracepointsC(formats)
+ if strings.Contains(output, "Skipping") {
+ t.Fatalf("%s was skipped: %s", tt.syscall, output)
+ }
+ requireContains(t, output, `SEC("tracepoint/syscalls/sys_enter_`+tt.syscall+`")`)
+ requireContains(t, output, `SEC("tracepoint/syscalls/sys_exit_`+tt.syscall+`")`)
+ })
+ }
}
func TestGenerateDefineConstants(t *testing.T) {
@@ -333,12 +373,20 @@ func TestGroupBySyscallInvalid(t *testing.T) {
func TestClassifySyscallNoExit(t *testing.T) {
formats := mustParseAll(t, FormatRead)
output := GenerateTracepointsC(formats)
- requireContains(t, output, "Ignoring")
+ requireContains(t, output, "Skipping")
if strings.Contains(output, "SEC(") {
t.Error("syscall with only enter and no exit should be ignored")
}
}
+func syntheticPair(syscall string) string {
+ enter := strings.Replace(FormatKill, "sys_enter_kill", "sys_enter_"+syscall, 1)
+ enter = strings.Replace(enter, "ID: 183", "ID: 1001", 1)
+ exit := strings.Replace(FormatExitKill, "sys_exit_kill", "sys_exit_"+syscall, 1)
+ exit = strings.Replace(exit, "ID: 182", "ID: 1000", 1)
+ return enter + "\n" + exit
+}
+
func requireContains(t *testing.T, haystack, needle string) {
t.Helper()
if !strings.Contains(haystack, needle) {
diff --git a/internal/generate/family.go b/internal/generate/family.go
new file mode 100644
index 0000000..f39b13f
--- /dev/null
+++ b/internal/generate/family.go
@@ -0,0 +1,166 @@
+package generate
+
+import "strings"
+
+// SyscallFamily is the broad syscall grouping attached to every parsed format.
+type SyscallFamily string
+
+const (
+ FamilyNetwork SyscallFamily = "Network"
+ FamilyIPC SyscallFamily = "IPC"
+ FamilyMemory SyscallFamily = "Memory"
+ FamilyProcess SyscallFamily = "Process"
+ FamilySignals SyscallFamily = "Signals"
+ FamilyTime SyscallFamily = "Time"
+ FamilySched SyscallFamily = "Sched"
+ FamilyFS SyscallFamily = "FS"
+ FamilyPolling SyscallFamily = "Polling"
+ FamilyAIO SyscallFamily = "AIO"
+ FamilySecurity SyscallFamily = "Security"
+ FamilyMisc SyscallFamily = "Misc"
+)
+
+var syscallFamilies = map[string]SyscallFamily{
+ "accept": FamilyNetwork, "accept4": FamilyNetwork, "bind": FamilyNetwork,
+ "connect": FamilyNetwork, "getpeername": FamilyNetwork, "getsockname": FamilyNetwork,
+ "getsockopt": FamilyNetwork, "listen": FamilyNetwork, "recvfrom": FamilyNetwork,
+ "recvmmsg": FamilyNetwork, "recvmsg": FamilyNetwork, "sendfile64": FamilyNetwork,
+ "sendmmsg": FamilyNetwork, "sendmsg": FamilyNetwork, "sendto": FamilyNetwork,
+ "setsockopt": FamilyNetwork, "shutdown": FamilyNetwork, "socket": FamilyNetwork,
+ "socketpair": FamilyNetwork, "splice": FamilyNetwork, "tee": FamilyNetwork,
+
+ "eventfd": FamilyIPC, "eventfd2": FamilyIPC, "inotify_add_watch": FamilyIPC,
+ "inotify_init": FamilyIPC, "inotify_init1": FamilyIPC, "inotify_rm_watch": FamilyIPC,
+ "memfd_create": FamilyIPC, "memfd_secret": FamilyIPC, "mq_getsetattr": FamilyIPC,
+ "mq_notify": FamilyIPC, "mq_open": FamilyIPC, "mq_timedreceive": FamilyIPC,
+ "mq_timedsend": FamilyIPC, "mq_unlink": FamilyIPC, "msgctl": FamilyIPC,
+ "msgget": FamilyIPC, "msgrcv": FamilyIPC, "msgsnd": FamilyIPC,
+ "pidfd_getfd": FamilyIPC, "pidfd_open": FamilyIPC, "pidfd_send_signal": FamilyIPC,
+ "pipe": FamilyIPC, "pipe2": FamilyIPC, "semctl": FamilyIPC, "semget": FamilyIPC,
+ "semop": FamilyIPC, "semtimedop": FamilyIPC, "shmat": FamilyIPC,
+ "shmctl": FamilyIPC, "shmdt": FamilyIPC, "shmget": FamilyIPC,
+ "signalfd": FamilyIPC, "signalfd4": FamilyIPC, "timerfd_create": FamilyIPC,
+ "timerfd_gettime": FamilyIPC, "timerfd_settime": FamilyIPC, "userfaultfd": FamilyIPC,
+
+ "brk": FamilyMemory, "madvise": FamilyMemory, "map_shadow_stack": FamilyMemory,
+ "mbind": FamilyMemory, "membarrier": FamilyMemory, "migrate_pages": FamilyMemory,
+ "mincore": FamilyMemory, "mlock": FamilyMemory, "mlock2": FamilyMemory,
+ "mlockall": FamilyMemory, "mmap": FamilyMemory, "mmap2": FamilyMemory,
+ "mprotect": FamilyMemory, "mremap": FamilyMemory, "mseal": FamilyMemory,
+ "munlock": FamilyMemory, "munlockall": FamilyMemory, "munmap": FamilyMemory,
+ "move_pages": FamilyMemory, "pkey_alloc": FamilyMemory, "pkey_free": FamilyMemory,
+ "pkey_mprotect": FamilyMemory, "process_madvise": FamilyMemory,
+ "process_mrelease": FamilyMemory, "process_vm_readv": FamilyMemory,
+ "process_vm_writev": FamilyMemory, "remap_file_pages": FamilyMemory,
+ "set_mempolicy": FamilyMemory, "set_mempolicy_home_node": FamilyMemory,
+
+ "arch_prctl": FamilyProcess, "clone": FamilyProcess, "clone3": FamilyProcess,
+ "execve": FamilyProcess, "execveat": FamilyProcess, "exit": FamilyProcess,
+ "exit_group": FamilyProcess, "fork": FamilyProcess, "getegid": FamilyProcess,
+ "geteuid": FamilyProcess, "getgid": FamilyProcess, "getgroups": FamilyProcess,
+ "getpgid": FamilyProcess, "getpgrp": FamilyProcess, "getpid": FamilyProcess,
+ "getppid": FamilyProcess, "getpriority": FamilyProcess, "getresgid": FamilyProcess,
+ "getresuid": FamilyProcess, "getrlimit": FamilyProcess, "getrusage": FamilyProcess,
+ "getsid": FamilyProcess, "gettid": FamilyProcess, "getuid": FamilyProcess,
+ "kcmp": FamilyProcess, "personality": FamilyProcess, "pivot_root": FamilyProcess,
+ "prctl": FamilyProcess, "prlimit64": FamilyProcess, "reboot": FamilyProcess,
+ "restart_syscall": FamilyProcess, "set_tid_address": FamilyProcess,
+ "setfsuid": FamilyProcess, "setfsgid": FamilyProcess, "setgid": FamilyProcess,
+ "setgroups": FamilyProcess, "setns": FamilyProcess, "setpgid": FamilyProcess,
+ "setpriority": FamilyProcess, "setregid": FamilyProcess, "setresgid": FamilyProcess,
+ "setresuid": FamilyProcess, "setreuid": FamilyProcess, "setrlimit": FamilyProcess,
+ "setsid": FamilyProcess, "setuid": FamilyProcess, "umask": FamilyProcess,
+ "unshare": FamilyProcess, "vfork": FamilyProcess, "vhangup": FamilyProcess,
+ "wait4": FamilyProcess, "waitid": FamilyProcess,
+
+ "kill": FamilySignals, "pause": FamilySignals, "rt_sigaction": FamilySignals,
+ "rt_sigpending": FamilySignals, "rt_sigprocmask": FamilySignals,
+ "rt_sigqueueinfo": FamilySignals, "rt_sigreturn": FamilySignals,
+ "rt_sigsuspend": FamilySignals, "rt_sigtimedwait": FamilySignals,
+ "rt_tgsigqueueinfo": FamilySignals, "sigaltstack": FamilySignals,
+ "tgkill": FamilySignals, "tkill": FamilySignals,
+
+ "clock_adjtime": FamilyTime, "clock_getres": FamilyTime, "clock_gettime": FamilyTime,
+ "clock_nanosleep": FamilyTime, "clock_settime": FamilyTime, "getitimer": FamilyTime,
+ "gettimeofday": FamilyTime, "nanosleep": FamilyTime, "setitimer": FamilyTime,
+ "settimeofday": FamilyTime, "time": FamilyTime, "timer_create": FamilyTime,
+ "timer_delete": FamilyTime, "timer_getoverrun": FamilyTime,
+ "timer_gettime": FamilyTime, "timer_settime": FamilyTime, "times": FamilyTime,
+
+ "sched_get_priority_max": FamilySched, "sched_get_priority_min": FamilySched,
+ "sched_getaffinity": FamilySched, "sched_getattr": FamilySched,
+ "sched_getparam": FamilySched, "sched_getscheduler": FamilySched,
+ "sched_rr_get_interval": FamilySched, "sched_setaffinity": FamilySched,
+ "sched_setattr": FamilySched, "sched_setparam": FamilySched,
+ "sched_setscheduler": FamilySched, "sched_yield": FamilySched,
+
+ "epoll_create": FamilyPolling, "epoll_create1": FamilyPolling,
+ "epoll_ctl": FamilyPolling, "epoll_pwait": FamilyPolling,
+ "epoll_pwait2": FamilyPolling, "epoll_wait": FamilyPolling,
+ "poll": FamilyPolling, "ppoll": FamilyPolling, "pselect6": FamilyPolling,
+ "select": FamilyPolling,
+
+ "io_cancel": FamilyAIO, "io_destroy": FamilyAIO, "io_getevents": FamilyAIO,
+ "io_pgetevents": FamilyAIO, "io_setup": FamilyAIO, "io_submit": FamilyAIO,
+ "io_uring_enter": FamilyAIO, "io_uring_register": FamilyAIO,
+ "io_uring_setup": FamilyAIO,
+
+ "add_key": FamilySecurity, "bpf": FamilySecurity, "capget": FamilySecurity,
+ "capset": FamilySecurity, "delete_module": FamilySecurity, "finit_module": FamilySecurity,
+ "get_mempolicy": FamilySecurity, "getrandom": FamilySecurity, "init_module": FamilySecurity,
+ "kexec_file_load": FamilySecurity, "keyctl": FamilySecurity,
+ "landlock_add_rule": FamilySecurity, "landlock_create_ruleset": FamilySecurity,
+ "landlock_restrict_self": FamilySecurity, "lookup_dcookie": FamilySecurity,
+ "perf_event_open": FamilySecurity, "ptrace": FamilySecurity,
+ "request_key": FamilySecurity, "seccomp": FamilySecurity,
+}
+
+// ClassifySyscallFamily returns the high-level syscall family for a tracepoint.
+func ClassifySyscallFamily(tracepointName string) SyscallFamily {
+ syscall := syscallName(tracepointName)
+ if family, ok := syscallFamilies[syscall]; ok {
+ return family
+ }
+ if isFSSyscall(syscall) {
+ return FamilyFS
+ }
+ return FamilyMisc
+}
+
+func syscallName(tracepointName string) string {
+ name := strings.TrimPrefix(tracepointName, "sys_enter_")
+ return strings.TrimPrefix(name, "sys_exit_")
+}
+
+func isFSSyscall(syscall string) bool {
+ for _, marker := range fsNameMarkers {
+ if strings.Contains(syscall, marker) {
+ return true
+ }
+ }
+ _, ok := fsSyscalls[syscall]
+ return ok
+}
+
+var fsNameMarkers = []string{"xattr", "stat", "chmod", "chown"}
+
+var fsSyscalls = map[string]struct{}{
+ "access": {}, "cachestat": {}, "chdir": {}, "chroot": {}, "close": {},
+ "close_range": {}, "copy_file_range": {}, "creat": {}, "dup": {}, "dup2": {},
+ "dup3": {}, "faccessat": {}, "faccessat2": {}, "fadvise64": {}, "fallocate": {},
+ "fcntl": {}, "fdatasync": {}, "fchdir": {}, "flock": {}, "fsconfig": {},
+ "fsmount": {}, "fsopen": {}, "fspick": {}, "fsync": {}, "ftruncate": {},
+ "futimesat": {}, "getcwd": {}, "getdents": {}, "getdents64": {}, "ioctl": {},
+ "link": {}, "linkat": {}, "lseek": {}, "mkdir": {}, "mkdirat": {},
+ "mknod": {}, "mknodat": {}, "mount": {}, "mount_setattr": {}, "move_mount": {},
+ "msync": {},
+ "name_to_handle_at": {}, "newfstat": {}, "newfstatat": {}, "newlstat": {},
+ "newstat": {}, "open": {}, "open_by_handle_at": {}, "open_tree": {},
+ "open_tree_attr": {}, "openat": {}, "openat2": {}, "quotactl": {},
+ "quotactl_fd": {}, "read": {}, "readahead": {}, "readlink": {}, "readlinkat": {},
+ "readv": {}, "rename": {}, "renameat": {}, "renameat2": {}, "rmdir": {},
+ "statfs": {}, "sync": {}, "sync_file_range": {}, "syncfs": {}, "symlink": {},
+ "symlinkat": {}, "truncate": {}, "umount2": {}, "unlink": {}, "unlinkat": {},
+ "utimensat": {}, "write": {}, "writev": {}, "pread64": {}, "preadv": {},
+ "preadv2": {}, "pwrite64": {}, "pwritev": {}, "pwritev2": {},
+}
diff --git a/internal/generate/family_test.go b/internal/generate/family_test.go
new file mode 100644
index 0000000..93431b4
--- /dev/null
+++ b/internal/generate/family_test.go
@@ -0,0 +1,51 @@
+package generate
+
+import "testing"
+
+func TestClassifySyscallFamily(t *testing.T) {
+ tests := []struct {
+ name string
+ want SyscallFamily
+ }{
+ {"sys_enter_accept", FamilyNetwork},
+ {"sys_exit_accept", FamilyNetwork},
+ {"sys_enter_pipe2", FamilyIPC},
+ {"sys_enter_munmap", FamilyMemory},
+ {"sys_enter_execve", FamilyProcess},
+ {"sys_enter_rt_sigaction", FamilySignals},
+ {"sys_enter_clock_gettime", FamilyTime},
+ {"sys_enter_sched_yield", FamilySched},
+ {"sys_enter_openat", FamilyFS},
+ {"sys_enter_epoll_wait", FamilyPolling},
+ {"sys_enter_io_uring_enter", FamilyAIO},
+ {"sys_enter_bpf", FamilySecurity},
+ {"sys_enter_unlisted_future_syscall", FamilyMisc},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := ClassifySyscallFamily(tt.name); got != tt.want {
+ t.Errorf("ClassifySyscallFamily(%q) = %s, want %s", tt.name, got, tt.want)
+ }
+ })
+ }
+}
+
+func TestParseFormatsTagsEveryFormatWithFamily(t *testing.T) {
+ formats := mustParseAll(t, FormatRead+"\n"+FormatExitSocket+"\n"+FormatExitKill)
+
+ tests := []struct {
+ index int
+ want SyscallFamily
+ }{
+ {0, FamilyFS},
+ {1, FamilyNetwork},
+ {2, FamilySignals},
+ }
+
+ for _, tt := range tests {
+ if got := formats[tt.index].Family; got != tt.want {
+ t.Errorf("formats[%d].Family = %s, want %s", tt.index, got, tt.want)
+ }
+ }
+}
diff --git a/internal/generate/format.go b/internal/generate/format.go
index ef51ba8..597d496 100644
--- a/internal/generate/format.go
+++ b/internal/generate/format.go
@@ -19,6 +19,7 @@ type Field struct {
type Format struct {
Name string
ID int
+ Family SyscallFamily
InternalFields []Field
ExternalFields []Field
}
@@ -64,6 +65,7 @@ func applyFormatLine(line string, _ []Format, current *Format, isExternal bool,
case strings.HasPrefix(trimmed, "name:"):
f := Format{}
f.Name = strings.TrimSpace(strings.TrimPrefix(trimmed, "name:"))
+ f.Family = ClassifySyscallFamily(f.Name)
*formats = append(*formats, f)
current = &(*formats)[len(*formats)-1]
isExternal = false