diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-20 14:37:41 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-20 14:37:41 +0300 |
| commit | 96355c79a38032ab4bd880b3b3ff4192ae709795 (patch) | |
| tree | 7a395706e3db680f6e695c8602501741eed6ad45 /internal/generate | |
| parent | f063e626a28339735da583142e5af864a60c2111 (diff) | |
task 27: add KindSleep and requested sleep metric
Diffstat (limited to 'internal/generate')
| -rw-r--r-- | internal/generate/bpfhandler.go | 13 | ||||
| -rw-r--r-- | internal/generate/classify.go | 5 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 18 | ||||
| -rw-r--r-- | internal/generate/codegen_test.go | 23 | ||||
| -rw-r--r-- | internal/generate/kindregistry.go | 1 | ||||
| -rw-r--r-- | internal/generate/testdata.go | 52 |
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; +` |
