summaryrefslogtreecommitdiff
path: root/internal
AgeCommit message (Collapse)Author
6 daysdocs(follow-forks): add process-tree-following plan + filter.c referencePaul Buetow
Document the planned opt-in "follow forks" mode that would let ior trace a target PID and all its descendants (needed for the landlock_restrict_self integration case, task ci0, and for tracing forking workloads as a tree). The plan covers the BPF descendant-set map, sched_process_fork/exit hooks, the FOLLOW_FORK gate in filter(), userland flag/seeding/assertion changes, and explicitly requires syscall-count aggregation to roll up across the followed tree. Add a reference comment above filter() pointing to the plan. Plan only — not implemented. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6 daysfeat(parquet): export rename/link oldname + assert oldname capture end-to-endPaul Buetow
rename-family (rename/renameat/renameat2) and link-family (link/linkat/ symlink/symlinkat) capture BOTH paths in BPF (name_event.oldname at args[1] for the AT-variants, after a dirfd, plus newname), but only newname reached any persisted output. event.Pair.FileName() resolves to oldnameNewnameFile.Name() == Newname, so the parquet `file` column, CSV, and flamegraph Path all carried newname; the captured oldname survived only in the TUI stream String() repr ("old:... ->new:..."). The oldname capture (and its args[1] index for the AT-variants) was therefore never validated end-to-end, and a wrong-oldname-index regression would surface in no persisted output or test. Surface oldname as one additive, backward-compatible column, mirroring the dedicated optional-column convention (address_space_bytes, requested_sleep_ns, epoll_*): - old_file (String): source/old path for rename/link syscalls; empty for every other syscall. The existing `file` column keeps its semantics (newname for rename/link). Data flows name_event.oldname -> event.Pair.Oldname (populated in handleNameExit alongside the existing File) -> streamrow.Row.OldName -> parquet.Record.OldFile. Docs (docs/parquet-querying.md) and the Magefile parquetValidate column list updated in lockstep. The new TestRenameRenameatOldnameInParquet integration test exercises the renameat AT-variant (oldname at args[1] after the olddfd dirfd) and asserts the parquet old_file column carries renameat-old.txt while file carries renameat-new.txt, and that old_file stays empty for non-rename/ link rows -- locking in the args[1] oldname capture end-to-end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6 daysfeat(parquet): surface epoll_ctl op/target-fd/events metadataPaul Buetow
epoll_ctl's BPF handler already decodes the operation (args[1]), target descriptor (args[2]), and requested event mask (args[3]->events) into an EpollCtlEvent, but the single resolved-epfd `fd` column was the only epoll detail reaching the output schema. Consumers could not see which descriptor was registered nor the operation performed. Surface the metadata as three additive, backward-compatible columns, mirroring the existing dedicated optional-column convention used by requested_sleep_ns and address_space_bytes: - epoll_op (String): ADD/MOD/DEL, or the raw decimal for unknown ops; empty for non-epoll_ctl rows. - epoll_target_fd (Int32): registered descriptor (args[2]); 0 otherwise. - epoll_events (UInt32): requested event mask; 0 otherwise. Data flows EpollCtlEvent -> event.Pair (new EpollCtl/HasEpoll fields, populated in handleEpollCtlExit) -> streamrow.Row -> parquet.Record. The op-to-string mapping lives on event.EpollCtl.OpName. Docs (docs/parquet-querying.md) and the Magefile parquetValidate column list updated in lockstep (also adding the previously-undocumented address_space_bytes/requested_sleep_ns columns). The polling parquet integration test now asserts epoll_ctl rows carry a decoded op and a valid target fd, and that other syscalls leave epoll_op empty. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daysfix(classify): assign vmsplice to FamilyNetwork, not MiscPaul Buetow
vmsplice(int fd, const struct iovec*, unsigned long nr_segs, unsigned int flags) is the iovec<->pipe variant of splice(2) and belongs to the same fd byte-mover cohort as its direct siblings splice/tee (and sendfile64/ copy_file_range). Its KIND (KindFd@arg0) and RET (TransferClassified, byte count) already matched splice/tee — only the family was wrong. Root cause: vmsplice was absent from the syscallFamilies map in internal/generate/family.go; its name matches no fsNameMarkers and it is not in fsSyscalls, so ClassifySyscallFamily fell through to FamilyMisc. This is the same documented Misc-fall-through anti-pattern already fixed for alarm/adjtimex/fanotify_init/fanotify_mark/file_getattr/file_setattr. The established mj0 decision placed splice/tee in Network, so the minimal sibling-consistent fix is vmsplice -> Network. Added "vmsplice": FamilyNetwork next to splice/tee with an explanatory comment, then re-ran `mage generate`. The regen is minimal and idempotent: only the two vmsplice trace IDs flip Misc->Network in generated_types.go and the vmsplice entry flips Misc->Network in generated_tracepoints.go; no TraceId renumbering and no other syscalls change. The generated C tracepoints are unaffected (family is a Go-side tag). Also moved vmsplice from the Misc list to the Network list in docs/syscall-tracing-plan.md (hand-maintained, docs-drift-validated), and corrected the misc_test.go comments which described vmsplice as a Misc syscall — it is still issued by the misc-basic workload and traced by name, but its transfer/byte-count coverage lives in retbytes_test.go alongside splice/tee. No vmsplice family assertion existed in the integration suite, so no coverage was relocated, only comments corrected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
12 daysfix(classify): capture timerfd_gettime/settime + splice/tee fd, not KindNullPaul Buetow
Root cause: the generic field matcher classifyByField only maps an arg literally named "fd" to KindFd. Several syscalls operate on an EXISTING fd whose tracepoint arg0 is named something else, so they fell through to KindNull -> null_event, capturing NO descriptor and dropping the fd they act on: - timerfd_gettime / timerfd_settime: arg0 is "int ufd" (the timerfd) - splice: arg0 is "int fd_in" (source fd of an in-kernel transfer) - tee: arg0 is "int fdin" (source fd of an in-kernel transfer) Fix: add explicit KindFd overrides for these four sys_enter_* keys to nameOnlyKindsTable so the enter handler captures arg0, mirroring the established epoll_wait(epfd) / mq_*(mqdes) / sendfile64(out_fd) / copy_file_range(fd_in) precedent. splice/tee were surfaced by a systemic sweep of tracepoint formats for fd-typed arg0 named other than "fd" that currently classify to null; they are TransferClassified siblings of sendfile64/copy_file_range and clearly fd-operating. The *at() family (dfd arg0) is intentionally untouched: it is path-classified, and timerfd_create remains the KindEventfd fd CREATOR. Regenerated artifacts (mage generate): the four enter handlers now emit fd_event capturing ctx->args[0] instead of null_event; exit handlers stay UNCLASSIFIED. Updated the generated kind maps, the golden result.txt, the classify_test expectations, and docs/syscall-tracing-plan.md (moved the four from kind "null" to kind "fd"; families IPC/Network unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 daysfix(classify): assign file_setattr to FamilyFS, not MiscPaul Buetow
file_setattr(2) (Linux 6.13+) is the write counterpart of file_getattr: it sets a file's extended attributes (struct file_attr) given dirfd@arg0 + pathname@arg1 + attr-buffer + size + at_flags, and returns 0/-1 (not a byte count). Its name matches none of the fsNameMarkers substrings ("stat"/"xattr"/"chmod"/"chown") and it is absent from the fsSyscalls set, so it was falling through to FamilyMisc -- the same alarm/fanotify/file_getattr-style misclassification. Add it to the explicit family map for sibling consistency with file_getattr. This also completes the file_getattr regeneration: the prior fix (96de9ef) was generated with a partial target that updated generated_tracepoints.go but did not propagate file_getattr into the traceId2Family map in generated_types.go; a full `mage generate` here reconciles both file_getattr (1058/1059) and file_setattr (1056/1057) to FamilyFS. mage build, generate idempotency, and the internal/generate, /tracepoints, /types unit tests (incl. docs-drift sync) all pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 daysfix(classify): assign file_getattr to FamilyFS, not MiscPaul Buetow
file_getattr(2) (Linux 6.13+) retrieves a file's extended attributes (struct file_attr) given a dirfd + pathname + attr buffer + size + at_flags. It is a path-based filesystem operation, the counterpart of statx and the FS_IOC_FSGETXATTR ioctl, so it belongs in FamilyFS. It was falling through to FamilyMisc because the fsNameMarkers substring list keys on "stat"/"xattr"/"chmod"/"chown" — "getattr" matches none of them — and the syscall is absent from the fsSyscalls set, the same Misc-fall-through defect previously fixed for alarm/fanotify_init/ fanotify_mark. Add an explicit "file_getattr": FamilyFS entry to the syscallFamilies map, regenerate the Go tracepoint map, and update the docs/syscall-tracing-plan.md family listing to match. KIND stays KindPathname (pathname@args[1], data-driven from the live tracepoint) and the return is 0/-1, hence UNCLASSIFIED (not a byte count) — both already correct, only the family was wrong. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 daysfix(classify): assign fanotify_mark to FamilyIPC, not MiscPaul Buetow
fanotify_mark(2) adds, removes, or modifies a mark on an fanotify notification group. Its arg0 is the fanotify group fd returned by fanotify_init(2); it carries a dirfd@arg3 and an optional pathname@arg4 and returns 0/-1 (not a byte count). It is the operation counterpart of fanotify_init and the direct analog of inotify_add_watch (both register a watch/mark on a filesystem object via the notification-group fd). inotify_add_watch is FamilyIPC, and fanotify_init was just moved Misc->IPC to sit with the fd-based event-notification primitives (eventfd, signalfd, timerfd, userfaultfd, inotify_*). fanotify_mark, however, was still falling through to FamilyMisc by omission from the explicit family table -- the same alarm/adjtimex-style misclassification fixed for fanotify_init in 88769d4, and flagged there as still-outstanding for fanotify_mark. Add it to the IPC family map for sibling consistency and regenerate. KIND is unchanged and correct: KindPathname capturing the optional pathname at args[4]. This matches the *at() cohort convention (fchmodat, fchownat, unlinkat, mkdirat, newfstatat, utimensat, name_to_handle_at all carry a dirfd at arg0 yet capture the pathname), since fanotify_mark has a dirfd@arg3 + pathname@arg4 pair. RET stays UNCLASSIFIED (returns 0/-1). Docs plan updated to keep the docs-drift tests in sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 daysfix(close): deregister fd only on successful close (ret==0)Paul Buetow
applyFdCloseState evicted the fd->path mapping (fdState + proc-fd cache) on every close exit, ignoring the return value. A failed close — most importantly EBADF ("fd isn't a valid open file descriptor"), but also EINTR/EIO — did not release any descriptor we track, so dropping the mapping there would let a later genuine close or a reuse of the fd number resolve against stale/empty state. Gate the eviction on ret==0, mirroring the ret==0 guard already used by applyCloseRangeState for close_range. close's exit tracepoint is generated as a ret_event (UNCLASSIFIED), so the exit carries the close return value in RetEvent.Ret. Tests: the close-exit event was being built as an fd_event, which does not match the real BPF wire format (sys_exit_close emits EXIT_RET_EVENT). Add a makeExitCloseEvent helper that emits the correct ret_event, route all 18 close-exit call sites and TestHandleFdExitCloseClearsProcFdCache through it, and add CloseFailureTest asserting a failed close (ret=-1) leaves the fd tracked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 daysfix(classify): assign fanotify_init to FamilyIPC, not MiscPaul Buetow
fanotify_init(2) creates and initializes an fanotify notification group and returns an event-queue file descriptor. It is the direct analog of inotify_init1 (both are filesystem-event notification facilities whose group-creating syscall is a flags-taking fd-creator). inotify_init/ inotify_init1 are FamilyIPC alongside the other fd-based event-notification primitives (eventfd, signalfd, timerfd, userfaultfd), yet fanotify_init fell through to FamilyMisc by omission from the explicit family table -- an alarm/adjtimex-style misclassification inconsistent with its siblings. Add fanotify_init to the IPC family map and regenerate. Kind (KindEventfd, flags at args[0]) and ret (UNCLASSIFIED, returned fd captured via the fd mechanism) were already correct and are unchanged. fanotify_mark stays in Misc (path-marking, not fd creation). Docs plan updated to keep the docs-drift test in sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01fix(classify): mq_timedsend returns status, not bytes — make ret UNCLASSIFIEDPaul Buetow
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>
2026-06-01fix(classify): msgsnd returns status, not bytes — make ret UNCLASSIFIEDPaul Buetow
msgsnd(2) returns 0 on success or -1 on error; the payload size msgsz is an INPUT argument, never the return value. It was wrongly listed in retClassifications as WriteClassified, which made the stats engine treat its 0 return as "bytes written". Remove it so its return stays UNCLASSIFIED, consistent with its SysV IPC siblings (msgrcv legitimately stays ReadClassified because it returns a received byte count). Regenerated tracepoints/docs accordingly. Verified: mage generate idempotent, mage build OK, internal/generate tests pass, and the TestSysVMsgBasic integration test (added in task 7i0) still passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01fix(classify): assign alarm to FamilyTime, not MiscPaul Buetow
alarm(2) arranges for a SIGALRM after a given number of seconds; it is a simplified setitimer(ITIMER_REAL) and, per alarm(2) NOTES, "alarm() and setitimer(2) share the same timer; calls to one will interfere with use of the other." The syscallFamilies table omitted alarm, so it fell through to FamilyMisc while its siblings setitimer/getitimer/timer_create were correctly FamilyTime — an adjtimex-style misclassification (cf. 7243b7c). Add alarm -> FamilyTime and move it from Misc to Time in the tracing plan; regenerate the family maps (trace IDs 468/469 now FamilyTime, "alarm": "Time"). Kind classification (KindNull/null_event: the single arg is an unsigned int seconds, no fd/path) and the UNCLASSIFIED return (seconds remaining, not a byte count; alarm never fails) were already correct. Also harden the misc-basic integration test with a deterministic enter_alarm assertion (alarm(0) is issued unconditionally by the scenario; the syscall-entry tracepoint always fires) so the alarm enter path is covered end-to-end even though alarm is now FamilyTime rather than Misc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(internal): make comm-propagation/filter tests hermeticPaul Buetow
TestCommPropagation and TestEventTypeFiltering/FdEventFiltering were flaky in full `mage test` runs (passing in isolation). For synthetic events whose tid was not in the comm cache, the event loop fell back to commResolver's default resolveFn, which reads /proc/<tid>/comm on the host. The fixed test pids/tids are small (e.g. defaultTid+100 == 111, defaultPid+1 == 11) and collide with real transient kernel threads (e.g. kworker/0:1-events), so the resolved comm depended on what happened to be running on the host at that instant. Fix: use commResolver's existing injectable resolveFn seam. Add a newHermeticCommResolver() test helper whose resolveFn returns ("", nil) and never touches /proc, and inject it into TestCommPropagation (via eventLoopConfig.commResolver) and newEventLoopWithFilter (used by TestEventTypeFiltering). No production code changes. Assertions are unchanged: positive comm names still come from the synthetic OpenEvent.Comm bytes; cache-miss tids now deterministically resolve to empty regardless of host state. Updated the stale "use a very large TID to avoid /proc collisions" comment accordingly. Verified: -count=50 (affected tests) and -race -count=10 green, full `mage test` and `mage build` green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01fix(types): fast-decode hot events at padded kernel struct sizePaul Buetow
The BPF side reserves sizeof(struct ...) via bpf_ringbuf_reserve, so the userspace ringbuf sample length equals the C struct size INCLUDING trailing alignment padding. For five event structs whose last field is 32-bit but which also contain a __u64 (forcing 8-byte trailing pad), the *EventSize fast-decode gate constants were set to the unpadded field-sum size instead of sizeof: open_event 300 -> 304 fd_event 28 -> 32 ret_event 36 -> 40 socket_event 36 -> 40 open_by_handle_at_event 28 -> 32 Because the kernel payload length never equalled those constants, the fast explicit-offset decoders were silently bypassed and every such event (including the very hot ret_event on each syscall exit, and fd_event on read/write/etc enter) fell back to the slow reflection-based binary.Read path. binary.Read reads only the field bytes and ignores trailing padding, so values were always correct -- this was a performance regression, not a correctness bug; verified by decoding padded payloads. Fix mirrors the existing socketpair/accept/pipe/eventfd/poll V1/V2 handling: the size gate now accepts both the kernel sizeof and the legacy field-sum size (the latter still emitted by Go binary.Write in tests and Bytes()), and the trailing pad bytes are ignored. Added kernel-layout tests feeding the padded payload for all five fixed decoders. Audit (task i20) confirmed every other event struct's Go layout, field order, and fast-decode offsets match the C side byte-for-byte. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(generate): remove redundant pure-classification unit testsPaul Buetow
Classification correctness (which family/kind/return-class a syscall maps to) is verified by inspection against the man pages and the classifier rules, not by dedicated unit tests. The tracing-relevant outcome — which fd/path/byte-count the generated BPF C actually captures — is covered by the GenerateTracepointsC codegen tests and the end-to-end integration tests, all of which are retained. Removed: - internal/generate/family_test.go (ClassifySyscallFamily / .Family table) - internal/generate/retclassify_test.go (ClassifyRet read/write/transfer/ unclassified tables) - ~70 pure-classification tests trimmed from classify_test.go, keeping only the GenerateTracepointsC codegen/tracing tests plus the shared helpers (mustParseAll, mqFormats, phaseAFormats, syntheticEnter/Exit, itoa) used by codegen_test.go. - pure-classification funcs interleaved in codegen_test.go (TestClassifyRet*Unclassified, TestClassifyTkillFallsThroughToNull, Test{Mkdirat,Rmdir}FamilyAndKindMatchSiblings). Kept all TestGenerate* handler tests (they assert the generated BPF C captures the correct fd/path/arg-index/return classification), the isNoreturnSyscall tests, docs-drift guards, eventloop dispatch tests, and the integration suite — so every affected syscall still has tracing coverage. No tracing gaps discovered. generate package: go test (incl. -race) green; mage build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31fix(classify): assign adjtimex to FamilyTime, not MiscPaul Buetow
adjtimex(2) and clock_adjtime(2) share one man page: both tune or query the kernel clock (clock_adjtime is adjtimex with an explicit clockid) and return a clock-state code or -1. The syscallFamilies table omitted adjtimex, so it fell through to FamilyMisc while its sibling clock_adjtime was correctly FamilyTime. Add adjtimex -> FamilyTime and move it from Misc to Time in the tracing plan; regenerate the family maps (trace IDs 418/419 now FamilyTime, "adjtimex": "Time"). Kind classification (KindNull/null_event) and the UNCLASSIFIED return (a clock-state code, not a byte count) were already correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31listxattrat: READ-classify return for xattr-list family consistencyPaul Buetow
listxattrat(2) (Linux 6.13+) returns the size in bytes of the list of extended attribute names, exactly like listxattr/llistxattr/flistxattr, but its exit was classified UNCLASSIFIED, so its read bytes were dropped from I/O totals. Classify it as ReadClassified and regenerate the BPF handler (ret_type now READ_CLASSIFIED). This mirrors the getxattrat fix (task ku, commit c3177bd) and completes xattr-family consistency: get-family and list-family are READ_CLASSIFIED while set-family and remove-family stay UNCLASSIFIED (they return 0/-1). Update the docs ReadClassified list and the retclassify expectation, and add an ioworkload scenario plus integration test: the workload sets a user xattr then lists names via the raw listxattrat(2) syscall with AT_FDCWD, and the test asserts enter_listxattrat captures the file path and accounts the returned name-list size as read bytes. Task: r20 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31getxattrat: READ-classify return for xattr-get family consistencyPaul Buetow
getxattrat(2) (Linux 6.13+) returns the xattr value size in bytes, exactly like getxattr/lgetxattr/fgetxattr, but its exit was classified UNCLASSIFIED, so its read bytes were dropped from I/O totals. Classify it as ReadClassified and regenerate the BPF handler (ret_type now READ_CLASSIFIED). Path extraction (args[1], after the dirfd) and the name-not-captured-as-path behaviour were already correct. Update the docs ReadClassified list and the retclassify expectation, and add the first xattr integration coverage: an ioworkload scenario that sets then getxattrat-reads a user xattr on tmpfs, plus a test that asserts enter_getxattrat captures the file path (not the xattr name) and accounts the returned value size as read bytes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in semctl handler as KindSysVOp/FamilyIPCPaul Buetow
Audit of semctl(2) confirmed the implementation is correct: it is classified KindSysVOp in FamilyIPC, consistent with its SysV control-syscall siblings msgctl/shmctl (and semget/semop/semtimedop). The enter handler emits a null_event and captures no argument, so the semid at args[0] -- a System V IPC identifier, NOT a file descriptor -- is correctly not recorded as an fd. The exit handler reports the raw op-dependent int status (value or -1) as UNCLASSIFIED, never a byte count. The classification table already covered semctl, but only msgctl's generated handler body was directly asserted. Add dedicated lock-in tests mirroring TestGenerateMsgctlHandler: - TestGenerateSemctlHandler: enter emits null_event, no ctx->args[] capture, no ev->fd; exit ret_type UNCLASSIFIED. - TestClassifyRetSemctlUnclassified: ret is UNCLASSIFIED. No classification, generated C, docs, or runtime behavior changed (mage generate produces no diff), so this is a test-only addition. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30generate: treat rt_sigreturn as noreturn (suppress dead exit handler)Paul Buetow
rt_sigreturn(2) restores the pre-signal execution context off the signal stack frame and resumes the interrupted instruction; it never returns to the instruction after the syscall. man sigreturn(2) states plainly that "sigreturn() never returns", and tracing against /sys/kernel/tracing confirms it: sys_enter_rt_sigreturn fires once per signal-handler return while sys_exit_rt_sigreturn never fires. The generator previously emitted a dead handle_sys_exit_rt_sigreturn (it can never run) and recorded a per-tid syscall_enter_state_map entry on the enter path that nothing would ever delete (no exit fires), leaking entries in the bounded map on every signal-handler return. Add rt_sigreturn to noreturnSyscalls so codegen suppresses the dead exit handler and routes the enter handler through ior_on_noreturn_syscall_enter (sampling decision only, no map write), exactly like exit/exit_group. The enter null_event is still emitted, and the FamilySignals/KindNull classification is unchanged. Regenerated the C/Go artifacts and the result baseline accordingly, and generalized the related comments. Lock-in tests: TestRtSigreturnIsNoreturn asserts rt_sigreturn is noreturn; TestRtSigSiblingsAreNotNoreturn guards that the returning rt_sig* siblings are not; TestGenerateExitNoreturnHandlers now also covers rt_sigreturn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in pwritev WRITE byte-count classificationPaul Buetow
Audit of pwritev(2) confirmed the existing classification is correct: pwritev returns the number of bytes written, so its exit is WRITE_CLASSIFIED (matching write/pwrite64/writev/pwritev2), fd is at args[0] (KindFd), and it lives in the FS family. The read-side sibling preadv stays READ_CLASSIFIED. No implementation changes were needed. Add TestClassifyPwritevWriteByteCount as a lock-in test mirroring the prior pwritev2/pwrite64 audits, with a preadv off-by-one contrast guard and transfer/unclassified negative checks across the whole p/readv/writev family so any stray reclassification trips the test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(pipe): lock in pipe/pipe2 IPC classification and fd-pair exit readsPaul Buetow
Audit of pipe(2)/pipe2(2) (task dx) confirmed the tracing implementation is correct: KindPipe (not KindFd, since args[0] is an output ptr to int[2], not an fd), FamilyIPC, and an UNCLASSIFIED int return. Enter stashes the output ptr (flags=0 for pipe, args[1] for pipe2); exit reads the fd pair via bpf_probe_read_user guarded by ret==0, mirroring the socketpair pipe-like pattern. The only gaps were missing lock-in tests, now added: - codegen: assert the exit handler reads the fd pair from the stashed output buffer (ret==0 guard, bpf_probe_read_user, fd0/fd1) and that the flag-less pipe variant hardcodes flags=0 and never reads args[1]. - classify: pipe/pipe2 are never KindFd and stay UNCLASSIFIED on ret. - runtime: a failed pipe (ret==-1) tracks no descriptors and attaches no file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(perf_event_open): lock in audit findingsPaul Buetow
Audited perf_event_open(2) against the man page: it returns a new fd (or -1), args[0] is a struct perf_event_attr* userspace pointer (NOT an fd), args[1] is a monitored pid, and only args[3] group_fd is a real fd. The existing implementation is correct (KindPerfOpen by name, not KindFd; FamilySecurity; exit as UNCLASSIFIED RetEvent). Add lock-in tests: - codegen: assert args[0] is read via bpf_probe_read_user as the attr struct and never captured as an fd (negative assertions on args[0]/args[1]). - eventloop: a failed return (-1) registers no fd in fdState. - perfDescriptorName format pin (perf: prefix). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in msgctl SysV-op classificationPaul Buetow
msgctl(2) operates on a System V message queue identified by msqid (args[0]). msqid is a SysV IPC id returned by msgget, NOT a file descriptor; capturing it as an fd would corrupt the fd-resource view. ior already classifies msgctl as KindSysVOp in FamilyIPC (consistent with siblings semctl/shmctl/msgget/msgsnd/msgrcv), emits a null_event with no arg capture, and reports the int status as UNCLASSIFIED. Add lock-in tests pinning this behavior: - TestGenerateMsgctlHandler: KindSysVOp for msgctl/semctl/shmctl, all FamilyIPC; generated enter handler emits null_event and captures no args (asserts no ctx->args[ and no ev->fd); exit ret is UNCLASSIFIED. - TestClassifyRetMsgctlUnclassified: msgctl ret is never a byte count. No implementation change needed; mage generate produces no diff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(mlockall): lock in KindNull/FamilyMemory classificationPaul Buetow
Audit of mlockall(2) (task 6w). mlockall(int flags) locks ALL process memory and takes a single flags bitmask (MCL_CURRENT/MCL_FUTURE/ MCL_ONFAULT) with NO address range, unlike its KindMem siblings mlock/mlock2/munlock (which take addr+len). It is therefore correctly classified KindNull in FamilyMemory, matching its sibling munlockall(2). All existing classification (classify.go, family.go, generated artifacts, docs plan) already match; no fixes needed. Add two lock-in tests documenting the reasoning: TestGenerateNullHandlerMlockall asserts the enter handler emits a null_event and never captures the flags int as an addr/fd/path, and TestClassifyRetMlockallUnclassified asserts the 0/-1 return is UNCLASSIFIED (not a byte count). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(dup): lock in fd_event handler captures oldfd (args[0])Paul Buetow
Audit of dup(2) found the tracing implementation already correct and consistent with its dup2/dup3 siblings: dup(int oldfd) takes a single fd argument (the sys_enter_dup tracepoint exposes it as field "fildes", unsigned int, at args[0]). It is classified KindFd (a plain fd_event), the enter handler captures ev->fd from args[0] per the KindFd convention, it is in the FS family (fd grouping), and its exit returns the new (lowest-numbered unused) descriptor or -1 as a plain UNCLASSIFIED ret_event (never a byte-count transfer). Like dup2, dup carries no flags and clears FD_CLOEXEC on the duplicate; the eventloop registerDup path registers the returned newfd onto the same underlying file with flags=0, which it already honors (applyFdTransferOp handles SYS_ENTER_DUP). Docs (FS, fd) and the drift tests are in sync; existing coverage already includes TestClassifyDup, the makeFdDupTestData full-lifecycle eventloop test, and integration TestDupBasic/TestDupInvalidFd. No discrepancies were found, so add a lock-in test (matching the dup2 audit) asserting the generated BPF C for dup captures fd from args[0] (not args[1]), emits an fd_event (not a dup3_event), wires no flags, and classifies the exit UNCLASSIFIED. Adds FormatExitDup testdata to drive the exit handler assertions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(arch_prctl): lock in KindNull/UNCLASSIFIED + FamilyProcessPaul Buetow
Audit of the arch_prctl(2) syscall found the tracing implementation already correct and consistent with the man page: - enter classifies as KindNull (op/addr never captured as fd/path) - exit is a ret_event with UNCLASSIFIED ret_type (int 0/-1 status) - family is Process (deliberately, unlike its x86 siblings ioperm/iopl/modify_ldt which are Misc), in sync with the docs and the tracepoints drift tests Add dedicated lock-in tests mirroring the prior iopl audit, using the real kernel tracepoint fields (option/arg2 on enter, ret on exit) so the heuristics are proven safe even without the name-only mapping. Also add explicit FamilyProcess assertions for arch_prctl and personality to guard against drift toward Misc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in listen(2) handler classificationPaul Buetow
Audit of syscall listen(2): int listen(int sockfd, int backlog). Confirmed the tracing implementation already matches the man page and its socket siblings (bind/connect/accept/getsockname/getpeername): - KindFd, capturing ev->fd = args[0] (sockfd) - FamilyNetwork - exit ret_event UNCLASSIFIED (returns 0/-1, no byte count) listen was already covered by the name-based classify/family/retclassify tests but lacked a dedicated generated-handler lock-in test like its bind/getsockname siblings. Add FormatListen/FormatExitListen tracepoint fixtures and TestGenerateListenHandler asserting the enter captures fd=args[0] (and never backlog at args[1]) and the exit stays UNCLASSIFIED. No classification or generated-code changes; mage generate produces no diff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(kexec_file_load): lock in KindFd/Security/UNCLASSIFIED auditPaul Buetow
Audit of kexec_file_load(2) against the man page confirmed the existing classification is already correct and consistent: KindFd capturing kernel_fd at args[0], FamilySecurity (matching its sibling kexec_load after task 6v), and an UNCLASSIFIED ret_event exit (returns 0/-1). The cmdline argument (args[3]) is a kernel command-line STRING, not a filesystem path, and is correctly never read as a path; the second fd initrd_fd (args[1]) is not captured, per the single-fd KindFd convention. Add a dedicated lock-in test plus real-kernel-format fixtures so future refactors cannot silently regress the fd wiring: assert ev->fd=args[0], no args[1] fd capture, no bpf_probe_read_user_str on the cmdline, and an UNCLASSIFIED (never READ/WRITE/TRANSFER) exit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(socketpair): lock in domain-is-not-an-fd invariant (c00)Paul Buetow
Audit of socketpair(2) found the tracing implementation already correct: KindSocketpair captures the two output fds from the sv[2] buffer (args[3]) at exit and never treats args[0] (the address-family/domain constant) as a file descriptor. Family=Network and UNCLASSIFIED ret are consistent with the socket/accept siblings and the docs. Add regression lock-in tests so a future field-shape or classification change cannot silently regress to recording the domain integer as a bogus fd: - TestClassifySocketpairNotFd: pins the name-based override so socketpair is KindSocketpair, never the generic KindFd path that reads args[0]. - TestHandleSocketpairExitDoesNotTrackDomainAsFd: uses AF_INET6 (10), distinct from the returned fds, and asserts fd 10 is never tracked while sv0/sv1 are. - TestHandleSocketpairExitDropsFdsOnError: on ret!=0 no descriptors are tracked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(setsockopt): lock in KindFd enter, UNCLASSIFIED exit, Network familyPaul Buetow
Audit of setsockopt(2) found the tracing implementation already correct: sockfd captured at ctx->args[0] (KindFd), exit ret_event UNCLASSIFIED, and FamilyNetwork — matching the man page and the bind/connect/getsockname/ getpeername/getsockopt siblings, with generated C/Go and docs all consistent. Add lock-in tests mirroring prior per-syscall audits: - TestClassifySetsockoptEnterFd: enter is KindFd with no pathname capture, asserted against the real sockfd/level/optname/optval/optlen fields. - TestClassifyExitSetsockoptUnclassifiedRet: exit is KindRet + UNCLASSIFIED (0/-1 status, not a byte count). - TestClassifyExitGetsockoptUnclassifiedRet: same for the read-side sibling. - TestClassifySyscallFamily: pin setsockopt (enter+exit) and getsockopt to FamilyNetwork. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(rmdir): lock in FS family, args[0] pathname capture, UNCLASSIFIED exitPaul Buetow
Audit of the rmdir(2) syscall found the tracing implementation already correct and fully consistent with its siblings: rmdir is in the FS family, classified KindPathname with the pathname captured from args[0] (its generated BPF C handler is byte-identical to unlink's), and its exit is a ret_event with UNCLASSIFIED ret_type (rmdir returns int 0/-1, not a byte count). The docs and drift tests, integration tests (unlink-rmdir success and unlink-rmdir-notempty ENOTEMPTY failure), and retclassify coverage all already match. To guard against future drift, add a dedicated rmdir lock-in: - FormatRmdir tracepoint fixture (single const char * pathname at args[0], mirroring the real sys_enter_rmdir format and unlink's shape). - TestGenerateRmdirHandlerCapturesPathFromArgs0: asserts the generated handler reads the path from args[0] (with a negative guard against args[1], since rmdir has no dirfd) and that the exit stays UNCLASSIFIED. - TestRmdirFamilyAndKindMatchSiblings: asserts rmdir shares FamilyFS and KindPathname/pathname with unlink/unlinkat/mkdir. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(request_key): lock in keyctl kind, security family, and unclassified returnPaul Buetow
Audit of request_key(2) found the tracing implementation already correct and consistent with the man page and the prior keyctl audit (task 7v): request_key classifies as KindKeyctl/FamilySecurity, the BPF handler captures option=-2 sentinel and key_serial=dest_keyring (args[3]) with no path/string capture of the const char * type/description/callout_info key-metadata args, and the exit returns a key serial / -1 that stays UNCLASSIFIED. Strengthen the dedicated TestClassifyRequestKey beyond a bare kind check to also assert PathnameField stays empty (string args are key metadata, not paths), family is Security on enter and exit, and the return is UNCLASSIFIED — bringing it to parity with the add_key contrast assertion. No code/generated changes; mage generate produces no diff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in pwrite64 WRITE_CLASSIFIED auditPaul Buetow
Add TestClassifyPwrite64WriteByteCount pinning the pwrite64(2) audit: fd at args[0] (KindFd), FS family, and WRITE_CLASSIFIED return (the syscall returns the number of bytes written). Asserts pread64 stays READ_CLASSIFIED as the read-side positional contrast, guards against transfer/unclassified misclassification, and checks the write/pread sibling group so a stray reclassification trips the test. No implementation changes: classify.go, family.go, generated C/Go, and docs/syscall-tracing-plan.md were already consistent and correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in lseek classification (offset, not byte count)Paul Buetow
Audit of lseek(2) confirmed the tracing implementation is already correct: enter is a KindFd fd_event capturing the fd from args[0], the syscall is FamilyFS alongside its read/write/fsync siblings, and the exit is a plain ret_event that stays UNCLASSIFIED. lseek returns the RESULTING file offset (off_t, bytes from the start of the file), which is a file position, NOT a count of bytes transferred — so it must never be READ/WRITE/TRANSFER classified, which would wrongly inflate I/O byte totals. Add lock-in tests pinning that behaviour so a future reclassification trips: - FormatLseek/FormatExitLseek tracepoint fixtures. - TestClassifyFdLseek: enter resolves to KindFd (fd at args[0]). - TestClassifyRetExitLseek: exit is KindRet and ClassifyRet stays UNCLASSIFIED. - lseek entry in TestClassifySyscallPairAccepted (end-to-end pair). - FS-family asserts for sys_enter/exit_lseek in family_test. - Enriched UNCLASSIFIED comment in retclassify_test explaining offset != bytes. No generated-artifact changes (mage generate produces no diff); no in-scope bugs and no out-of-scope follow-ups found. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30kexec_load: classify into Security family with its siblingPaul Buetow
kexec_load(2) and kexec_file_load are documented together on the same man page and both load a new kernel for later execution by reboot(2). kexec_file_load was already FamilySecurity, but kexec_load fell through to FamilyMisc. Move kexec_load to FamilySecurity so the siblings share a family. Kind classification was already correct: kexec_load takes raw user pointers (KindNull, no fd/path) while kexec_file_load takes fds (KindFd); the return value (long 0/-1, no byte count) stays UNCLASSIFIED. Update docs/syscall-tracing-plan.md to match, regenerate artifacts, and add lock-in tests for the family and UNCLASSIFIED return of both kexec syscalls plus reboot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in io_uring_register classification auditPaul Buetow
Audit of io_uring_register(2) confirmed the existing tracing is correct: KindFd with the io_uring fd captured at args[0], FamilyAIO (matching io_uring_setup/io_uring_enter), and an UNCLASSIFIED ret_event exit. The sys_enter_io_ KindNull prefix rule does NOT mis-catch it because classifyNameOnly consults the exact nameOnlyKindsTable (KindFd) before the prefix list. Add two lock-in tests to guard these invariants: - TestIoUringRegisterTablePrecedenceOverIoPrefix: the explicit KindFd table entry wins over the sys_enter_io_ KindNull prefix rule (with an io_submit sanity check that the prefix rule still yields KindNull for fd-less AIO siblings). - TestIoUringRegisterReturnUnclassified: the exit returns 0/small-positive, never a byte count, so the io_uring group stays out of retClassifications. No code, docs, or generated artifacts changed; mage generate produces no diff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(getsockname): lock in KindFd/FamilyNetwork/UNCLASSIFIED classificationPaul Buetow
Audit of getsockname(2) confirmed correct tracing: enter is KindFd with the sockfd captured from args[0], family is FamilyNetwork, and the exit ret_event is UNCLASSIFIED (0/-1, no byte count) — matching the man page and its bind/connect/listen/accept/getpeername siblings. Integration coverage already exists (ioworkload calls Getsockname; TestSocketIntro- spection asserts enter_getsockname). Add lock-in tests symmetric with the existing getpeername coverage: - TestClassifyExitGetsockname: exit tracepoint maps to KindRet. - TestGenerateGetsocknameHandler: enter captures fd=args[0]; the addr output pointer (args[1]) and addrlen in/out pointer (args[2]) are not captured, and the exit stays UNCLASSIFIED. - FormatGetsockname/FormatExitGetsockname fixtures copied verbatim from the real kernel tracepoint format (third arg is a pointer, unlike bind's by-value addrlen). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(getcwd): lock in KindNull enter + exit-time cwd resolutionPaul Buetow
Audit of the getcwd(2) tracing path. getcwd's args[0] is a char *buf OUTPUT buffer: the kernel writes the absolute cwd path into it and the contents are only valid AFTER the syscall returns. Reading it at enter would capture an empty/garbage string, so getcwd is correctly KindNull at enter and the cwd is resolved at EXIT from /proc/<tid>/cwd when the return value is positive (handleNullExit). Family FS, docs and drift tests already aligned; no behavior change required. Add lock-in tests pinning the correct behavior: - generate: strengthen TestClassifyNullGetcwd to assert the enter kind is never KindPathname/KindName and no pathname field is captured; add TestClassifyByFieldGetcwdBufNotPath proving the generic field classifier never treats char *buf as a path (defense-in-depth). - eventloop: add GetcwdFailureEventTest asserting that a failed getcwd (negative errno, e.g. -ERANGE) attaches no cwd path, and document the output-buffer nuance in the success-case test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(kill): lock in KindNull enter and UNCLASSIFIED retPaul Buetow
Audit of kill(2) (pid_t pid, int sig): the pid at args[0] is a process/ process-group identifier and sig a signal number, neither an fd nor a path, so the enter tracepoint is KindNull and the int 0/-1 return is a status code (UNCLASSIFIED), not a transferred byte count. Classification and docs (Signals/null) already matched and need no change. Add TestClassifyExitKillUnclassifiedRet (the return-value lock-in its signal siblings tkill/tgkill/rt_sigqueueinfo already have) and harden TestClassifyKillExplicitNull to assert no PathnameField is captured, documenting the deliberate contrast with pidfd_send_signal (KindFd/IPC, args[0] is a real pidfd). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(iopl): lock in KindNull enter and UNCLASSIFIED ret classificationPaul Buetow
Audit of iopl(2) (task wu). iopl(int level) changes the x86 I/O privilege level of the calling thread and returns int 0/-1. The existing coverage only asserted KindNull via a synthetic arg0 field (TestClassifyE7NullNameOnlyKinds) and the FamilyMisc family tag (from the prior ioperm audit, task uu). Add dedicated lock-in tests that use the real 'int level' tracepoint field to prove it is never captured as an fd or path, and that the sys_exit_iopl ret stays KindRet/UNCLASSIFIED (a status code, not a transferred byte count). No implementation, generated-artifact, or docs changes were needed - everything already matched the man page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(gettid): lock in KindNull/FamilyProcess/UNCLASSIFIED classificationPaul Buetow
Audit of gettid(2) ('pid_t gettid(void)', no args, always succeeds) found the classification correct and consistent with its no-arg id-returning siblings getpid/getppid/getuid/getgid (FamilyProcess, KindNull enter, ret_event UNCLASSIFIED exit), and mage generate produces no diff. However gettid lacked dedicated lock-in coverage and was missing entirely from the family_test.go Process table despite its siblings being asserted there. Add TestClassifyGettidNullEnter and TestClassifyExitGettidUnclassifiedRet (mirroring the getgid pattern: enter null_event capturing nothing, exit ret classified UNCLASSIFIED so the returned tid is never mistaken for a byte count) plus gettid enter+exit FamilyProcess assertions in family_test.go. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in getgid null enter and UNCLASSIFIED retPaul Buetow
Audit of getgid(2) found its classification already correct: family Process, enter KindNull (gid_t getgid(void) takes no arguments), exit KindRet with UNCLASSIFIED ret_type (returns a gid, never a byte count, and always succeeds). Family, kind, generated C handler, and docs all matched its no-arg id-returning siblings getuid/geteuid/getegid/getpid/ gettid/getppid, so no implementation or doc changes were needed. Add two dedicated lock-in tests using the real tracepoint fields, mirroring the setuid/setpgid audit pattern, so a stray reclassification of getgid trips a test: - TestClassifyGetgidNullEnter: enter is KindNull, no path/fd capture. - TestClassifyExitGetgidUnclassifiedRet: exit is KindRet, UNCLASSIFIED. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(finit_module): lock in family and ret classificationPaul Buetow
The finit_module audit (task 8t) confirmed the tracing implementation matches man 2 finit_module: KindFd with fd at args[0], param_values string never captured as a path, exit UNCLASSIFIED, and FamilySecurity alongside init_module/delete_module. No implementation discrepancies were found. Extend TestClassifyInitModuleVsFinitModule to also assert the previously-untested dimensions so the classification stays pinned: - finit_module captures no path (empty PathnameField), like init_module - both module-loading syscalls are FamilySecurity - both exits are UNCLASSIFIED (0/-1 return, no byte count) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in bind syscall classificationPaul Buetow
Audit of bind(2): int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen). Verified the existing classification is correct and consistent with its socket-setup siblings connect/listen/accept/ getsockname/getpeername: - KindFd, capturing ev->fd = args[0] (the sockfd); the addr pointer and addrlen are not captured. - FamilyNetwork. - Exit is UNCLASSIFIED (returns 0/-1, no transferred byte count). No implementation or doc changes were needed (docs/syscall-tracing-plan.md already lists bind under Network and fd; drift test green). Added regression coverage: - FormatBind/FormatExitBind fixtures mirroring the real kernel tracepoint. - TestGenerateBindHandler with negative guards (no probe_read on the sockaddr, no fd capture from args[1]/args[2], exit stays UNCLASSIFIED). - bind + connect/listen/getsockname/getpeername added to the family (FamilyNetwork) and ret-classification (UNCLASSIFIED) lock-in lists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(signalfd4): lock in args[3] flags index for eventfd handlerPaul Buetow
Audit of signalfd4(2) confirmed the tracing is correct: classified as KindEventfd in FamilyIPC like its fd-creating siblings (eventfd2, timerfd_create, inotify_init1, signalfd), flags captured from args[3] per signalfd4(ufd, mask, sizemask, flags), and the return value left Unclassified (it is an fd, not a byte count). Add testdata fixtures FormatSignalfd4/FormatExitSignalfd4 (real Linux 7.0 tracepoint data) and a codegen lock-in test asserting the generated handler reads flags from args[3], with negative guards against args[0] (ufd), args[1] (mask pointer) and args[2] (sizemask). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(select): lock in nfds/timeval capture and UNCLASSIFIED exitPaul Buetow
Audit of syscall select confirmed the tracing is already correct: select is KindPoll/FamilyPolling like poll/ppoll/pselect6, the enter handler captures nfds from args[0] as a count (not as an fd) and the timeout from the args[4] timeval, and the exit is an UNCLASSIFIED ret_event (ready-fd count, not a byte transfer). Add TestGenerateSelectHandlerCapturesNfdsAndTimevalTimeout mirroring the ppoll lock-in test, with negative assertions that no argument is ever captured as an fd and that the exit carries no bytes/fd fields. This guards against regressing nfds (a count) into a KindFd fd capture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30sendfile64: capture out_fd instead of dropping both fdsPaul Buetow
sendfile64(out_fd, in_fd, offset, count) transfers bytes between two file descriptors in the kernel and returns the number of bytes written to out_fd. Its tracepoint fields carry no field literally named "fd", so it fell through to KindNull and captured no descriptor at all - inconsistent with its sibling copy_file_range (KindFd) and the read/write/sendto/recvfrom families. Add an explicit sys_enter_sendfile64 -> KindFd override that captures out_fd (args[0], the destination the bytes are written to), matching the single-fd KindFd convention. The return value stays TransferClassified, consistent with copy_file_range/splice/tee/vmsplice. Family stays Network (sendfile is historically socket-oriented; copy_file_range=FS is pure file-to-file). Update docs/syscall-tracing-plan.md (move sendfile64 from null to fd kind), regenerate C/Go artifacts, fix the phase-A classify assertion, and add TestClassifySendfile64CapturesOutFd as a lock-in + negative test. The existing TestRetbytesPhaseA integration test still passes with the runtime change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in sched_setparam pid-not-fd classificationPaul Buetow
Add TestClassifySchedSetparamPidNotFd as a dedicated regression test for the sched_setparam(2) audit. The syscall takes a pid_t (args[0], NOT an fd; 0 = calling thread) and a userspace const struct sched_param *, so the enter must classify as KindNull and the exit as KindRet/UNCLASSIFIED (returns 0/-1, no byte transfer), matching family Sched. Implementation, docs, and generated C/Go artifacts already matched the man page; sched_setparam was previously only asserted as a sibling check inside the sched_getparam test. This pins its full behavior directly, consistent with prior sched_getparam/sched_getattr audits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>