summaryrefslogtreecommitdiff
path: root/internal/generate
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-20 22:25:40 +0300
committerPaul Buetow <paul@buetow.org>2026-05-20 22:25:40 +0300
commit7a9839917461b12c810329ccb8fd3c6de06902d2 (patch)
tree7e68d52cec796d8a4d7b5110ba87cdce48a0fcb9 /internal/generate
parent271af607921ceabc640271c475a66e45b9460d3f (diff)
d7: add POSIX mq syscall kind/classification and coverage
Diffstat (limited to 'internal/generate')
-rw-r--r--internal/generate/bpfhandler.go14
-rw-r--r--internal/generate/classify.go21
-rw-r--r--internal/generate/classify_test.go119
-rw-r--r--internal/generate/codegen_test.go15
-rw-r--r--internal/generate/kindregistry.go1
-rw-r--r--internal/generate/retclassify_test.go4
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 {