summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-19 20:29:31 +0300
committerPaul Buetow <paul@buetow.org>2026-05-19 20:29:31 +0300
commit11a8642b7035ff558fb84d7761e93525c84e4908 (patch)
treeaa1f501fcf8f3a5474d26658731782e061cccc15 /internal/generate
parentc67b34fca467fc4e5e8aba7a1b8929d8aa55a833 (diff)
z6: add KindPoll wiring for poll/select ready counts
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/bpfhandler.go17
-rw-r--r--internal/generate/classify.go9
-rw-r--r--internal/generate/classify_test.go36
-rw-r--r--internal/generate/codegen_test.go24
-rw-r--r--internal/generate/kindregistry.go1
-rw-r--r--internal/generate/testdata.go111
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;
+`