summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-01 23:13:11 +0300
committerPaul Buetow <paul@buetow.org>2026-06-01 23:13:11 +0300
commitbd544509846fb7e1882aa466d24aea937c2b2804 (patch)
tree5e9a31a7cc6a7256b52ce4e842d9bb72f2baed80
parent8251307ac3187b346ed12e9a54d9bf6d7cba7e53 (diff)
fix(classify): mq_timedsend returns status, not bytes — make ret UNCLASSIFIED
mq_timedsend(2)/mq_send(3) return 0 on success or -1 on error; the payload size msg_len is an INPUT argument, never the return value. It was wrongly listed in retClassifications as WriteClassified, which made bytesFromRet attribute its 0 return as "bytes written" (the stats engine WriteClassified path). Remove it so its return stays UNCLASSIFIED, consistent with its POSIX mq sibling mq_timedreceive (which legitimately stays ReadClassified because it returns the received byte count). This is the exact same defect just fixed for SysV msgsnd (5057bd9) and mirrors the msgrcv/msgsnd asymmetry. Regenerated tracepoints/docs accordingly and updated the pre-existing classify unit test and the TestPosixMqBasic integration assertion: the mq_timedsend send no longer asserts a write byte count (now expects 0), while mq_timedreceive keeps its received-byte-count assertion. Verified: mage generate idempotent, mage build OK, internal/generate tests pass. TestPosixMqBasic skips in this sandbox (mq_open: permission denied) but compiles with the corrected assertions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rw-r--r--docs/syscall-tracing-plan.md2
-rw-r--r--integrationtests/ipc_test.go5
-rw-r--r--internal/c/generated_tracepoints.c4
-rw-r--r--internal/c/generated_tracepoints_result.txt2
-rw-r--r--internal/generate/classify.go8
-rw-r--r--internal/generate/classify_test.go4
6 files changed, 18 insertions, 7 deletions
diff --git a/docs/syscall-tracing-plan.md b/docs/syscall-tracing-plan.md
index 2225d48..9bb94e1 100644
--- a/docs/syscall-tracing-plan.md
+++ b/docs/syscall-tracing-plan.md
@@ -94,7 +94,7 @@ Payload bytes classified by return value:
- ReadClassified: `fgetxattr`, `flistxattr`, `getdents`, `getdents64`, `getrandom`, `getxattr`, `getxattrat`, `lgetxattr`, `listxattr`, `listxattrat`, `llistxattr`, `mq_timedreceive`, `msgrcv`, `pread64`, `preadv`, `preadv2`, `process_vm_readv`, `read`, `readlink`, `readlinkat`, `readv`, `recvfrom`, `recvmsg`, `syslog`
- TransferClassified: `copy_file_range`, `sendfile64`, `splice`, `tee`, `vmsplice`
-- WriteClassified: `mq_timedsend`, `process_vm_writev`, `pwrite64`, `pwritev`, `pwritev2`, `sendmsg`, `sendto`, `write`, `writev`
+- WriteClassified: `process_vm_writev`, `pwrite64`, `pwritev`, `pwritev2`, `sendmsg`, `sendto`, `write`, `writev`
All other traced syscalls are treated as non-bytes for throughput accounting.
Memory extent is tracked separately via address-space metrics.
diff --git a/integrationtests/ipc_test.go b/integrationtests/ipc_test.go
index 5672bbe..9c1efcc 100644
--- a/integrationtests/ipc_test.go
+++ b/integrationtests/ipc_test.go
@@ -113,7 +113,10 @@ func TestPosixMqBasic(t *testing.T) {
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)
+ // mq_timedsend returns 0 on success (a status, not a byte count), so it is
+ // UNCLASSIFIED and must NOT be attributed any write bytes. Only
+ // mq_timedreceive returns a real received byte count (ReadClassified).
+ assertEventBytesEqual(t, result, sendExp, 0)
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 8e3f996..4ec7b86 100644
--- a/internal/c/generated_tracepoints.c
+++ b/internal/c/generated_tracepoints.c
@@ -2593,7 +2593,7 @@ int handle_sys_enter_mq_timedsend(struct syscall_trace_enter *ctx) {
return 0;
}
-/// sys_exit_mq_timedsend is a struct ret_event (WRITE_CLASSIFIED) (kind=ret)
+/// sys_exit_mq_timedsend is a struct ret_event (UNCLASSIFIED) (kind=ret)
SEC("tracepoint/syscalls/sys_exit_mq_timedsend")
int handle_sys_exit_mq_timedsend(struct syscall_trace_exit *ctx) {
__u32 pid, tid;
@@ -2613,7 +2613,7 @@ 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 = WRITE_CLASSIFIED;
+ ev->ret_type = UNCLASSIFIED;
bpf_ringbuf_submit(ev, 0);
return 0;
diff --git a/internal/c/generated_tracepoints_result.txt b/internal/c/generated_tracepoints_result.txt
index 0a88f30..3804441 100644
--- a/internal/c/generated_tracepoints_result.txt
+++ b/internal/c/generated_tracepoints_result.txt
@@ -544,7 +544,7 @@ sys_exit_mq_getsetattr is a struct ret_event (UNCLASSIFIED) (kind=ret)
sys_exit_mq_notify is a struct ret_event (UNCLASSIFIED) (kind=ret)
sys_exit_mq_open is a struct ret_event (UNCLASSIFIED) (kind=ret)
sys_exit_mq_timedreceive is a struct ret_event (READ_CLASSIFIED) (kind=ret)
-sys_exit_mq_timedsend is a struct ret_event (WRITE_CLASSIFIED) (kind=ret)
+sys_exit_mq_timedsend is a struct ret_event (UNCLASSIFIED) (kind=ret)
sys_exit_mq_unlink is a struct ret_event (UNCLASSIFIED) (kind=ret)
sys_exit_mremap is a struct ret_event (UNCLASSIFIED) (kind=ret)
sys_exit_mseal is a struct ret_event (UNCLASSIFIED) (kind=ret)
diff --git a/internal/generate/classify.go b/internal/generate/classify.go
index c1125e5..3ba0c00 100644
--- a/internal/generate/classify.go
+++ b/internal/generate/classify.go
@@ -632,5 +632,11 @@ var retClassifications = map[string]RetClassification{
// UNCLASSIFIED so the stats engine never treats the return as bytes written.
"write": WriteClassified,
"writev": WriteClassified,
- "mq_timedsend": WriteClassified,
+ // mq_timedsend is deliberately NOT listed here: mq_timedsend(2)/mq_send(3)
+ // return 0 on success or -1 on error — NOT a byte count (msg_len is an
+ // INPUT arg, never the return). Listing it as WriteClassified made
+ // bytesFromRet attribute its 0 return as "bytes written". Like its POSIX mq
+ // sibling mq_timedreceive (which genuinely returns the received byte count
+ // and stays ReadClassified), mq_timedsend's int status must stay
+ // UNCLASSIFIED. This mirrors the SysV IPC msgsnd vs msgrcv asymmetry.
}
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index bd03af4..4993293 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -350,7 +350,9 @@ func TestClassifyMqSyscallPairsAcceptedAndClassified(t *testing.T) {
}{
{"mq_open", "struct open_event", "UNCLASSIFIED"},
{"mq_unlink", "struct path_event", "UNCLASSIFIED"},
- {"mq_timedsend", "struct fd_event", "WRITE_CLASSIFIED"},
+ // mq_timedsend returns 0/-1 (a status), not a byte count, so its ret is
+ // UNCLASSIFIED. Only mq_timedreceive returns a real received byte count.
+ {"mq_timedsend", "struct fd_event", "UNCLASSIFIED"},
{"mq_timedreceive", "struct fd_event", "READ_CLASSIFIED"},
{"mq_notify", "struct fd_event", "UNCLASSIFIED"},
{"mq_getsetattr", "struct fd_event", "UNCLASSIFIED"},