summaryrefslogtreecommitdiff
path: root/internal
AgeCommit message (Collapse)Author
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>
2026-05-30test(sched_getparam): lock in KindNull/FamilySched/UNCLASSIFIED classificationPaul Buetow
Audit of sched_getparam(2): int sched_getparam(pid_t pid, struct sched_param *param). args[0] is a PID (not an fd) and param is a userspace output pointer, so the enter tracepoint classifies as KindNull and the family is Sched; the exit returns int 0/-1 (a status code, not a byte count) and stays KindRet/UNCLASSIFIED. Classification in classify.go, family.go, and docs/syscall-tracing-plan.md already match the man page and the sched_* siblings; mage generate produces no diff. This adds a dedicated lock-in regression test mirroring the prior sched_getattr audit, pinning the pid-not-fd invariant, the family, the exit classification, and sibling consistency with sched_setparam. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in mkdirat path capture at args[1]Paul Buetow
Audit of mkdirat(2) found the tracing implementation correct: the generated BPF handler reads the pathname from args[1] (after the dirfd at args[0]), while the sibling mkdir(2) reads from args[0] (no dirfd). Both are KindPathname / FamilyFS with an UNCLASSIFIED return, consistent with mknod/mknodat and docs/syscall-tracing-plan.md. The arg index is data-driven from the kernel format via FieldNumber, so no source change was needed. Add lock-in unit tests and real-format fixtures asserting: - mkdirat captures the path from args[1], NOT args[0] (negative guard) - mkdir captures the path from args[0] - mkdirat/mkdir/mknodat share FamilyFS and KindPathname - FieldNumber(pathname) = 1 for mkdirat, 0 for mkdir Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30creat: keep pathname on failed creat; lock in fd->path mappingPaul Buetow
creat(pathname, mode) is equivalent to open(pathname, O_CREAT|O_WRONLY|O_TRUNC, mode): on success it returns a new fd, on failure -1. handlePathExit already special-cased creat to register the returned fd->path mapping in fdState (matching handleOpenExit for open/openat/openat2), but on failure (ret<0) it left ep.File unset, silently dropping the path. handleOpenExit keeps the path via NewPathname for failed opens so error scenarios stay observable; align the creat branch with that behavior. Strengthen CreatEventTest to assert the returned fd is registered with the correct path and synthesized open flags, and add a negative FailedCreatEventTest covering the ret<0 path (no fd registered, path retained). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30fix(sleep): record sentinel for TIMER_ABSTIME clock_nanosleep (a20)Paul Buetow
clock_nanosleep with the TIMER_ABSTIME flag passes an ABSOLUTE wakeup time in the request timespec, not a relative duration. The generated BPF sleep handler computed requested_ns = tv_sec*1e9 + tv_nsec unconditionally, so absolute sleeps exported a bogus multi-decade "sleep duration" in CSV/parquet/stream. generateExtraSleep now carries an optional flags-argument expression per sleep syscall. For clock_nanosleep the generated handler checks args[1] & TIMER_ABSTIME (value 1) and only computes the relative duration when the flag is clear; absolute sleeps keep the existing -1 sentinel (same value used for null/unreadable timespec pointers). nanosleep is always relative and stays unconditional (no flags arg). - Regenerated internal/c/generated_tracepoints.c (mage generate idempotent). - Added codegen tests asserting the TIMER_ABSTIME guard for clock_nanosleep and its absence for nanosleep. - Extended the ioworkload sleep scenario to issue an absolute clock_nanosleep and the sleep parquet integration test to assert it is reported as -1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(clock_nanosleep): lock in exit handler UNCLASSIFIED ret + args[2] ↵Paul Buetow
request ptr Audit of clock_nanosleep tracing confirmed the classification is correct: KindSleep + FamilyTime (matching the nanosleep sibling), the enter handler captures the request timespec at ctx->args[2] (not args[0]/clockid), and the exit handler emits a plain ret_event with ret_type UNCLASSIFIED, which is correct since clock_nanosleep returns 0 or a positive errno, never an fd or byte count. Strengthen the existing enter-handler test with the requested_ns -1 sentinel and add TestClockNanosleepExitHandlerIsUnclassifiedRet to pin the exit-side behavior. No classification, docs, or generated-code changes were needed (mage generate produces no diff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30Document Process-vs-Misc boundary; keep set_tid_address in Process (task 920)Paul Buetow
Resolve the family-split question for set_tid_address vs its per-thread registration siblings rseq/set_robust_list/get_robust_list (Misc). DECISION: keep set_tid_address in FamilyProcess. The 520 registration-vs- operation rule governs the IPC-vs-Misc boundary (does the syscall perform the futex/sync op?), not Process-vs-Misc. set_tid_address registers clear_child_tid, the kernel's primary thread-EXIT notification mechanism (zeroed + FUTEX_WAKEd at teardown), set by the C runtime for essentially every thread via clone(2) CLONE_CHILD_CLEARTID, and returns the caller's tid like gettid/getpid -- mandatory thread-lifecycle plumbing belonging with clone/fork/exit/gettid. rseq (scheduling optimization) and robust_list (opt-in futex cleanup) are OPTIONAL per-thread features and stay Misc. - family.go: add Process-vs-Misc boundary-rule block (mandatory-thread- lifecycle vs optional-opt-in-feature axis) next to set_tid_address. - family_test.go: lock in set_tid_address enter+exit as Process with a WHY comment contrasting it against the rseq/robust_list Misc cluster. No generated-artifact or docs drift: classification unchanged (Process list in docs/syscall-tracing-plan.md already correct); mage generate is idempotent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in set_tid_address ret as UNCLASSIFIEDPaul Buetow
set_tid_address(2) always returns the caller's thread ID and never fails (no -1, no byte count). Assert its exit stays UNCLASSIFIED in TestClassifyRetUnclassified alongside its pid/tid-returning Process siblings setsid/getsid/getpid/getppid, so a stray byte-count reclassification trips the test. Audit of yz confirmed the existing classification is correct: KindNull (single userspace tidptr, no fd/path) and FamilyProcess. The KindNull case is already covered by TestClassify97NameOnlyKinds; this adds the previously-missing return-value assertion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in tkill classification (Signals/null/UNCLASSIFIED)Paul Buetow
Audit of tkill(2) (task 310) confirmed correct tracing: tkill(tid, sig) is FamilySignals, kind=null, ret UNCLASSIFIED, matching its siblings kill/tgkill/rt_sig*. tkill/tgkill are intentionally absent from the name-only kind table; ClassifyFormat returns KindNone for them (the pid_t tid is not matched by the fd rule, so the thread id is never misread as a file descriptor) and classifyEnterForGeneration promotes that to KindNull at generation time. This was untested, so add lock-in coverage closing the gap: - TestGenerateTkillHandler: enter emits null_event, captures no arg (tid is not an fd), exit reports raw status as UNCLASSIFIED. - TestClassifyTkillFallsThroughToNull: pins ClassifyFormat=KindNone and the KindNull generation fallback, so a future fd-rule regression fails. - TestClassifyRetTkillUnclassified: 0/-1 status is not a byte count. - Extend TestClassifySyscallFamily with kill/tkill/tgkill (enter+exit) so a stray reclassification out of FamilySignals trips the test. No generated output or runtime behavior changed (mage generate clean). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(setpgid): lock in KindNull/Process/UNCLASSIFIED audit findingsPaul Buetow
Audit of setpgid(pid_t pid, pid_t pgid): both args are process/process- group identifiers (kernel tracepoint type pid_t), never an fd or path; the call returns int 0/-1. Verified it is correctly classified KindNull (null_event), FamilyProcess, and UNCLASSIFIED ret, and that the Process and null lists in docs/syscall-tracing-plan.md stay in sync. No classification change was needed. Add lock-in tests so a future stray reclassification trips immediately: - TestClassifySetpgidNullEnter feeds the REAL tracepoint fields (pid_t pid, pid_t pgid) and asserts KindNull, proving args[0] (pid) is never mistaken for an fd: isFdType matches only int/unsigned int/unsigned long (not pid_t) and the fd heuristic also requires field name fd. - TestClassifyExitSetpgidUnclassifiedRet asserts the exit is KindRet and ClassifyRet stays UNCLASSIFIED (status code, not a byte count). - Add setpgid to the retclassify UNCLASSIFIED cluster beside setsid/getsid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30family: document Misc-vs-IPC boundary rule; keep robust_list in Misc (task 520)Paul Buetow
Resolve the get_robust_list/set_robust_list classification decision: keep both as FamilyMisc rather than promoting them to FamilyIPC alongside the recently-moved futex_* syscalls. Rule (now documented in family.go next to the futex IPC block): a syscall is IPC only if it PERFORMS the actual IPC/sync operation (futex wait/wake/requeue on the futex word, or an op on an IPC object). Per-thread registration/bookkeeping that merely hands the kernel a pointer it consults later -- rseq and get_robust_list/set_robust_list -- stays Misc. man 2 get_robust_list confirms the robust futex list is 'managed in user space: the kernel knows only about the location of the head'; these syscalls register/query that per-thread head pointer and never wait/wake or touch shared memory, structurally identical to rseq. The split axis is operation-vs-registration, not name similarity. No classification change, so mage generate is a no-op (generated artifacts and docs/syscall-tracing-plan.md unchanged). Strengthened the rseq/ robust_list comments in family_test.go and the TestClassifyGetRobustListPidNotFd lock-in comment in classify_test.go to cite the rule and mark the decision resolved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30fix(z10): skip enter-state write for noreturn syscallsPaul Buetow
After p10 suppressed the sys_exit_exit/sys_exit_exit_group handlers, the enter handlers for exit/exit_group still called ior_on_syscall_enter, which writes a per-tid entry into syscall_enter_state_map. With the exit handler gone, nothing ever bpf_map_delete_elem'd that entry, so stale per-tid state accumulated in the bounded (32768) map on hosts churning many distinct tids and could starve legitimate inserts. Add ior_on_noreturn_syscall_enter in internal/c/filter.c: it only makes the sampling decision (ior_should_emit_trace) and deliberately does NOT record enter-state. The code generator now emits this hook for noreturn enter handlers (detected via isNoreturnSyscall(syscallName(name))) so the enter null_event is still emitted while the dead, unreclaimable map write is skipped. Regenerated generated_tracepoints.c accordingly. Extend TestGenerateExitNoreturnHandlers with a negative assertion (no ior_on_syscall_enter for noreturn) and add TestGenerateReturningSyscallEnterRecordsState as a positive contrast. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30fix(family): classify seteuid/setegid as Process for consistencyPaul Buetow
classify.go has KindNull entries for sys_enter_seteuid/sys_enter_setegid, but family.go's FamilyProcess credential cluster omitted them, so ClassifySyscallFamily returned Misc — inconsistent with their siblings setuid/setgid/setresuid/setreuid/setfsuid. Add seteuid/setegid to the FamilyProcess group with a comment noting they are latent: current kernels expose no dedicated seteuid/setegid tracepoints (glibc wraps them over setreuid/setresuid), so they never reach the generated tracepoint map or docs/syscall-tracing-plan.md. mage generate confirmed idempotent with no diff, so docs/drift test are untouched. Lock-in unit tests assert Process for seteuid/setegid (enter+exit) by name directly, since no tracepoint exists. Refs task 620. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30fix(family): reclassify get_mempolicy as Memory not SecurityPaul Buetow
get_mempolicy(2) retrieves the NUMA memory policy for a thread or address range and is logically a Memory-family syscall, sharing FamilyMemory with its NUMA siblings set_mempolicy, set_mempolicy_home_node, mbind, migrate_pages, and move_pages. It was misclassified FamilySecurity (a copy/paste/alphabetical-ordering slip alongside the security modules). Move it to the Memory group in internal/generate/family.go, update the Memory and Security lists in docs/syscall-tracing-plan.md (keeps the docs_drift_test green), and regenerate artifacts (traceId2Family 735/734 and syscallFamilies[get_mempolicy] flip Security->Memory; C unchanged, mage generate idempotent). Add enter+exit family lock-in assertions for get_mempolicy and set_mempolicy alongside the NUMA siblings so the whole cluster is pinned. Task 120. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30test(generate): lock in times(2) syscall classificationPaul Buetow
Audit of times(2) (clock_t times(struct tms *buf)) confirmed its classification is correct and consistent with its time/clock siblings: - sys_enter_times: KindNull (single userspace output struct tms *buf; no fd or pathname argument). - sys_exit_times: ret_event UNCLASSIFIED — times() returns a clock_t tick count (or (clock_t)-1 on error), which is a tick tally, not a transferred byte count. - family: FamilyTime, alongside gettimeofday/clock_gettime, and NOT FamilyProcess where getrusage lives. No misclassification was found; docs/syscall-tracing-plan.md already lists times under the Time family and the null kind. Add lock-in tests so any stray reclassification trips a unit test: - family_test.go: assert sys_enter/exit_times == FamilyTime. - retclassify_test.go: assert sys_exit_times stays UNCLASSIFIED. KindNull is already covered by TestClassifyM7NameOnlyKinds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>