diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-19 17:24:30 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-19 17:24:30 +0300 |
| commit | 6ef2ad7d15b3a11e643f312884c222ac53165623 (patch) | |
| tree | a08cf1809e85d32d8fc839e9df036cff97779166 /internal/generate | |
| parent | 843def4f5c23db050cccfae57a9acb5899c110f4 (diff) | |
y6: add epoll ctl/wait tracing and ready-count coverage
Diffstat (limited to 'internal/generate')
| -rw-r--r-- | internal/generate/bpfhandler.go | 6 | ||||
| -rw-r--r-- | internal/generate/classify.go | 9 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 34 | ||||
| -rw-r--r-- | internal/generate/codegen_test.go | 27 | ||||
| -rw-r--r-- | internal/generate/kindregistry.go | 1 | ||||
| -rw-r--r-- | internal/generate/testdata.go | 128 |
6 files changed, 204 insertions, 1 deletions
diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go index 1a6b210..944025a 100644 --- a/internal/generate/bpfhandler.go +++ b/internal/generate/bpfhandler.go @@ -83,6 +83,8 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string { return generateExtraPipe(f, isEnter) case KindEventfd: return generateExtraEventfd(f, isEnter) + case KindEpollCtl: + return generateExtraEpollCtl() case KindOpen: return generateExtraOpen(f) case KindPathname: @@ -199,6 +201,10 @@ func generateExtraEventfd(f *Format, isEnter bool) string { return " __s32 flags = 0;\n __s32 *pending = bpf_map_lookup_elem(&eventfd_flags_map, &tid);\n if (pending) {\n flags = *pending;\n bpf_map_delete_elem(&eventfd_flags_map, &tid);\n }\n ev->flags = flags;\n ev->ret = ctx->ret;\n" } +func generateExtraEpollCtl() string { + return " ev->epfd = (__s32)ctx->args[0];\n ev->op = (__s32)ctx->args[1];\n ev->fd = (__s32)ctx->args[2];\n ev->events = 0;\n if (ctx->args[3] != 0) {\n __u32 user_events = 0;\n if (bpf_probe_read_user(&user_events, sizeof(user_events), (void *)ctx->args[3]) == 0) {\n ev->events = user_events;\n }\n }\n" +} + // eventStructName returns the C struct name for a TracepointKind. The mapping // is driven by kindRegistry so adding a new kind only requires a registry entry. func eventStructName(kind TracepointKind) string { diff --git a/internal/generate/classify.go b/internal/generate/classify.go index 0bc00b3..21a8bf9 100644 --- a/internal/generate/classify.go +++ b/internal/generate/classify.go @@ -20,6 +20,7 @@ const ( KindAccept KindPipe KindEventfd + KindEpollCtl ) type RetClassification string @@ -130,6 +131,14 @@ func classifyNameOnly(name string) (ClassificationResult, bool) { return ClassificationResult{Kind: KindFd}, true case "sys_enter_setsockopt": return ClassificationResult{Kind: KindFd}, true + case "sys_enter_epoll_wait": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_epoll_pwait": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_epoll_pwait2": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_epoll_ctl": + return ClassificationResult{Kind: KindEpollCtl}, true } if strings.HasPrefix(name, "sys_enter_io_") { return ClassificationResult{Kind: KindNull}, true diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go index 09989b8..74d4143 100644 --- a/internal/generate/classify_test.go +++ b/internal/generate/classify_test.go @@ -369,6 +369,34 @@ func TestClassifyExitEventfd2(t *testing.T) { } } +func TestClassifyEpollCtl(t *testing.T) { + r := classifyFromData(t, FormatEpollCtl) + if r.Kind != KindEpollCtl { + t.Errorf("epoll_ctl: got kind %d, want KindEpollCtl", r.Kind) + } +} + +func TestClassifyEpollWait(t *testing.T) { + r := classifyFromData(t, FormatEpollWait) + if r.Kind != KindFd { + t.Errorf("epoll_wait: got kind %d, want KindFd", r.Kind) + } +} + +func TestClassifyEpollPwait(t *testing.T) { + r := classifyFromData(t, FormatEpollPwait) + if r.Kind != KindFd { + t.Errorf("epoll_pwait: got kind %d, want KindFd", r.Kind) + } +} + +func TestClassifyEpollPwait2(t *testing.T) { + r := classifyFromData(t, FormatEpollPwait2) + if r.Kind != KindFd { + t.Errorf("epoll_pwait2: got kind %d, want KindFd", r.Kind) + } +} + func TestClassifyKillRequiresGenerationFallback(t *testing.T) { r := classifyFromData(t, FormatKill) if r.Kind != KindNone { @@ -413,6 +441,10 @@ func TestClassifySyscallPairAccepted(t *testing.T) { {"pipe2", FormatPipe2, FormatExitPipe2, KindPipe}, {"eventfd", FormatEventfd, FormatExitEventfd, KindEventfd}, {"eventfd2", FormatEventfd2, FormatExitEventfd2, KindEventfd}, + {"epoll_ctl", FormatEpollCtl, FormatExitEpollCtl, KindEpollCtl}, + {"epoll_wait", FormatEpollWait, FormatExitEpollWait, KindFd}, + {"epoll_pwait", FormatEpollPwait, FormatExitEpollPwait, KindFd}, + {"epoll_pwait2", FormatEpollPwait2, FormatExitEpollPwait2, KindFd}, {"kill", FormatKill, FormatExitKill, KindNull}, } @@ -444,6 +476,8 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) { {"pipe2", FormatPipe2, FormatExitPipe2, FamilyIPC}, {"eventfd", FormatEventfd, FormatExitEventfd, FamilyIPC}, {"eventfd2", FormatEventfd2, FormatExitEventfd2, FamilyIPC}, + {"epoll_ctl", FormatEpollCtl, FormatExitEpollCtl, FamilyPolling}, + {"epoll_wait", FormatEpollWait, FormatExitEpollWait, FamilyPolling}, {"kill", FormatKill, FormatExitKill, FamilySignals}, } diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index 1c99314..23b5a2a 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -255,6 +255,29 @@ func TestGenerateEventfdHandler(t *testing.T) { requireContains(t, output, "ev->ret = ctx->ret;") } +func TestGenerateEpollCtlHandler(t *testing.T) { + output := generateFromPair(t, FormatEpollCtl, FormatExitEpollCtl) + + requireContains(t, output, "struct epoll_ctl_event *ev") + requireContains(t, output, "ev->event_type = ENTER_EPOLL_CTL_EVENT;") + requireContains(t, output, "ev->epfd = (__s32)ctx->args[0];") + requireContains(t, output, "ev->op = (__s32)ctx->args[1];") + requireContains(t, output, "ev->fd = (__s32)ctx->args[2];") + requireContains(t, output, "ev->events = 0;") + requireContains(t, output, "if (ctx->args[3] != 0) {") + requireContains(t, output, "bpf_probe_read_user(&user_events, sizeof(user_events), (void *)ctx->args[3])") + requireContains(t, output, "ev->event_type = EXIT_RET_EVENT;") +} + +func TestGenerateEpollWaitHandlerUsesEpollFd(t *testing.T) { + output := generateFromPair(t, FormatEpollWait, FormatExitEpollWait) + + requireContains(t, output, "struct fd_event *ev") + requireContains(t, output, "ev->event_type = ENTER_FD_EVENT;") + requireContains(t, output, "ev->trace_id = SYS_ENTER_EPOLL_WAIT;") + requireContains(t, output, "ev->fd = (__s32)ctx->args[0];") +} + func TestGenerateNameToHandleAtHandler(t *testing.T) { output := generateFromPair(t, FormatNameToHandleAt, FormatExitNameToHandleAt) @@ -367,6 +390,7 @@ func TestGenerateAllEventTypes(t *testing.T) { {KindAccept, "ENTER_ACCEPT_EVENT", "EXIT_ACCEPT_EVENT"}, {KindPipe, "ENTER_PIPE_EVENT", "EXIT_PIPE_EVENT"}, {KindEventfd, "ENTER_EVENTFD_EVENT", "EXIT_EVENTFD_EVENT"}, + {KindEpollCtl, "ENTER_EPOLL_CTL_EVENT", "EXIT_EPOLL_CTL_EVENT"}, } for _, tt := range tests { @@ -398,6 +422,7 @@ func TestEventStructNames(t *testing.T) { {KindAccept, "accept_event"}, {KindPipe, "pipe_event"}, {KindEventfd, "eventfd_event"}, + {KindEpollCtl, "epoll_ctl_event"}, } for _, tt := range tests { @@ -416,7 +441,7 @@ func TestEnterReject(t *testing.T) { t.Error("KindNone should be enter-rejected") } - accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd} + accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd, KindEpollCtl} for _, k := range accepted { if isEnterRejected(k) { t.Errorf("kind %d should NOT be enter-rejected", k) diff --git a/internal/generate/kindregistry.go b/internal/generate/kindregistry.go index 17f7542..07f9d57 100644 --- a/internal/generate/kindregistry.go +++ b/internal/generate/kindregistry.go @@ -30,6 +30,7 @@ var kindRegistry = map[TracepointKind]kindMeta{ KindAccept: {structName: "accept_event", enterAccepted: true}, KindPipe: {structName: "pipe_event", enterAccepted: true}, KindEventfd: {structName: "eventfd_event", enterAccepted: true}, + KindEpollCtl: {structName: "epoll_ctl_event", enterAccepted: true}, // KindNone is intentionally absent: it represents "unclassified" and is // never enter-accepted. lookupKind returns the zero kindMeta (enterAccepted=false) // for any unregistered kind, so KindNone is implicitly rejected. diff --git a/internal/generate/testdata.go b/internal/generate/testdata.go index b34f98d..9b9496b 100644 --- a/internal/generate/testdata.go +++ b/internal/generate/testdata.go @@ -1058,3 +1058,131 @@ format: print fmt: "0x%lx", REC->ret ` + +const FormatEpollCtl = `name: sys_enter_epoll_ctl +ID: 1079 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:int epfd; offset:16; size:8; signed:0; + field:int op; offset:24; size:8; signed:0; + field:int fd; offset:32; size:8; signed:0; + field:struct epoll_event * event; offset:40; size:8; signed:0; + +print fmt: "epfd: 0x%08lx, op: 0x%08lx, fd: 0x%08lx, event: 0x%08lx", ((unsigned long)(REC->epfd)), ((unsigned long)(REC->op)), ((unsigned long)(REC->fd)), ((unsigned long)(REC->event)) +` + +const FormatExitEpollCtl = `name: sys_exit_epoll_ctl +ID: 1078 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:long ret; offset:16; size:8; signed:1; + +print fmt: "0x%lx", REC->ret +` + +const FormatEpollWait = `name: sys_enter_epoll_wait +ID: 1077 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:int epfd; offset:16; size:8; signed:0; + field:struct epoll_event * events; offset:24; size:8; signed:0; + field:int maxevents; offset:32; size:8; signed:0; + field:int timeout; offset:40; size:8; signed:0; + +print fmt: "epfd: 0x%08lx, events: 0x%08lx, maxevents: 0x%08lx, timeout: 0x%08lx", ((unsigned long)(REC->epfd)), ((unsigned long)(REC->events)), ((unsigned long)(REC->maxevents)), ((unsigned long)(REC->timeout)) +` + +const FormatExitEpollWait = `name: sys_exit_epoll_wait +ID: 1076 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:long ret; offset:16; size:8; signed:1; + +print fmt: "0x%lx", REC->ret +` + +const FormatEpollPwait = `name: sys_enter_epoll_pwait +ID: 1075 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:int epfd; offset:16; size:8; signed:0; + field:struct epoll_event * events; offset:24; size:8; signed:0; + field:int maxevents; offset:32; size:8; signed:0; + field:int timeout; offset:40; size:8; signed:0; + field:sigset_t * sigmask; offset:48; size:8; signed:0; + field:size_t sigsetsize; offset:56; size:8; signed:0; + +print fmt: "epfd: 0x%08lx, events: 0x%08lx, maxevents: 0x%08lx, timeout: 0x%08lx, sigmask: 0x%08lx, sigsetsize: 0x%08lx", ((unsigned long)(REC->epfd)), ((unsigned long)(REC->events)), ((unsigned long)(REC->maxevents)), ((unsigned long)(REC->timeout)), ((unsigned long)(REC->sigmask)), ((unsigned long)(REC->sigsetsize)) +` + +const FormatExitEpollPwait = `name: sys_exit_epoll_pwait +ID: 1074 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:long ret; offset:16; size:8; signed:1; + +print fmt: "0x%lx", REC->ret +` + +const FormatEpollPwait2 = `name: sys_enter_epoll_pwait2 +ID: 1073 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:int epfd; offset:16; size:8; signed:0; + field:struct epoll_event * events; offset:24; size:8; signed:0; + field:int maxevents; offset:32; size:8; signed:0; + field:const struct __kernel_timespec * timeout; offset:40; size:8; signed:0; + field:const sigset_t * sigmask; offset:48; size:8; signed:0; + field:size_t sigsetsize; offset:56; size:8; signed:0; + +print fmt: "epfd: 0x%08lx, events: 0x%08lx, maxevents: 0x%08lx, timeout: 0x%08lx, sigmask: 0x%08lx, sigsetsize: 0x%08lx", ((unsigned long)(REC->epfd)), ((unsigned long)(REC->events)), ((unsigned long)(REC->maxevents)), ((unsigned long)(REC->timeout)), ((unsigned long)(REC->sigmask)), ((unsigned long)(REC->sigsetsize)) +` + +const FormatExitEpollPwait2 = `name: sys_exit_epoll_pwait2 +ID: 1072 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:long ret; offset:16; size:8; signed:1; + +print fmt: "0x%lx", REC->ret +` |
