diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-19 20:29:31 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-19 20:29:31 +0300 |
| commit | 11a8642b7035ff558fb84d7761e93525c84e4908 (patch) | |
| tree | aa1f501fcf8f3a5474d26658731782e061cccc15 /internal/generate | |
| parent | c67b34fca467fc4e5e8aba7a1b8929d8aa55a833 (diff) | |
z6: add KindPoll wiring for poll/select ready counts
Diffstat (limited to 'internal/generate')
| -rw-r--r-- | internal/generate/bpfhandler.go | 17 | ||||
| -rw-r--r-- | internal/generate/classify.go | 9 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 36 | ||||
| -rw-r--r-- | internal/generate/codegen_test.go | 24 | ||||
| -rw-r--r-- | internal/generate/kindregistry.go | 1 | ||||
| -rw-r--r-- | internal/generate/testdata.go | 111 |
6 files changed, 197 insertions, 1 deletions
diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go index 944025a..20859ee 100644 --- a/internal/generate/bpfhandler.go +++ b/internal/generate/bpfhandler.go @@ -85,6 +85,8 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string { return generateExtraEventfd(f, isEnter) case KindEpollCtl: return generateExtraEpollCtl() + case KindPoll: + return generateExtraPoll(f.Name) case KindOpen: return generateExtraOpen(f) case KindPathname: @@ -205,6 +207,21 @@ 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" } +func generateExtraPoll(name string) string { + switch name { + case "sys_enter_poll": + return " ev->nfds = (__s32)ctx->args[1];\n ev->timeout_ns = -1;\n __s32 timeout_ms = (__s32)ctx->args[2];\n if (timeout_ms >= 0) {\n ev->timeout_ns = ((__s64)timeout_ms) * 1000000LL;\n }\n" + case "sys_enter_ppoll": + return " ev->nfds = (__s32)ctx->args[1];\n ev->timeout_ns = -1;\n if (ctx->args[2] != 0) {\n struct __ior_timespec {\n __s64 tv_sec;\n __s64 tv_nsec;\n } ts = {};\n if (bpf_probe_read_user(&ts, sizeof(ts), (void *)ctx->args[2]) == 0) {\n ev->timeout_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;\n }\n }\n" + case "sys_enter_select": + return " ev->nfds = (__s32)ctx->args[0];\n ev->timeout_ns = -1;\n if (ctx->args[4] != 0) {\n struct __ior_timeval {\n __s64 tv_sec;\n __s64 tv_usec;\n } tv = {};\n if (bpf_probe_read_user(&tv, sizeof(tv), (void *)ctx->args[4]) == 0) {\n ev->timeout_ns = tv.tv_sec * 1000000000LL + tv.tv_usec * 1000LL;\n }\n }\n" + case "sys_enter_pselect6": + return " ev->nfds = (__s32)ctx->args[0];\n ev->timeout_ns = -1;\n if (ctx->args[4] != 0) {\n struct __ior_timespec {\n __s64 tv_sec;\n __s64 tv_nsec;\n } ts = {};\n if (bpf_probe_read_user(&ts, sizeof(ts), (void *)ctx->args[4]) == 0) {\n ev->timeout_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;\n }\n }\n" + default: + return " ev->nfds = -1;\n ev->timeout_ns = -1;\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 21a8bf9..7c391fb 100644 --- a/internal/generate/classify.go +++ b/internal/generate/classify.go @@ -21,6 +21,7 @@ const ( KindPipe KindEventfd KindEpollCtl + KindPoll ) type RetClassification string @@ -139,6 +140,14 @@ func classifyNameOnly(name string) (ClassificationResult, bool) { return ClassificationResult{Kind: KindFd}, true case "sys_enter_epoll_ctl": return ClassificationResult{Kind: KindEpollCtl}, true + case "sys_enter_poll": + return ClassificationResult{Kind: KindPoll}, true + case "sys_enter_ppoll": + return ClassificationResult{Kind: KindPoll}, true + case "sys_enter_select": + return ClassificationResult{Kind: KindPoll}, true + case "sys_enter_pselect6": + return ClassificationResult{Kind: KindPoll}, 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 74d4143..367d7c8 100644 --- a/internal/generate/classify_test.go +++ b/internal/generate/classify_test.go @@ -397,6 +397,34 @@ func TestClassifyEpollPwait2(t *testing.T) { } } +func TestClassifyPoll(t *testing.T) { + r := classifyFromData(t, FormatPoll) + if r.Kind != KindPoll { + t.Errorf("poll: got kind %d, want KindPoll", r.Kind) + } +} + +func TestClassifyPpoll(t *testing.T) { + r := classifyFromData(t, FormatPpoll) + if r.Kind != KindPoll { + t.Errorf("ppoll: got kind %d, want KindPoll", r.Kind) + } +} + +func TestClassifySelect(t *testing.T) { + r := classifyFromData(t, FormatSelect) + if r.Kind != KindPoll { + t.Errorf("select: got kind %d, want KindPoll", r.Kind) + } +} + +func TestClassifyPselect6(t *testing.T) { + r := classifyFromData(t, FormatPselect6) + if r.Kind != KindPoll { + t.Errorf("pselect6: got kind %d, want KindPoll", r.Kind) + } +} + func TestClassifyKillRequiresGenerationFallback(t *testing.T) { r := classifyFromData(t, FormatKill) if r.Kind != KindNone { @@ -445,6 +473,10 @@ func TestClassifySyscallPairAccepted(t *testing.T) { {"epoll_wait", FormatEpollWait, FormatExitEpollWait, KindFd}, {"epoll_pwait", FormatEpollPwait, FormatExitEpollPwait, KindFd}, {"epoll_pwait2", FormatEpollPwait2, FormatExitEpollPwait2, KindFd}, + {"poll", FormatPoll, FormatExitPoll, KindPoll}, + {"ppoll", FormatPpoll, FormatExitPpoll, KindPoll}, + {"select", FormatSelect, FormatExitSelect, KindPoll}, + {"pselect6", FormatPselect6, FormatExitPselect6, KindPoll}, {"kill", FormatKill, FormatExitKill, KindNull}, } @@ -478,6 +510,10 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) { {"eventfd2", FormatEventfd2, FormatExitEventfd2, FamilyIPC}, {"epoll_ctl", FormatEpollCtl, FormatExitEpollCtl, FamilyPolling}, {"epoll_wait", FormatEpollWait, FormatExitEpollWait, FamilyPolling}, + {"poll", FormatPoll, FormatExitPoll, FamilyPolling}, + {"ppoll", FormatPpoll, FormatExitPpoll, FamilyPolling}, + {"select", FormatSelect, FormatExitSelect, FamilyPolling}, + {"pselect6", FormatPselect6, FormatExitPselect6, FamilyPolling}, {"kill", FormatKill, FormatExitKill, FamilySignals}, } diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index 23b5a2a..a3baed7 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -278,6 +278,26 @@ func TestGenerateEpollWaitHandlerUsesEpollFd(t *testing.T) { requireContains(t, output, "ev->fd = (__s32)ctx->args[0];") } +func TestGeneratePollHandlerCapturesNfdsAndTimeout(t *testing.T) { + output := generateFromPair(t, FormatPoll, FormatExitPoll) + + requireContains(t, output, "struct poll_event *ev") + requireContains(t, output, "ev->event_type = ENTER_POLL_EVENT;") + requireContains(t, output, "ev->nfds = (__s32)ctx->args[1];") + requireContains(t, output, "ev->timeout_ns = ((__s64)timeout_ms) * 1000000LL;") + requireContains(t, output, "ev->event_type = EXIT_RET_EVENT;") +} + +func TestGeneratePselect6HandlerCapturesTimeoutPointer(t *testing.T) { + output := generateFromPair(t, FormatPselect6, FormatExitPselect6) + + requireContains(t, output, "struct poll_event *ev") + requireContains(t, output, "ev->event_type = ENTER_POLL_EVENT;") + requireContains(t, output, "ev->nfds = (__s32)ctx->args[0];") + requireContains(t, output, "if (ctx->args[4] != 0) {") + requireContains(t, output, "ev->timeout_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;") +} + func TestGenerateNameToHandleAtHandler(t *testing.T) { output := generateFromPair(t, FormatNameToHandleAt, FormatExitNameToHandleAt) @@ -391,6 +411,7 @@ func TestGenerateAllEventTypes(t *testing.T) { {KindPipe, "ENTER_PIPE_EVENT", "EXIT_PIPE_EVENT"}, {KindEventfd, "ENTER_EVENTFD_EVENT", "EXIT_EVENTFD_EVENT"}, {KindEpollCtl, "ENTER_EPOLL_CTL_EVENT", "EXIT_EPOLL_CTL_EVENT"}, + {KindPoll, "ENTER_POLL_EVENT", "EXIT_POLL_EVENT"}, } for _, tt := range tests { @@ -423,6 +444,7 @@ func TestEventStructNames(t *testing.T) { {KindPipe, "pipe_event"}, {KindEventfd, "eventfd_event"}, {KindEpollCtl, "epoll_ctl_event"}, + {KindPoll, "poll_event"}, } for _, tt := range tests { @@ -441,7 +463,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, KindEpollCtl} + accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd, KindEpollCtl, KindPoll} 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 07f9d57..e8efe99 100644 --- a/internal/generate/kindregistry.go +++ b/internal/generate/kindregistry.go @@ -31,6 +31,7 @@ var kindRegistry = map[TracepointKind]kindMeta{ KindPipe: {structName: "pipe_event", enterAccepted: true}, KindEventfd: {structName: "eventfd_event", enterAccepted: true}, KindEpollCtl: {structName: "epoll_ctl_event", enterAccepted: true}, + KindPoll: {structName: "poll_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 9b9496b..e3e3036 100644 --- a/internal/generate/testdata.go +++ b/internal/generate/testdata.go @@ -1186,3 +1186,114 @@ format: print fmt: "0x%lx", REC->ret ` + +const FormatPoll = `name: sys_enter_poll +ID: 915 +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:struct pollfd * ufds; offset:16; size:8; signed:0; + field:unsigned int nfds; offset:24; size:8; signed:0; + field:int timeout; offset:32; size:8; signed:0; +` + +const FormatExitPoll = `name: sys_exit_poll +ID: 914 +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; +` + +const FormatPpoll = `name: sys_enter_ppoll +ID: 913 +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:struct pollfd * ufds; offset:16; size:8; signed:0; + field:unsigned int nfds; offset:24; size:8; signed:0; + field:const struct __kernel_timespec * tmo_p; offset:32; size:8; signed:0; + field:const sigset_t * sigmask; offset:40; size:8; signed:0; + field:size_t sigsetsize; offset:48; size:8; signed:0; +` + +const FormatExitPpoll = `name: sys_exit_ppoll +ID: 912 +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; +` + +const FormatSelect = `name: sys_enter_select +ID: 919 +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 n; offset:16; size:8; signed:0; + field:fd_set * inp; offset:24; size:8; signed:0; + field:fd_set * outp; offset:32; size:8; signed:0; + field:fd_set * exp; offset:40; size:8; signed:0; + field:struct timeval * tvp; offset:48; size:8; signed:0; +` + +const FormatExitSelect = `name: sys_exit_select +ID: 918 +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; +` + +const FormatPselect6 = `name: sys_enter_pselect6 +ID: 917 +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 n; offset:16; size:8; signed:0; + field:fd_set * inp; offset:24; size:8; signed:0; + field:fd_set * outp; offset:32; size:8; signed:0; + field:fd_set * exp; offset:40; size:8; signed:0; + field:const struct __kernel_timespec * tsp; offset:48; size:8; signed:0; + field:void * sig; offset:56; size:8; signed:0; +` + +const FormatExitPselect6 = `name: sys_exit_pselect6 +ID: 916 +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; +` |
