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 /internal/generate | |
| parent | 271af607921ceabc640271c475a66e45b9460d3f (diff) | |
d7: add POSIX mq syscall kind/classification and coverage
Diffstat (limited to 'internal/generate')
| -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 |
6 files changed, 168 insertions, 6 deletions
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 { |
