diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-20 22:25:40 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-20 22:25:40 +0300 |
| commit | 7a9839917461b12c810329ccb8fd3c6de06902d2 (patch) | |
| tree | 7e68d52cec796d8a4d7b5110ba87cdce48a0fcb9 | |
| parent | 271af607921ceabc640271c475a66e45b9460d3f (diff) | |
d7: add POSIX mq syscall kind/classification and coverage
| -rw-r--r-- | cmd/ioworkload/scenario_mq.go | 144 | ||||
| -rw-r--r-- | cmd/ioworkload/scenarios.go | 1 | ||||
| -rw-r--r-- | integrationtests/ipc_test.go | 49 | ||||
| -rw-r--r-- | internal/c/generated_tracepoints.c | 54 | ||||
| -rw-r--r-- | internal/c/generated_tracepoints_result.txt | 16 | ||||
| -rw-r--r-- | internal/generate/bpfhandler.go | 14 | ||||
| -rw-r--r-- | internal/generate/classify.go | 21 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 119 | ||||
| -rw-r--r-- | internal/generate/codegen_test.go | 15 | ||||
| -rw-r--r-- | internal/generate/kindregistry.go | 1 | ||||
| -rw-r--r-- | internal/generate/retclassify_test.go | 4 |
11 files changed, 401 insertions, 37 deletions
diff --git a/cmd/ioworkload/scenario_mq.go b/cmd/ioworkload/scenario_mq.go new file mode 100644 index 0000000..8b627a7 --- /dev/null +++ b/cmd/ioworkload/scenario_mq.go @@ -0,0 +1,144 @@ +package main + +import ( + "errors" + "fmt" + "os" + "syscall" + "time" + "unsafe" +) + +const ( + mqPayload = "ior-mq-payload" +) + +type mqAttr struct { + Flags int64 + Maxmsg int64 + Msgsize int64 + Curmsgs int64 + Pad [4]int64 +} + +func mqPosixBasic() error { + name := fmt.Sprintf("/ior-mq-%d-%d", os.Getpid(), time.Now().UnixNano()) + attr := mqAttr{Maxmsg: 8, Msgsize: 128} + mqd, err := mqOpen(name, syscall.O_CREAT|syscall.O_EXCL|syscall.O_RDWR, 0o600, &attr) + if err != nil { + return err + } + defer syscall.Close(mqd) //nolint:errcheck + defer mqUnlink(name) //nolint:errcheck + + if err := mqNotify(mqd); err != nil { + return err + } + if err := mqGetsetattr(mqd); err != nil { + return err + } + + deadline := time.Now().Add(2 * time.Second) + if err := mqTimedsend(mqd, []byte(mqPayload), 0, deadline); err != nil { + return err + } + + buf := make([]byte, 128) + n, err := mqTimedreceive(mqd, buf, deadline) + if err != nil { + return err + } + if got := string(buf[:n]); got != mqPayload { + return fmt.Errorf("mq_timedreceive payload = %q, want %q", got, mqPayload) + } + return nil +} + +func mqOpen(name string, flags int, mode uint32, attr *mqAttr) (int, error) { + ptr, err := syscall.BytePtrFromString(name) + if err != nil { + return 0, fmt.Errorf("queue name: %w", err) + } + fd, _, errno := syscall.Syscall6( + syscall.SYS_MQ_OPEN, + uintptr(unsafe.Pointer(ptr)), + uintptr(flags), + uintptr(mode), + uintptr(unsafe.Pointer(attr)), + 0, + 0, + ) + if errno != 0 { + return 0, fmt.Errorf("mq_open: %w", errno) + } + return int(fd), nil +} + +func mqUnlink(name string) error { + ptr, err := syscall.BytePtrFromString(name) + if err != nil { + return fmt.Errorf("queue name: %w", err) + } + _, _, errno := syscall.Syscall(syscall.SYS_MQ_UNLINK, uintptr(unsafe.Pointer(ptr)), 0, 0) + if errno != 0 { + return fmt.Errorf("mq_unlink: %w", errno) + } + return nil +} + +func mqNotify(mqd int) error { + _, _, errno := syscall.Syscall(syscall.SYS_MQ_NOTIFY, uintptr(mqd), 0, 0) + if errno == 0 || errors.Is(errno, syscall.EINVAL) { + return nil + } + return fmt.Errorf("mq_notify: %w", errno) +} + +func mqGetsetattr(mqd int) error { + var old mqAttr + _, _, errno := syscall.Syscall( + syscall.SYS_MQ_GETSETATTR, + uintptr(mqd), + 0, + uintptr(unsafe.Pointer(&old)), + ) + if errno != 0 { + return fmt.Errorf("mq_getsetattr: %w", errno) + } + return nil +} + +func mqTimedsend(mqd int, payload []byte, priority uint32, deadline time.Time) error { + ts := syscall.NsecToTimespec(deadline.UnixNano()) + _, _, errno := syscall.Syscall6( + syscall.SYS_MQ_TIMEDSEND, + uintptr(mqd), + uintptr(unsafe.Pointer(&payload[0])), + uintptr(len(payload)), + uintptr(priority), + uintptr(unsafe.Pointer(&ts)), + 0, + ) + if errno != 0 { + return fmt.Errorf("mq_timedsend: %w", errno) + } + return nil +} + +func mqTimedreceive(mqd int, buf []byte, deadline time.Time) (int, error) { + ts := syscall.NsecToTimespec(deadline.UnixNano()) + var prio uint32 + n, _, errno := syscall.Syscall6( + syscall.SYS_MQ_TIMEDRECEIVE, + uintptr(mqd), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(len(buf)), + uintptr(unsafe.Pointer(&prio)), + uintptr(unsafe.Pointer(&ts)), + 0, + ) + if errno != 0 { + return 0, fmt.Errorf("mq_timedreceive: %w", errno) + } + return int(n), nil +} diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go index 79d4e6d..7ca5aa4 100644 --- a/cmd/ioworkload/scenarios.go +++ b/cmd/ioworkload/scenarios.go @@ -34,6 +34,7 @@ var scenarios = map[string]func() error{ "pipe2-basic": pipe2Basic, "eventfd-basic": eventfdBasic, "eventfd2-basic": eventfd2Basic, + "mq-posix-basic": mqPosixBasic, "mountfs-management": mountfsManagement, "polling-epoll": pollingEpoll, "sleep-syscalls": sleepSyscalls, diff --git a/integrationtests/ipc_test.go b/integrationtests/ipc_test.go index b9967b8..007ebc2 100644 --- a/integrationtests/ipc_test.go +++ b/integrationtests/ipc_test.go @@ -1,6 +1,11 @@ package integrationtests -import "testing" +import ( + "strings" + "testing" +) + +const mqPayloadLen = uint64(14) func TestPipeBasic(t *testing.T) { result, _ := runScenarioResult(t, "pipe-basic", []ExpectedEvent{ @@ -45,3 +50,45 @@ func TestEventfd2Basic(t *testing.T) { assertTracepointPathPrefix(t, result, "enter_eventfd2", "eventfd:") assertTracepointPathPrefix(t, result, "enter_close", "eventfd:") } + +func TestPosixMqBasic(t *testing.T) { + enableParallelIfRequested(t) + h := newTestHarness(t) + result, pid, err := h.Run("mq-posix-basic", defaultDuration) + if err != nil { + errText := err.Error() + if strings.Contains(errText, "mq_open: permission denied") || + strings.Contains(errText, "mq_open: operation not permitted") || + strings.Contains(errText, "mq_open: function not implemented") { + t.Skipf("mq syscalls unavailable in this environment: %v", err) + } + t.Fatalf("run scenario mq-posix-basic: %v", err) + } + + AssertNoUnexpectedPID(t, result, pid) + AssertNoUnexpectedComm(t, result, "ioworkload") + AssertEventsPresent(t, result, []ExpectedEvent{ + {Tracepoint: "enter_mq_open", MinCount: 1}, + {Tracepoint: "enter_mq_unlink", MinCount: 1}, + {Tracepoint: "enter_mq_timedsend", MinCount: 1}, + {Tracepoint: "enter_mq_timedreceive", MinCount: 1}, + {Tracepoint: "enter_mq_notify", MinCount: 1}, + {Tracepoint: "enter_mq_getsetattr", MinCount: 1}, + {Tracepoint: "enter_close", MinCount: 1}, + }) + + assertTracepointPathPrefix(t, result, "enter_mq_open", "/ior-mq-") + assertTracepointPathPrefix(t, result, "enter_mq_unlink", "/ior-mq-") + assertTracepointPathPrefix(t, result, "enter_mq_timedsend", "/ior-mq-") + assertTracepointPathPrefix(t, result, "enter_mq_timedreceive", "/ior-mq-") + assertTracepointPathPrefix(t, result, "enter_mq_notify", "/ior-mq-") + assertTracepointPathPrefix(t, result, "enter_mq_getsetattr", "/ior-mq-") + assertTracepointPathPrefix(t, result, "enter_close", "/ior-mq-") + + sendExp := ExpectedEvent{Tracepoint: "enter_mq_timedsend", Comm: "ioworkload", PathContains: "/ior-mq-"} + recvExp := ExpectedEvent{Tracepoint: "enter_mq_timedreceive", Comm: "ioworkload", PathContains: "/ior-mq-"} + assertEventBytesAtLeast(t, result, sendExp, mqPayloadLen) + assertEventBytesAtLeast(t, result, recvExp, mqPayloadLen) + assertEventDurationPositive(t, result, sendExp) + assertEventDurationPositive(t, result, recvExp) +} diff --git a/internal/c/generated_tracepoints.c b/internal/c/generated_tracepoints.c index 4db2e8d..d14f5ef 100644 --- a/internal/c/generated_tracepoints.c +++ b/internal/c/generated_tracepoints.c @@ -2444,7 +2444,7 @@ int handle_sys_exit_keyctl(struct syscall_trace_exit *ctx) { return 0; } -/// sys_enter_mq_open is a struct null_event +/// sys_enter_mq_open is a struct open_event SEC("tracepoint/syscalls/sys_enter_mq_open") int handle_sys_enter_mq_open(struct syscall_trace_enter *ctx) { __u32 pid, tid; @@ -2454,15 +2454,19 @@ int handle_sys_enter_mq_open(struct syscall_trace_enter *ctx) { if (!ior_on_syscall_enter(tid, SYS_ENTER_MQ_OPEN)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct open_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct open_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_OPEN_EVENT; ev->trace_id = SYS_ENTER_MQ_OPEN; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + __builtin_memset(&(ev->filename), 0, sizeof(ev->filename) + sizeof(ev->comm)); + bpf_probe_read_user_str(ev->filename, sizeof(ev->filename), (void *)ctx->args[0]); + bpf_get_current_comm(&ev->comm, sizeof(ev->comm)); + ev->flags = ctx->args[1]; bpf_ringbuf_submit(ev, 0); return 0; @@ -2494,7 +2498,7 @@ int handle_sys_exit_mq_open(struct syscall_trace_exit *ctx) { return 0; } -/// sys_enter_mq_unlink is a struct null_event +/// sys_enter_mq_unlink is a struct path_event SEC("tracepoint/syscalls/sys_enter_mq_unlink") int handle_sys_enter_mq_unlink(struct syscall_trace_enter *ctx) { __u32 pid, tid; @@ -2504,15 +2508,17 @@ int handle_sys_enter_mq_unlink(struct syscall_trace_enter *ctx) { if (!ior_on_syscall_enter(tid, SYS_ENTER_MQ_UNLINK)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct path_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct path_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_PATH_EVENT; ev->trace_id = SYS_ENTER_MQ_UNLINK; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + __builtin_memset(&(ev->pathname), 0, sizeof(ev->pathname)); + bpf_probe_read_user_str(ev->pathname, sizeof(ev->pathname), (void*)ctx->args[0]); bpf_ringbuf_submit(ev, 0); return 0; @@ -2544,7 +2550,7 @@ int handle_sys_exit_mq_unlink(struct syscall_trace_exit *ctx) { return 0; } -/// sys_enter_mq_timedsend is a struct null_event +/// sys_enter_mq_timedsend is a struct fd_event SEC("tracepoint/syscalls/sys_enter_mq_timedsend") int handle_sys_enter_mq_timedsend(struct syscall_trace_enter *ctx) { __u32 pid, tid; @@ -2554,21 +2560,22 @@ int handle_sys_enter_mq_timedsend(struct syscall_trace_enter *ctx) { if (!ior_on_syscall_enter(tid, SYS_ENTER_MQ_TIMEDSEND)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_FD_EVENT; ev->trace_id = SYS_ENTER_MQ_TIMEDSEND; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->fd = (__s32)ctx->args[0]; bpf_ringbuf_submit(ev, 0); return 0; } -/// sys_exit_mq_timedsend is a struct ret_event (UNCLASSIFIED) +/// sys_exit_mq_timedsend is a struct ret_event (WRITE_CLASSIFIED) SEC("tracepoint/syscalls/sys_exit_mq_timedsend") int handle_sys_exit_mq_timedsend(struct syscall_trace_exit *ctx) { __u32 pid, tid; @@ -2588,13 +2595,13 @@ int handle_sys_exit_mq_timedsend(struct syscall_trace_exit *ctx) { ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); ev->ret = ctx->ret; - ev->ret_type = UNCLASSIFIED; + ev->ret_type = WRITE_CLASSIFIED; bpf_ringbuf_submit(ev, 0); return 0; } -/// sys_enter_mq_timedreceive is a struct null_event +/// sys_enter_mq_timedreceive is a struct fd_event SEC("tracepoint/syscalls/sys_enter_mq_timedreceive") int handle_sys_enter_mq_timedreceive(struct syscall_trace_enter *ctx) { __u32 pid, tid; @@ -2604,21 +2611,22 @@ int handle_sys_enter_mq_timedreceive(struct syscall_trace_enter *ctx) { if (!ior_on_syscall_enter(tid, SYS_ENTER_MQ_TIMEDRECEIVE)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_FD_EVENT; ev->trace_id = SYS_ENTER_MQ_TIMEDRECEIVE; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->fd = (__s32)ctx->args[0]; bpf_ringbuf_submit(ev, 0); return 0; } -/// sys_exit_mq_timedreceive is a struct ret_event (UNCLASSIFIED) +/// sys_exit_mq_timedreceive is a struct ret_event (READ_CLASSIFIED) SEC("tracepoint/syscalls/sys_exit_mq_timedreceive") int handle_sys_exit_mq_timedreceive(struct syscall_trace_exit *ctx) { __u32 pid, tid; @@ -2638,13 +2646,13 @@ int handle_sys_exit_mq_timedreceive(struct syscall_trace_exit *ctx) { ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); ev->ret = ctx->ret; - ev->ret_type = UNCLASSIFIED; + ev->ret_type = READ_CLASSIFIED; bpf_ringbuf_submit(ev, 0); return 0; } -/// sys_enter_mq_notify is a struct null_event +/// sys_enter_mq_notify is a struct fd_event SEC("tracepoint/syscalls/sys_enter_mq_notify") int handle_sys_enter_mq_notify(struct syscall_trace_enter *ctx) { __u32 pid, tid; @@ -2654,15 +2662,16 @@ int handle_sys_enter_mq_notify(struct syscall_trace_enter *ctx) { if (!ior_on_syscall_enter(tid, SYS_ENTER_MQ_NOTIFY)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_FD_EVENT; ev->trace_id = SYS_ENTER_MQ_NOTIFY; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->fd = (__s32)ctx->args[0]; bpf_ringbuf_submit(ev, 0); return 0; @@ -2694,7 +2703,7 @@ int handle_sys_exit_mq_notify(struct syscall_trace_exit *ctx) { return 0; } -/// sys_enter_mq_getsetattr is a struct null_event +/// sys_enter_mq_getsetattr is a struct fd_event SEC("tracepoint/syscalls/sys_enter_mq_getsetattr") int handle_sys_enter_mq_getsetattr(struct syscall_trace_enter *ctx) { __u32 pid, tid; @@ -2704,15 +2713,16 @@ int handle_sys_enter_mq_getsetattr(struct syscall_trace_enter *ctx) { if (!ior_on_syscall_enter(tid, SYS_ENTER_MQ_GETSETATTR)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_FD_EVENT; ev->trace_id = SYS_ENTER_MQ_GETSETATTR; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->fd = (__s32)ctx->args[0]; bpf_ringbuf_submit(ev, 0); return 0; diff --git a/internal/c/generated_tracepoints_result.txt b/internal/c/generated_tracepoints_result.txt index 3a5282e..892cb1a 100644 --- a/internal/c/generated_tracepoints_result.txt +++ b/internal/c/generated_tracepoints_result.txt @@ -175,12 +175,12 @@ sys_enter_mount_setattr is a struct path_event sys_enter_move_mount is a struct two_fd_event sys_enter_move_pages is a struct null_event sys_enter_mprotect is a struct null_event -sys_enter_mq_getsetattr is a struct null_event -sys_enter_mq_notify is a struct null_event -sys_enter_mq_open is a struct null_event -sys_enter_mq_timedreceive is a struct null_event -sys_enter_mq_timedsend is a struct null_event -sys_enter_mq_unlink is a struct null_event +sys_enter_mq_getsetattr is a struct fd_event +sys_enter_mq_notify is a struct fd_event +sys_enter_mq_open is a struct open_event +sys_enter_mq_timedreceive is a struct fd_event +sys_enter_mq_timedsend is a struct fd_event +sys_enter_mq_unlink is a struct path_event sys_enter_mremap is a struct mem_event sys_enter_mseal is a struct null_event sys_enter_msgctl is a struct null_event @@ -545,8 +545,8 @@ sys_exit_mprotect is a struct ret_event (UNCLASSIFIED) sys_exit_mq_getsetattr is a struct ret_event (UNCLASSIFIED) sys_exit_mq_notify is a struct ret_event (UNCLASSIFIED) sys_exit_mq_open is a struct ret_event (UNCLASSIFIED) -sys_exit_mq_timedreceive is a struct ret_event (UNCLASSIFIED) -sys_exit_mq_timedsend is a struct ret_event (UNCLASSIFIED) +sys_exit_mq_timedreceive is a struct ret_event (READ_CLASSIFIED) +sys_exit_mq_timedsend is a struct ret_event (WRITE_CLASSIFIED) sys_exit_mq_unlink is a struct ret_event (UNCLASSIFIED) sys_exit_mremap is a struct ret_event (UNCLASSIFIED) sys_exit_mseal is a struct ret_event (UNCLASSIFIED) diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go index afdb83e..57f635a 100644 --- a/internal/generate/bpfhandler.go +++ b/internal/generate/bpfhandler.go @@ -103,6 +103,8 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string { return generateExtraSleep(f.Name) case KindOpen: return generateExtraOpen(f) + case KindMqOpen: + return generateExtraMqOpen(f) case KindPathname: return generateExtraPathname(tp, f) case KindName: @@ -131,8 +133,16 @@ func generateExtraFd(f *Format) string { // generateExtraOpen returns the filename/comm/flags capture lines for open-family events. func generateExtraOpen(f *Format) string { - filenameIdx := f.FieldNumber("filename") - flagsIdx := f.FieldNumber("flags") + return generateExtraOpenWithFields(f, "filename", "flags") +} + +func generateExtraMqOpen(f *Format) string { + return generateExtraOpenWithFields(f, "u_name", "oflag") +} + +func generateExtraOpenWithFields(f *Format, pathnameField, flagsField string) string { + filenameIdx := f.FieldNumber(pathnameField) + flagsIdx := f.FieldNumber(flagsField) var b strings.Builder b.WriteString(" __builtin_memset(&(ev->filename), 0, sizeof(ev->filename) + sizeof(ev->comm));\n") fmt.Fprintf(&b, " bpf_probe_read_user_str(ev->filename, sizeof(ev->filename), (void *)ctx->args[%d]);\n", filenameIdx) diff --git a/internal/generate/classify.go b/internal/generate/classify.go index 2b750cf..4afc035 100644 --- a/internal/generate/classify.go +++ b/internal/generate/classify.go @@ -8,6 +8,7 @@ const ( KindNone TracepointKind = iota KindFd KindOpen + KindMqOpen KindPathname KindName KindRet @@ -38,7 +39,7 @@ const ( type ClassificationResult struct { Kind TracepointKind - PathnameField string // for KindPathname: "pathname", "path", "filename", or "name" + PathnameField string // for KindPathname: e.g. "pathname", "path", "filename", "name", "u_name" } // ClassifyFormat determines the tracepoint kind for a parsed format section. @@ -171,6 +172,14 @@ func classifyNameOnly(name string) (ClassificationResult, bool) { return ClassificationResult{Kind: KindSleep}, true case "sys_enter_clock_nanosleep": return ClassificationResult{Kind: KindSleep}, true + case "sys_enter_mq_timedsend": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_mq_timedreceive": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_mq_notify": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_mq_getsetattr": + return ClassificationResult{Kind: KindFd}, true } if strings.HasPrefix(name, "sys_enter_io_") { return ClassificationResult{Kind: KindNull}, true @@ -226,6 +235,14 @@ func classifyNameAndField(name, fieldType, fieldName string) (ClassificationResu if isCStringPtrType(fieldType) && fieldName == "specialfile" { return ClassificationResult{Kind: KindPathname, PathnameField: "specialfile"}, true } + case "sys_enter_mq_open": + if isCStringPtrType(fieldType) && fieldName == "u_name" { + return ClassificationResult{Kind: KindMqOpen}, true + } + case "sys_enter_mq_unlink": + if isCStringPtrType(fieldType) && fieldName == "u_name" { + return ClassificationResult{Kind: KindPathname, PathnameField: "u_name"}, true + } } if strings.HasPrefix(name, "sys_enter") && @@ -292,6 +309,7 @@ var retClassifications = map[string]RetClassification{ "recvmsg": ReadClassified, "recvfrom": ReadClassified, "syslog": ReadClassified, + "mq_timedreceive": ReadClassified, "copy_file_range": TransferClassified, "sendfile64": TransferClassified, @@ -307,4 +325,5 @@ var retClassifications = map[string]RetClassification{ "sendto": WriteClassified, "write": WriteClassified, "writev": WriteClassified, + "mq_timedsend": WriteClassified, } diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go index b853994..5eef40f 100644 --- a/internal/generate/classify_test.go +++ b/internal/generate/classify_test.go @@ -453,6 +453,59 @@ func TestClassifyClockNanosleep(t *testing.T) { } } +func TestClassifyMqOpen(t *testing.T) { + r := ClassifyFormat(&Format{ + Name: "sys_enter_mq_open", + ExternalFields: []Field{ + {Type: "long", Name: "__syscall_nr"}, + {Type: "const char *", Name: "u_name"}, + {Type: "int", Name: "oflag"}, + }, + }) + if r.Kind != KindMqOpen { + t.Errorf("mq_open: got kind %d, want KindMqOpen", r.Kind) + } +} + +func TestClassifyMqUnlink(t *testing.T) { + r := ClassifyFormat(&Format{ + Name: "sys_enter_mq_unlink", + ExternalFields: []Field{ + {Type: "long", Name: "__syscall_nr"}, + {Type: "const char *", Name: "u_name"}, + }, + }) + if r.Kind != KindPathname { + t.Errorf("mq_unlink: got kind %d, want KindPathname", r.Kind) + } + if r.PathnameField != "u_name" { + t.Errorf("mq_unlink: PathnameField = %q, want u_name", r.PathnameField) + } +} + +func TestClassifyMqFdSyscallsByName(t *testing.T) { + tests := []string{ + "mq_timedsend", + "mq_timedreceive", + "mq_notify", + "mq_getsetattr", + } + for _, name := range tests { + t.Run(name, func(t *testing.T) { + r := ClassifyFormat(&Format{ + Name: "sys_enter_" + name, + ExternalFields: []Field{ + {Type: "long", Name: "__syscall_nr"}, + {Type: "mqd_t", Name: "mqdes"}, + }, + }) + if r.Kind != KindFd { + t.Errorf("%s: got kind %d, want KindFd", name, r.Kind) + } + }) + } +} + func TestClassifyMount(t *testing.T) { r := classifyFromData(t, FormatMount) if r.Kind != KindPathname { @@ -746,6 +799,36 @@ func TestBatchMessageSyscallPairsDeferByteClassification(t *testing.T) { } } +func TestClassifyMqSyscallPairsAcceptedAndClassified(t *testing.T) { + tests := []struct { + name string + enterKindText string + exitClassification string + }{ + {"mq_open", "struct open_event", "UNCLASSIFIED"}, + {"mq_unlink", "struct path_event", "UNCLASSIFIED"}, + {"mq_timedsend", "struct fd_event", "WRITE_CLASSIFIED"}, + {"mq_timedreceive", "struct fd_event", "READ_CLASSIFIED"}, + {"mq_notify", "struct fd_event", "UNCLASSIFIED"}, + {"mq_getsetattr", "struct fd_event", "UNCLASSIFIED"}, + } + + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + output := GenerateTracepointsC(mqFormats(tt.name, 9200+i*2)) + if strings.Contains(output, "Ignoring") || strings.Contains(output, "Skipping") { + t.Fatalf("syscall %s was not accepted:\n%s", tt.name, output) + } + if !strings.Contains(output, "/// sys_enter_"+tt.name+" is a "+tt.enterKindText) { + t.Fatalf("sys_enter_%s did not use %s:\n%s", tt.name, tt.enterKindText, output) + } + if !strings.Contains(output, "/// sys_exit_"+tt.name+" is a struct ret_event ("+tt.exitClassification+")") { + t.Fatalf("sys_exit_%s did not use %s:\n%s", tt.name, tt.exitClassification, output) + } + }) + } +} + func phaseAFormats(name string, enterID int) []Format { enterFields := []Field{ {Type: "long", Name: "__syscall_nr"}, @@ -774,6 +857,42 @@ func phaseAFormats(name string, enterID int) []Format { } } +func mqFormats(name string, enterID int) []Format { + enterFields := []Field{ + {Type: "long", Name: "__syscall_nr"}, + } + switch name { + case "mq_open": + enterFields = append(enterFields, + Field{Type: "const char *", Name: "u_name"}, + Field{Type: "int", Name: "oflag"}, + Field{Type: "umode_t", Name: "mode"}, + ) + case "mq_unlink": + enterFields = append(enterFields, Field{Type: "const char *", Name: "u_name"}) + default: + enterFields = append(enterFields, Field{Type: "mqd_t", Name: "mqdes"}) + } + + return []Format{ + { + Name: "sys_enter_" + name, + ID: enterID, + Family: ClassifySyscallFamily("sys_enter_" + name), + ExternalFields: enterFields, + }, + { + Name: "sys_exit_" + name, + ID: enterID - 1, + Family: ClassifySyscallFamily("sys_exit_" + name), + ExternalFields: []Field{ + {Type: "long", Name: "__syscall_nr"}, + {Type: "long", Name: "ret"}, + }, + }, + } +} + func TestClassifyFormatNoExternalFields(t *testing.T) { f := &Format{ Name: "sys_enter_test", diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index ac04eba..7ad076f 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -53,6 +53,17 @@ func TestGenerateOpenHandlerDirect(t *testing.T) { requireContains(t, output, "ev->flags = ctx->args[1];") } +func TestGenerateMqOpenHandler(t *testing.T) { + output := GenerateTracepointsC(mqFormats("mq_open", 9300)) + + requireContains(t, output, `SEC("tracepoint/syscalls/sys_enter_mq_open")`) + requireContains(t, output, "struct open_event *ev") + requireContains(t, output, "ev->event_type = ENTER_OPEN_EVENT;") + requireContains(t, output, "ev->trace_id = SYS_ENTER_MQ_OPEN;") + requireContains(t, output, "bpf_probe_read_user_str(ev->filename, sizeof(ev->filename), (void *)ctx->args[0]);") + requireContains(t, output, "ev->flags = ctx->args[1];") +} + func TestGenerateOpenat2Handler(t *testing.T) { f := mustParseOne(t, FormatOpenat2) r := ClassifyFormat(&f) @@ -450,6 +461,7 @@ func TestGenerateAllEventTypes(t *testing.T) { }{ {KindFd, "ENTER_FD_EVENT", "EXIT_FD_EVENT"}, {KindOpen, "ENTER_OPEN_EVENT", "EXIT_OPEN_EVENT"}, + {KindMqOpen, "ENTER_OPEN_EVENT", "EXIT_OPEN_EVENT"}, {KindPathname, "ENTER_PATH_EVENT", "EXIT_PATH_EVENT"}, {KindName, "ENTER_NAME_EVENT", "EXIT_NAME_EVENT"}, {KindRet, "ENTER_RET_EVENT", "EXIT_RET_EVENT"}, @@ -486,6 +498,7 @@ func TestEventStructNames(t *testing.T) { }{ {KindFd, "fd_event"}, {KindOpen, "open_event"}, + {KindMqOpen, "open_event"}, {KindPathname, "path_event"}, {KindName, "name_event"}, {KindRet, "ret_event"}, @@ -521,7 +534,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, KindTwoFd, KindPoll, KindMem, KindSleep} + accepted := []TracepointKind{KindFd, KindOpen, KindMqOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept, KindPipe, KindEventfd, KindEpollCtl, KindTwoFd, 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 2775f38..188beb0 100644 --- a/internal/generate/kindregistry.go +++ b/internal/generate/kindregistry.go @@ -18,6 +18,7 @@ type kindMeta struct { var kindRegistry = map[TracepointKind]kindMeta{ KindFd: {structName: "fd_event", enterAccepted: true}, KindOpen: {structName: "open_event", enterAccepted: true}, + KindMqOpen: {structName: "open_event", enterAccepted: true}, KindPathname: {structName: "path_event", enterAccepted: true}, KindName: {structName: "name_event", enterAccepted: true}, KindRet: {structName: "ret_event", enterAccepted: false}, diff --git a/internal/generate/retclassify_test.go b/internal/generate/retclassify_test.go index 1c5b2ac..f837957 100644 --- a/internal/generate/retclassify_test.go +++ b/internal/generate/retclassify_test.go @@ -7,7 +7,7 @@ func TestClassifyRetRead(t *testing.T) { "fgetxattr", "flistxattr", "getdents", "getdents64", "getxattr", "lgetxattr", "listxattr", "llistxattr", "pread64", "preadv", "preadv2", "process_vm_readv", "read", "readlink", "readlinkat", - "readv", "recvmsg", "recvfrom", "syslog", + "readv", "recvmsg", "recvfrom", "syslog", "mq_timedreceive", } for _, name := range reads { if got := ClassifyRet("sys_exit_" + name); got != ReadClassified { @@ -19,7 +19,7 @@ func TestClassifyRetRead(t *testing.T) { func TestClassifyRetWrite(t *testing.T) { writes := []string{ "process_vm_writev", "pwrite64", "pwritev", "pwritev2", - "sendmsg", "sendto", "write", "writev", + "sendmsg", "sendto", "write", "writev", "mq_timedsend", } for _, name := range writes { if got := ClassifyRet("sys_exit_" + name); got != WriteClassified { |
