summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-20 14:37:41 +0300
committerPaul Buetow <paul@buetow.org>2026-05-20 14:37:41 +0300
commit96355c79a38032ab4bd880b3b3ff4192ae709795 (patch)
tree7a395706e3db680f6e695c8602501741eed6ad45 /internal/generate
parentf063e626a28339735da583142e5af864a60c2111 (diff)
task 27: add KindSleep and requested sleep metric
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/bpfhandler.go13
-rw-r--r--internal/generate/classify.go5
-rw-r--r--internal/generate/classify_test.go18
-rw-r--r--internal/generate/codegen_test.go23
-rw-r--r--internal/generate/kindregistry.go1
-rw-r--r--internal/generate/testdata.go52
6 files changed, 111 insertions, 1 deletions
diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go
index 9e6f5d7..5c42464 100644
--- a/internal/generate/bpfhandler.go
+++ b/internal/generate/bpfhandler.go
@@ -97,6 +97,8 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string {
return generateExtraPoll(f.Name)
case KindMem:
return generateExtraMem(f.Name)
+ case KindSleep:
+ return generateExtraSleep(f.Name)
case KindOpen:
return generateExtraOpen(f)
case KindPathname:
@@ -243,6 +245,17 @@ func generateExtraMem(name string) string {
}
}
+func generateExtraSleep(name string) string {
+ ptrExpr := "0"
+ switch name {
+ case "sys_enter_nanosleep":
+ ptrExpr = "ctx->args[0]"
+ case "sys_enter_clock_nanosleep":
+ ptrExpr = "ctx->args[2]"
+ }
+ return " ev->requested_ns = -1;\n if (" + ptrExpr + " != 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 *)" + ptrExpr + ") == 0) {\n ev->requested_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;\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 a2da47d..2bb2b81 100644
--- a/internal/generate/classify.go
+++ b/internal/generate/classify.go
@@ -23,6 +23,7 @@ const (
KindEpollCtl
KindPoll
KindMem
+ KindSleep
)
type RetClassification string
@@ -153,6 +154,10 @@ func classifyNameOnly(name string) (ClassificationResult, bool) {
return ClassificationResult{Kind: KindMem}, true
case "sys_enter_mremap":
return ClassificationResult{Kind: KindMem}, true
+ case "sys_enter_nanosleep":
+ return ClassificationResult{Kind: KindSleep}, true
+ case "sys_enter_clock_nanosleep":
+ return ClassificationResult{Kind: KindSleep}, 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 2bc0e88..daf008a 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -439,6 +439,20 @@ func TestClassifyMremap(t *testing.T) {
}
}
+func TestClassifyNanosleep(t *testing.T) {
+ r := classifyFromData(t, FormatNanosleep)
+ if r.Kind != KindSleep {
+ t.Errorf("nanosleep: got kind %d, want KindSleep", r.Kind)
+ }
+}
+
+func TestClassifyClockNanosleep(t *testing.T) {
+ r := classifyFromData(t, FormatClockNanosleep)
+ if r.Kind != KindSleep {
+ t.Errorf("clock_nanosleep: got kind %d, want KindSleep", r.Kind)
+ }
+}
+
func TestClassifyKillRequiresGenerationFallback(t *testing.T) {
r := classifyFromData(t, FormatKill)
if r.Kind != KindNone {
@@ -493,6 +507,8 @@ func TestClassifySyscallPairAccepted(t *testing.T) {
{"pselect6", FormatPselect6, FormatExitPselect6, KindPoll},
{"munmap", FormatMunmap, FormatExitMunmap, KindMem},
{"mremap", FormatMremap, FormatExitMremap, KindMem},
+ {"nanosleep", FormatNanosleep, FormatExitNanosleep, KindSleep},
+ {"clock_nanosleep", FormatClockNanosleep, FormatExitClockNanosleep, KindSleep},
{"kill", FormatKill, FormatExitKill, KindNull},
}
@@ -532,6 +548,8 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) {
{"pselect6", FormatPselect6, FormatExitPselect6, FamilyPolling},
{"munmap", FormatMunmap, FormatExitMunmap, FamilyMemory},
{"mremap", FormatMremap, FormatExitMremap, FamilyMemory},
+ {"nanosleep", FormatNanosleep, FormatExitNanosleep, FamilyTime},
+ {"clock_nanosleep", FormatClockNanosleep, FormatExitClockNanosleep, FamilyTime},
{"kill", FormatKill, FormatExitKill, FamilySignals},
}
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index 3ec8d72..bc4ec3a 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -310,6 +310,25 @@ func TestGeneratePselect6HandlerCapturesTimeoutPointer(t *testing.T) {
requireContains(t, output, "ev->timeout_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;")
}
+func TestGenerateSleepHandlerCapturesRequestedTimespec(t *testing.T) {
+ output := generateFromPair(t, FormatNanosleep, FormatExitNanosleep)
+
+ requireContains(t, output, "struct sleep_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_SLEEP_EVENT;")
+ requireContains(t, output, "ev->requested_ns = -1;")
+ requireContains(t, output, "if (ctx->args[0] != 0) {")
+ requireContains(t, output, "ev->requested_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;")
+}
+
+func TestGenerateClockNanosleepHandlerCapturesRequestedTimespec(t *testing.T) {
+ output := generateFromPair(t, FormatClockNanosleep, FormatExitClockNanosleep)
+
+ requireContains(t, output, "struct sleep_event *ev")
+ requireContains(t, output, "ev->event_type = ENTER_SLEEP_EVENT;")
+ requireContains(t, output, "if (ctx->args[2] != 0) {")
+ requireContains(t, output, "ev->requested_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;")
+}
+
func TestGenerateNameToHandleAtHandler(t *testing.T) {
output := generateFromPair(t, FormatNameToHandleAt, FormatExitNameToHandleAt)
@@ -425,6 +444,7 @@ func TestGenerateAllEventTypes(t *testing.T) {
{KindEpollCtl, "ENTER_EPOLL_CTL_EVENT", "EXIT_EPOLL_CTL_EVENT"},
{KindPoll, "ENTER_POLL_EVENT", "EXIT_POLL_EVENT"},
{KindMem, "ENTER_MEM_EVENT", "EXIT_MEM_EVENT"},
+ {KindSleep, "ENTER_SLEEP_EVENT", "EXIT_SLEEP_EVENT"},
}
for _, tt := range tests {
@@ -459,6 +479,7 @@ func TestEventStructNames(t *testing.T) {
{KindEpollCtl, "epoll_ctl_event"},
{KindPoll, "poll_event"},
{KindMem, "mem_event"},
+ {KindSleep, "sleep_event"},
}
for _, tt := range tests {
@@ -477,7 +498,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, KindPoll, KindMem}
+ accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd, KindEpollCtl, KindPoll, KindMem, KindSleep}
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 756ed17..96d98d8 100644
--- a/internal/generate/kindregistry.go
+++ b/internal/generate/kindregistry.go
@@ -33,6 +33,7 @@ var kindRegistry = map[TracepointKind]kindMeta{
KindEpollCtl: {structName: "epoll_ctl_event", enterAccepted: true},
KindPoll: {structName: "poll_event", enterAccepted: true},
KindMem: {structName: "mem_event", enterAccepted: true},
+ KindSleep: {structName: "sleep_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 6b08f98..d88a1b9 100644
--- a/internal/generate/testdata.go
+++ b/internal/generate/testdata.go
@@ -1358,3 +1358,55 @@ format:
field:int __syscall_nr; offset:8; size:4; signed:1;
field:long ret; offset:16; size:8; signed:1;
`
+
+const FormatNanosleep = `name: sys_enter_nanosleep
+ID: 441
+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 __kernel_timespec * rqtp; offset:16; size:8; signed:0;
+ field:struct __kernel_timespec * rmtp; offset:24; size:8; signed:0;
+`
+
+const FormatExitNanosleep = `name: sys_exit_nanosleep
+ID: 440
+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 FormatClockNanosleep = `name: sys_enter_clock_nanosleep
+ID: 447
+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:clockid_t which_clock; offset:16; size:8; signed:0;
+ field:int flags; offset:24; size:8; signed:0;
+ field:const struct __kernel_timespec * rqtp; offset:32; size:8; signed:0;
+ field:struct __kernel_timespec * rmtp; offset:40; size:8; signed:0;
+`
+
+const FormatExitClockNanosleep = `name: sys_exit_clock_nanosleep
+ID: 446
+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;
+`