summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-19 17:24:30 +0300
committerPaul Buetow <paul@buetow.org>2026-05-19 17:24:30 +0300
commit6ef2ad7d15b3a11e643f312884c222ac53165623 (patch)
treea08cf1809e85d32d8fc839e9df036cff97779166 /internal/generate
parent843def4f5c23db050cccfae57a9acb5899c110f4 (diff)
y6: add epoll ctl/wait tracing and ready-count coverage
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/bpfhandler.go6
-rw-r--r--internal/generate/classify.go9
-rw-r--r--internal/generate/classify_test.go34
-rw-r--r--internal/generate/codegen_test.go27
-rw-r--r--internal/generate/kindregistry.go1
-rw-r--r--internal/generate/testdata.go128
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
+`