summaryrefslogtreecommitdiff
path: root/cmd
AgeCommit message (Collapse)Author
6 daystest(memory): add coverage for mlock/mlock2/munlock/mlockall/munlockallPaul Buetow
The memory-locking cluster (FamilyMemory) had no end-to-end integration coverage; mmap_test.go only exercised mmap/msync/mremap/munmap. These syscalls return UNCLASSIFIED, so enter-tracepoint presence is the right end-to-end check (mlock/mlock2/munlock are KindMem, mlockall/munlockall are KindNull). Add a mmap-memory-lock scenario that, on a single anonymous page (small enough to stay under RLIMIT_MEMLOCK), issues raw syscalls for mlock, munlock, mlock2(addr,len,0), mlockall(MCL_CURRENT) and munlockall so the exact sys_enter_ tracepoints fire. mlock/mlock2/mlockall tolerate EPERM/ENOMEM best-effort since the enter tracepoint fires before the error; munlock/munlockall always succeed and clean up. TestMmapMemoryLock asserts enter_mlock, enter_munlock, enter_mlock2, enter_mlockall and enter_munlockall each fire (MinCount>=1, Comm ioworkload). All five captured under sudo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6 daystest: add mount_setattr coverage and assert vmsplice TRANSFER byte countPaul Buetow
mount_setattr(2) was the only new-mount-API sibling not exercised end-to-end (unlike move_mount/fsmount/fspick/open_tree), and vmsplice(2) had zero end-to-end assertion despite being TRANSFER_CLASSIFIED. uj0 (mount_setattr): add a best-effort RawSyscall6(SYS_MOUNT_SETATTR) call to mountfsManagement() aimed at the scenario mount point with AT_FDCWD and a MountAttr requesting MOUNT_ATTR_RDONLY. It needs CAP_SYS_ADMIN (Linux 5.12+) and the path is not a mount, so it returns EPERM/EINVAL, but its sys_enter_ tracepoint fires on kernel entry before any check -- the same best-effort pattern used for the other mount-API calls. Add mount_setattr to mountfsTraceArgs and assert enter_mount_setattr MinCount>=1 in TestMountFsManagementSyscalls. bl0 (vmsplice): add a deterministic retbytesVmsplice driver to the phase-A workload (mirroring the getdents64/readlinkat drivers): a spaced retry loop that gathers a fixed 18-byte user iovec into a fresh pipe via vmsplice and drains it each iteration. vmsplice is TRANSFER_CLASSIFIED, so the exit reports ctx->ret = bytes moved. Add vmsplice to retbytesTraceArgs and assert enter_vmsplice presence plus assertEventBytesAtLeast(payloadLen=18) and a positive duration in TestRetbytesPhaseA, locking in the TRANSFER byte attribution like its splice/tee siblings. Coverage hardening only; classification/tracing verified correct by inspection (mount_setattr=FamilyFS/KindPathname/UNCLASSIFIED, vmsplice=FamilyNetwork/KindFd/TRANSFER_CLASSIFIED). Verified: TEST_NAME=TestMountFsManagementSyscalls mage testWithName PASS (enter_mount_setattr captured); TEST_NAME=TestRetbytesPhaseA mage testWithName PASS (vmsplice bytes>=18 asserted). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6 daystest: add deterministic ioctl coverage and quotactl_fd in mountfsPaul Buetow
ioctl (FamilyFS, KindFd@arg0) previously only fired implicitly via the Go runtime/terminal. Add scenario_ioctl.go issuing a benign FIONREAD ioctl on an opened temp file (registered as ioctl-basic) and ioctl_test.go asserting enter_ioctl resolves to the temp file path, mirroring the fcntl suite. quotactl_fd (FamilyFS, KindFd@arg0) had no coverage while its sibling quotactl was tested in mountfs. Add a best-effort RawSyscall6 SYS_QUOTACTL_FD call on an fd opened on the mount point in scenario_mountfs.go, extend mountfsTraceArgs, and assert enter_quotactl_fd (MinCount>=1). The sys_enter tracepoint fires on kernel entry regardless of privilege/quota support. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6 daystest: add coverage for pidfd_send_signal and fadvise64Paul Buetow
pidfd_send_signal (FamilyIPC, KindFd@arg0) and fadvise64 (KindFd, UNCLASSIFIED fd-based hint) previously had no end-to-end integration coverage despite correct classification/tracing. pidfd_send_signal: add a pidfd-send-signal ioworkload scenario that opens a pidfd for the current process and issues a sig-0 liveness probe (delivers nothing, safe to target self) via syscall.Syscall6 with the per-arch nr 424. TestPidfdSendSignal asserts enter_pidfd_send_signal is captured; pidfd_send_signal added to the pidfd -trace-syscalls list. fadvise64: add readwrite-fadvise64 and readwrite-fadvise64-ebadf scenarios using unix.Fadvise(fd, 0, 0, FADV_NORMAL), mirroring the readahead tests. TestReadwriteFadvise64 asserts enter_fadvise64 with Bytes==0 (UNCLASSIFIED: offset/len are hints, not bytes transferred) and positive duration; the ebadf variant asserts enter capture with Bytes==0 on the failing call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest: add coverage for mknodat and ioprio_get/ioprio_setPaul Buetow
Add end-to-end integration scenarios and tests for two previously untested syscalls: - mknodat(2): new dir-mknodat-fifo scenario creates an unprivileged FIFO node (S_IFIFO, no CAP_MKNOD) via unix.Mknodat under AT_FDCWD and unlinks it. TestDirMknodatFifo asserts enter_mknodat fires with pathname@args[1] (after dirfd@args[0]), proven by a PathContains match on the distinct fifo name, mirroring the mkdirat coverage. - ioprio_get(2)/ioprio_set(2): new ioprio-basic scenario (the I/O-priority analogues of getpriority/setpriority) issues the raw syscalls (no x/sys wrapper exists), reading the current self I/O priority and re-applying it, or a harmless unprivileged best-effort value when none is set. TestIoprioBasic asserts enter_ioprio_get and enter_ioprio_set fire (null enters, UNCLASSIFIED ret), mirroring priority-basic. Realtime class is deliberately avoided as it needs CAP_SYS_ADMIN. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest: add coverage for setitimer (signal-safe) and statfs/fstatfsPaul Buetow
setitimer/getitimer (di0): no scenario previously exercised the classic interval-timer family. Add intervalTimerNoop, which calls setitimer(ITIMER_REAL, &{0,0,0,0}, NULL) with an all-zero itimerval so the timer is disarmed and NO SIGALRM is ever scheduled (mirrors miscAlarmCancel's alarm(0) and posixTimerLifecycle's never-firing pattern), followed by a safe getitimer read. Both are KindNull on enter / UNCLASSIFIED on exit, so TestIntervalTimerNoop asserts enter_setitimer and enter_getitimer presence. statfs/fstatfs (7j0): stat_test.go covered stat/fstat/lstat/newfstatat/statx but not the statfs family. Add statStatfs, which calls syscall.Statfs(path) (enter_statfs path_event captures the pathname) and syscall.Fstatfs(fd) (enter_fstatfs fd_event). TestStatStatfs asserts enter_statfs PathContains the filename and enter_fstatfs presence. Covers audits it (fstatfs) and e00 (statfs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest: add end-to-end coverage for getrandom (READ byte count) and flock (KindFd)Paul Buetow
Two previously-untested syscalls now have integration coverage: - getrandom (Security family, READ_CLASSIFIED): new security-getrandom scenario fills a 32-byte buffer via unix.Getrandom, looping past any signal-interrupted short reads so the cumulative byte count is strictly positive. TestSecurityGetrandom asserts enter_getrandom MinCount>=1, bytes>=1 (locking in the READ byte-count classification end-to-end), and a positive duration. - flock (FamilyFS, KindFd@args[0], UNCLASSIFIED): new flock-basic scenario opens a temp file, takes LOCK_EX then LOCK_UN via syscall.Flock, and closes it. TestFlockBasic asserts enter_flock with PathContains the temp filename, confirming the fd resolves to the file path via the procfd cache. Both scenarios use raw unix/syscall calls so the exact sys_enter tracepoints fire, and are registered in cmd/ioworkload/scenarios.go. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(aio): add io_getevents and io_cancel enter coveragePaul Buetow
Close the integration-test gaps for two classic-AIO syscalls that the existing scenario never exercised. The AIO workload only drove io_setup/io_submit/io_destroy, so io_getevents (nr 208) and io_cancel (nr 210) had no end-to-end coverage despite their tracer classification (FamilyAIO, KindNull enter, UNCLASSIFIED ret) being correct by inspection. cmd/ioworkload/scenario_aio.go: - Factor the submit scaffolding (temp dir, target file, AIO context) into withAioTarget so each scenario stays short. - ioSubmitWrite now returns the submitted iocb pointer (io_cancel needs it). - aio-getevents: submit, then reap the completion with a blocking io_getevents (min_nr=1, NULL timeout); asserts the return is a count. - aio-cancel: submit, then best-effort io_cancel (return ignored: it races the I/O completion and often yields -EINVAL/-EAGAIN, but enter still fires), then drain the ring non-blockingly (min_nr=0) so io_destroy has nothing in flight and we never hang when the cancel left no completion. integrationtests/aio_test.go: - TestAioGetevents asserts enter_io_getevents (MinCount 1), mirroring TestAioSubmit, with io_getevents added to the trace-arg set. - TestAioCancel asserts ONLY enter_io_cancel (MinCount 1) — never success — because io_cancel's return is non-deterministic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(landlock): cover landlock_add_rule end-to-end (nj0)Paul Buetow
Extend the existing security-landlock scenario to also exercise landlock_add_rule in-process. After creating the ruleset fd, the scenario builds a struct landlock_path_beneath_attr (allowed_access = LANDLOCK_ACCESS_FS_READ_FILE, parent_fd = open("/", O_PATH)) and calls landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &attr, 0) (syscall nr 445), then closes both fds. landlock_add_rule is unprivileged and has no process-wide side effects (it only builds a ruleset that is never enforced), so it is safe to run in the shared workload process. The call is issued unconditionally even when ruleset creation fails: sys_enter_landlock_add_rule fires before the kernel validates the fd, so the enter tracepoint is captured regardless of whether Landlock is enabled, matching create_ruleset coverage. ret is UNCLASSIFIED (0/-1, not a byte count). TestSecurityLandlockCreateRuleset now adds landlock_add_rule to the trace-arg set and asserts enter_landlock_add_rule MinCount>=1 plus a positive event duration, capturing ruleset_fd at args[0] (KindFd). landlock_restrict_self (ci0) is intentionally NOT covered here: it would require a traced child subprocess, which the integration harness cannot support (it filters by the single workload PID at the BPF layer). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(utime): add end-to-end coverage for futimesat and utimensatPaul Buetow
utime_test.go previously covered only utime/utimes/ENOENT. Add scenarios and tests for the dirfd-relative siblings futimesat(2) and utimensat(2), which take a dirfd at args[0] and the pathname at args[1]. Both scenarios use raw syscalls with AT_FDCWD as the dirfd so the exact enter_futimesat and enter_utimensat tracepoints fire, and the tests assert PathContains the filename, proving ior captures the path from args[1] (after the dirfd). Classification/tracing were already verified by audits qt/f10; this is pure coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(xattr): cover get/list/lset/setxattrat/removexattr + fd-based xattr ↵Paul Buetow
variants Extend the xattr scenario + integration test to exercise end-to-end the xattr syscall variants that previously had no coverage (only setxattr and the -at get/list/remove variants were tested). Classifications were already verified correct by inspection; this is pure coverage hardening. READ-classified variants (assert enter path/fd + bytes>=1): - getxattr(path,name,...) READ, KindPathname@arg0 (ej0) - lgetxattr(path,name,...) READ, KindPathname@arg0 (oj0) - listxattr(path,...) READ, KindPathname@arg0 (rj0) - llistxattr(path,...) READ, KindPathname@arg0 (rj0) - fgetxattr(fd,...) READ, KindFd@arg0 (8i0) - flistxattr(fd,...) READ, KindFd@arg0 (8i0) UNCLASSIFIED variants (assert enter path/fd + bytes==0): - lsetxattr(path,...) KindPathname@arg0 (cl0) - setxattrat(dirfd,path,...) KindPathname@arg1 (vj0) - removexattr(path,name) KindPathname@arg0 (kj0) - lremovexattr(path,name) KindPathname@arg0 (kj0) - fsetxattr(fd,...) KindFd@arg0 (8i0) - fremovexattr(fd,...) KindFd@arg0 (8i0) The l* GET/LIST/SET/REMOVE variants target a regular file (not a bare symlink) so they fire deterministically: user.* xattrs on a symlink itself are kernel-restricted (EPERM). setxattrat uses the raw syscall (nr 463, Linux 6.13+) since Go does not export it; this kernel (7.0.9) supports it. New scenarios use golang.org/x/sys/unix (raw syscalls, no glibc redirect) so the exact tracepoints fire. New tests scope -trace-syscalls to exactly the variant under test to avoid substring-match cross-contamination. All 13 TestXattr* integration tests PASS under root (mage testWithName). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(priority): add end-to-end coverage for getpriority/setpriorityPaul Buetow
getpriority/setpriority (FamilyProcess, KindNull enter, UNCLASSIFIED ret) were untested end-to-end: no ioworkload scenario invoked them. Add a safe, unprivileged, non-disruptive priority-basic scenario that reads the current nice value via getpriority(PRIO_PROCESS, 0) and re-applies that exact value via setpriority(PRIO_PROCESS, 0, currentNice) — a byte-for-byte no-op, mirroring the existing schedRoundtripAffinity round-trip. Note getpriority returns 20-nice, so the value is converted back before re-applying. Register the scenario in scenarios.go and add TestPriorityBasic, which asserts enter_getpriority and enter_setpriority appear as null_event enters attributed to the ioworkload process. Enter-tracepoint presence is the right check given KindNull/UNCLASSIFIED (no fd/path/bytes to assert). Coverage only — classification verified correct in audits 6u and pz. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(timerfd): assert timerfd_settime/gettime fd capture end-to-endPaul Buetow
The fd-from-air-eventfd-users workload only called timerfd_create then closed the fd, so timerfd_settime/timerfd_gettime were never exercised by any integration test. Commit 6ac9fa4 fixed those two syscalls to KindFd@arg0 (capturing the operating timerfd via fd_event instead of a null_event), but that fix had no end-to-end coverage. Extend the workload to arm the still-open timerfd via timerfd_settime (1s relative expiry, so it never fires) and read it back via timerfd_gettime before closing. Assert in TestFdFromAirEventfdUsers that both enter handlers fire (MinCount>=1) and resolve to the "timerfd:" path prefix, proving arg0 fd is captured rather than null. Locks in the 6ac9fa4 KindFd fix. splice/tee are NOT touched: retbytes_test.go already asserts enter_splice/enter_tee plus positive transfer byte counts, which inherently exercises their arg0 fd capture, so no new coverage is needed there. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(chown): add end-to-end coverage for chown/fchown/lchown/fchownat familyPaul Buetow
chown/lchown/fchownat/fchown previously had no integration coverage: no scenario in cmd/ioworkload/ and no test in integrationtests/. All four are correctly classified (chown/lchown KindPathname args[0]; fchownat KindPathname args[1]; fchown KindFd args[0]; all FamilyFS, all UNCLASSIFIED ret) but nothing exercised them end-to-end. Add a chown-basic scenario that, on a temp file and a symlink the caller owns, issues raw chown(path,-1,-1), lchown(symlink,-1,-1), fchownat(AT_FDCWD,path, -1,-1,0) and fchown(fd,-1,-1). Owner/group -1/-1 means "leave both ids unchanged", which the kernel accepts without CAP_CHOWN, so the scenario is fully UNPRIVILEGED and nothing is actually modified. Raw syscalls are used so each distinct tracepoint fires rather than glibc redirecting chown to fchownat. TestChownBasic asserts enter_chown and enter_fchownat capture the regular file path (chownfile.txt), enter_lchown captures the symlink (chownlink), and enter_fchown fires (KindFd, no path). Mirrors the chmod coverage (scenario_chmod.go / chmod_test.go). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(chmod): add end-to-end coverage for chmod/fchmod/fchmodat familyPaul Buetow
chmod/fchmod/fchmodat/fchmodat2 previously had no integration coverage: no scenario in cmd/ioworkload/ and no test in integrationtests/. All four are correctly classified (chmod KindPathname args[0]; fchmodat/fchmodat2 KindPathname args[1]; fchmod KindFd args[0]; all FamilyFS, all UNCLASSIFIED ret) but nothing exercised them end-to-end. Add a chmod-basic scenario that, on a temp file the caller owns (so all calls are unprivileged), issues raw chmod(path, 0640), fchmodat(AT_FDCWD, path, 0644, 0), fchmod(fd, 0644), and best-effort fchmodat2(AT_FDCWD, path, 0640, 0) (raw syscall 452, ENOSYS tolerated on kernels < 6.6). Raw syscalls are used so each distinct tracepoint fires rather than glibc redirecting chmod to fchmodat. TestChmodBasic asserts enter_chmod and enter_fchmodat capture the file path (chmodfile.txt) and enter_fchmod fires (KindFd, no path). fchmodat2 is not asserted since it is version-gated, though it does fire on current kernels. Mirrors the utime coverage (scenario_utime.go / utime_test.go). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(retbytes): assert readlinkat READ_CLASSIFIED byte count end-to-endPaul Buetow
readlink/readlinkat are READ_CLASSIFIED (exit ctx->ret = link-target byte count), but the integration suite only asserted the enter_readlinkat pathname tracepoint via MinCount in link_test.go. The exit byte classification and positive duration were never validated end-to-end, unlike sibling READ-classified syscalls (read/recvfrom/getxattrat/ getdents64) in retbytes_test.go. Add retbytesReadlinkat to the phase-A workload: it creates a symlink with a known non-empty absolute target, opens the parent O_DIRECTORY, and re-issues SYS_READLINKAT in a short spaced window (mirroring the getdents64 driver) so ior can attach and capture an enter/exit pair under parallel load. Each call re-resolves the same link, so ctx->ret stays equal to the target length and is strictly positive. Add readlinkat (and symlink, used to build the link without mixing tracepoints) to retbytesTraceArgs, assert enter_readlinkat presence (MinCount) plus bytes>=1 via assertEventBytesAtLeast and a positive duration. bytes>=1 (not an exact target length) because the resolved path varies across temp dirs; >=1 is the safest invariant. Coverage hardening only; classify.go readlink/readlinkat=ReadClassified and the BPF arg capture (args[1]=pathname for readlinkat) are correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 daystest(retbytes): assert getdents64 READ_CLASSIFIED byte count end-to-endPaul Buetow
getdents64 was listed in retbytesTraceArgs but the retbytes workload never issued it and TestRetbytesPhaseA never asserted its exit byte count, leaving the READ_CLASSIFIED ctx->ret path for getdents/getdents64 unverified end-to-end (unlike sibling read/recvfrom/getxattrat). Add retbytesGetdents to the phase-A workload: it populates a temp directory with several files, opens it O_DIRECTORY, and re-issues getdents64 in a short window (lseek-rewind each iteration) so ior can attach and capture an enter/exit pair under parallel load. A non-empty directory guarantees ctx->ret > 0. Assert enter_getdents64 presence (MinCount) plus bytes>=1 via assertEventBytesAtLeast and a positive duration, mirroring the existing READ-classified siblings. Bytes>=1 (not an exact payload length) because dirent size varies with filename length and alignment. Coverage hardening only; classify.go getdents/getdents64=ReadClassified is correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 daystest(integration): add landlock_create_ruleset coveragePaul Buetow
Add a Security-family end-to-end scenario + test for landlock_create_ruleset, which was previously untested. The new securityLandlockCreateRuleset scenario (registered as "security-landlock") builds a minimal valid struct landlock_ruleset_attr{handled_access_fs=LANDLOCK_ACCESS_FS_READ_FILE}, calls landlock_create_ruleset(&attr, sizeof(attr), 0) via raw syscall (nr=444 on amd64/arm64), and closes the returned ruleset fd. It tolerates ENOSYS/EOPNOTSUPP (kernel < 5.13 or Landlock LSM disabled) since the sys_enter tracepoint fires before any such error. It deliberately never calls landlock_restrict_self, which would irreversibly sandbox the shared integration-test runner. TestSecurityLandlockCreateRuleset asserts enter_landlock_create_ruleset MinCount>=1 and positive duration unconditionally, plus conditional "landlockfd:" path-prefix assertions on the create/close pair with an open/close path-stability check. Verified: TEST_NAME=TestSecurityLandlockCreateRuleset mage testWithName PASS (kernel 7.0.9); mage build, go build ./cmd/ioworkload/, and go vet ./integrationtests/ all clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 daystest(integration): add cachestat end-to-end coveragePaul Buetow
cachestat(2) had no integration coverage. Add a readwrite-cachestat ioworkload scenario and TestReadwriteCachestat mirroring the readahead precedent: open a temp file, write data to populate the page cache, then issue cachestat via a raw syscall (no glibc/unix wrapper) with a whole-file cachestat_range and zeroed cachestat output buffer, flags=0. ENOSYS on kernels < 6.5 is tolerated for portability. The test asserts enter_cachestat is captured with the fd-resolved file path, that the UNCLASSIFIED return attributes zero bytes, and that the syscall duration is positive. golang.org/x/sys is promoted to a direct dependency. Verified PASS under sudo on kernel 7.0.9. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 daystest(integration): add inotify family tracing coveragePaul Buetow
Add an inotify-basic ioworkload scenario and an end-to-end integration test covering the inotify IPC family, which previously had no integration coverage (only inotify_init1 had a unit-level eventloop test). The scenario issues inotify_init1(IN_CLOEXEC) -> inotify_add_watch on a temp file (IN_CREATE|IN_DELETE|IN_MODIFY) -> inotify_rm_watch -> close. It is non-blocking: it registers and removes the watch without reading events, and cleans up the temp dir on return. TestInotifyBasic asserts enter_inotify_init1, enter_inotify_add_watch, enter_inotify_rm_watch and enter_close each fire at least once, with positive durations and PID/comm hermetic guards. The init1 instance fd resolves to the inotifyfd: path label; add_watch/rm_watch capture the instance fd@arg0 which resolves to the same registered label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 daystest(xattr): add removexattrat end-to-end integration coveragePaul Buetow
removexattrat(2) (Linux 6.13+) was the one xattr *at-variant lacking integration coverage: xattr_test.go exercised getxattrat/listxattrat (READ-classified byte counts) and the path-based setxattr, but never the REMOVE *at variant. Unlike its getxattrat/listxattrat siblings, removexattrat returns a 0/-1 status (not a byte count), so its exit must be UNCLASSIFIED — matching removexattr/lremovexattr/fremovexattr. Add an ioworkload scenario (xattr-removexattrat) that setxattr's a user.* attribute via a real path then removes it via raw removexattrat(AT_FDCWD, path, 0, name), plus TestXattrRemovexattrat asserting the path (args[1], after the dirfd) is captured (never the xattr name at args[3]) and that accounted bytes are exactly zero (guarding against wrongly READ-classifying it like getxattrat/ listxattrat). Distinct from the fd-based fremovexattr gap (task 8i0). Classification verified by inspection: FamilyFS (xattr marker), KindPathname at the pathname field (ctx->args[1]), and absent from the ret-classification table => UNCLASSIFIED. No generator change needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(integration): cover fsconfig/fspick/open_tree in mount-API scenarioPaul Buetow
Extend the mountfs-management scenario with best-effort fsconfig (KindFd), fspick (KindPathname), and open_tree (KindOpen) calls to complete new-mount-API end-to-end coverage. fsconfig reuses the fscontext fd from fsopen (FSCONFIG_SET_STRING + FSCONFIG_CMD_CREATE), fspick targets "/" with FSPICK_NO_AUTOMOUNT, and open_tree clones the scenario mount point with OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC. All returned fds are closed and all errno values are ignored, so ENOSYS/EPERM/EINVAL/EBADF are tolerated; the sys_enter_ tracepoints fire on kernel entry regardless, creating no mounts on the host. Assert enter_fsconfig/enter_fspick/enter_open_tree (MinCount>=1) in TestMountFsManagementSyscalls and add the three syscalls to the trace filter. Gating is unchanged (root-only via the shared harness). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(integration): add SysV msg/sem tracing coveragePaul Buetow
Add sysv-msg-basic and sysv-sem-basic ioworkload scenarios that exercise the SysV message-queue and semaphore families end-to-end via raw syscalls, mirroring the existing sysv-shm-basic scenario. sysv-msg-basic: msgget(IPC_PRIVATE) -> msgsnd -> msgrcv -> msgctl(IPC_RMID), using a struct msgbuf {int64 mtype; [16]byte mtext} and msgsz = body length (excluding mtype). sysv-sem-basic: semget(IPC_PRIVATE, 1) -> semop(+1) -> semop(-1) -> semctl(IPC_RMID), incrementing before decrementing so the operation can never block. Both defer IPC_RMID right after the get so no kernel IPC object leaks even on partial failure. Add TestSysVMsgBasic and TestSysVSemBasic asserting the enter_ events for msgget/msgsnd/msgrcv/msgctl and semget/semop/semctl are traced with MinCount>=1 and positive duration, plus PID/comm hermetic guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(integration): add Sched family tracing coveragePaul Buetow
Add a self-targeted, non-disruptive sched-basic ioworkload scenario and a dedicated TestSchedBasic integration test. The scenario pins to one OS thread (LockOSThread) and exercises only safe Sched syscalls: sched_yield; sched_getaffinity then sched_setaffinity re-applying the identical mask (a no-op); and read-only sched_getscheduler, sched_getparam, sched_getattr, sched_get_priority_max/min, and sched_rr_get_interval. sched_setscheduler, sched_setattr, and sched_setparam are intentionally excluded. The test scopes -trace-syscalls to the sched_* family, guards on PID and comm, and asserts enter_ tracepoints fire (MinCount>=1) for sched_yield, sched_getaffinity, sched_getscheduler, and sched_getparam. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(integration): add Misc family tracing coveragePaul Buetow
Add a misc-basic ioworkload scenario and an end-to-end integration test for the previously-uncovered Misc syscall family. The scenario exercises only the safe, unprivileged, non-blocking, side-effect-free Misc syscalls: getcpu (raw SYS_GETCPU), uname / sys_newuname (unix.Uname), sysinfo (unix.Sysinfo), vmsplice into a self-created and self-drained pipe with a tiny buffer, and alarm(0) to cancel any pending alarm. Code comments document why the remaining Misc syscalls are intentionally excluded (CAP_SYS_ADMIN / global host mutation, CAP_SYS_RAWIO / x86-only, Linux 6.13+ availability, runtime-managed, or not user-callable). misc_test.go asserts enter_getcpu, enter_newuname, and enter_sysinfo are each traced at least once for the ioworkload process, restricting tracing to the issued syscalls and keeping the existing PID/comm hermetic guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(integration): add Signals family tracing coveragePaul Buetow
The SIGNALS syscall family previously had zero end-to-end coverage (signalfd/signalfd4 are IPC-family fd creators, not Signals). Add a self-targeting ioworkload scenario and an integration test that assert the family's tracepoints fire. scenario_signals.go (signals-basic) issues, all self-directed so it mutates no other process: - rt_sigaction : install SIG_IGN disposition for SIGUSR1 - rt_sigprocmask: BLOCK SIGUSR1 before sending, so self-delivery only marks it pending (never runs a handler / kills us) - sigaltstack : set then disable an alternate signal stack - kill/tgkill/tkill/rt_sigqueueinfo: send SIGUSR1 to self four ways - rt_sigpending : query the pending mask - rt_sigtimedwait: reap the pending signal with a SHORT 100ms timeout (hang guard); EAGAIN tolerated Safety: signal is blocked before any send; rt_sigtimedwait uses a 100ms timeout so it cannot hang; the original signal mask and SIGUSR1 disposition are restored and the alt stack disabled on exit. The goroutine is pinned with LockOSThread so gettid() matches the tgkill/tkill target and the per-thread mask applies to the waiting thread. Raw syscalls are issued directly so the tracepoints fire regardless of the Go runtime's own signal handling. pause (noreturn) and rt_sigreturn (handler-return only) are deliberately excluded. signals_test.go asserts enter_ tracepoints with MinCount>=1 for rt_sigaction, rt_sigprocmask, rt_sigpending, sigaltstack, kill, tgkill, and rt_sigtimedwait, plus a positive duration for the rt_sigtimedwait enter/exit pair. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(integration): add SysV shm tracing coveragePaul Buetow
The SysV shared-memory family (shmget/shmat/shmdt/shmctl) had no end-to-end integration coverage. Add an ioworkload `sysv-shm-basic` scenario that, without privileges, runs shmget(IPC_PRIVATE) -> shmat -> write into the mapped segment -> shmdt -> shmctl(IPC_RMID), always issuing IPC_RMID (via defer) so no kernel segment leaks. Add TestSysVShmBasic asserting enter_shmget/enter_shmat/enter_shmdt/ enter_shmctl are each traced with a positive (paired enter/exit) duration. msg/sem coverage is scoped out and tracked as a follow-up task (7i0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01test(integration): add pwritev/pwritev2 retbytes coveragePaul Buetow
Add write-side positional vectored coverage to close the remaining gap in the pwrite64-family byte-accounting validation. The retbytes/readwrite integration suite already exercised pwrite64 (scalar) and the read-side preadv/preadv2, but the WRITE_CLASSIFIED byte attribution for the vectored positional writes pwritev/pwritev2 was only covered by unit tests, not end-to-end. New ioworkload scenarios: - readwrite-pwritev: issues pwritev (syscall.SYS_PWRITEV) writing a known two-iovec payload at offset 0 to a temp file. - readwrite-pwritev2: issues pwritev2 via the explicit syscall number (328 amd64 / 287 arm64, mirroring preadv2SyscallNr) with offset 0 and no flags. New integration tests assert enter_pwritev/enter_pwritev2 fired and that the attributed retbytes equal the exact iovec total, validating WRITE_CLASSIFIED end-to-end. Both pass as root. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31test(readahead): add end-to-end integration coveragePaul Buetow
readahead(2) was traced (KindFd enter fd_event from args[0], UNCLASSIFIED exit ret_event) but had no integration or ioworkload scenario coverage, unlike its sibling sync_file_range. Add readwrite-readahead and readwrite-readahead-ebadf scenarios plus TestReadwriteReadahead / TestReadwriteReadaheadEbadf, asserting enter_readahead capture with path attribution, zero attributed bytes (readahead returns 0/-1, not a byte count, so it is correctly UNCLASSIFIED), and positive end-to-end duration. No classification change: inspection confirms KindFd / UNCLASSIFIED is correct per man 2 readahead; bytesFromRet returns 0 for UNCLASSIFIED so the 0/-1 return is never misattributed as bytes. 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-31test(timer): add POSIX timer family end-to-end integration coveragePaul Buetow
The POSIX per-process timer family (timer_create, timer_settime, timer_gettime, timer_getoverrun, timer_delete) had no end-to-end integration coverage; only the unrelated fd-returning sibling timerfd_create was exercised. Add a posix-timer-lifecycle workload scenario and TestPosixTimerLifecycle to validate these are traced as null_events, and guard against timer_create being misclassified like timerfd_create (timer_create returns a timer_t via an output pointer, not an fd, so its records must carry no 'timerfd:' descriptor path). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31test(retbytes): assert read byte counts for pread64/preadv/preadv2Paul Buetow
The retbytes integration coverage exercised read/write/sendto/etc but the positional read p-variants only had presence assertions (pread64) or no coverage at all (preadv/preadv2), so their READ_CLASSIFIED byte accounting was validated only by unit tests, not end-to-end. Add a positive byte-count assertion to TestReadwritePread and new readwrite-preadv / readwrite-preadv2 workload scenarios plus integration tests that read a known payload and assert the attributed byte count, mirroring the existing pwrite64 assertion. preadv2 lacks a Go syscall.SYS_PREADV2 constant, so its number is provided per-GOARCH (amd64=327, arm64=286) following the securitySyscallNumbers pattern. Addresses the read side of b20. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31test(openat2): add end-to-end integration coverage for openat2Paul Buetow
openat2(2) is the one open-family syscall with a structurally distinct argument layout: flags/mode live inside the open_how struct (args[2]), not as a plain int, and args[3] is the struct size. The tracer correctly reads the path from args[1] and omits flags (ev->flags = -1) rather than misreading the struct ptr/size, and registers the returned fd->path mapping via the shared handleOpenExit path. This was verified by inspection but had no integration scenario, unlike open/openat/creat/ open_by_handle_at. Add an open-openat2 ioworkload scenario issuing the raw openat2 syscall (Go has no wrapper and routes Open through openat) and a TestOpenOpenat2 integration test asserting the enter_openat2 tracepoint captures the path. Verified passing as root. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31test(mountfs): exercise fsopen end-to-end in mount-API scenarioPaul Buetow
The mountfs-management integration scenario covered the new mount API syscalls fsmount/move_mount/mount/umount/pivot_root but not fsopen, the API's entry point and a direct eventfd-kind sibling of fsmount. Add a best-effort fsopen("tmpfs", FSOPEN_CLOEXEC) call (closing the returned context fd on success) and assert enter_fsopen is traced. fsopen's tracing is otherwise correct: args[1] flags captured, args[0] fsname (a filesystem TYPE, not a path) deliberately not treated as a pathname, returned fd registered as the 'fsopenfd:<flags>' descriptor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31test(aio): add io_submit end-to-end integration coveragePaul Buetow
Audit of io_submit tracing (task 0v) confirmed the tracer is correct by inspection: KindNull (sys_enter_io_ prefix rule) so ctx_id/nr/iocbpp are opaque and no fd/path is captured; FamilyAIO; return is UNCLASSIFIED (the return is a count of iocbs submitted, not a byte count, so it must not inflate READ/WRITE/TRANSFER totals). Enter/exit are paired and timed. No implementation discrepancy and no docs drift. Add a genuine end-to-end test: new aio-submit ioworkload scenario sets up an AIO context and submits one real IOCB_CMD_PWRITE iocb against a temp file via raw syscalls, then tears the context down. TestAioSubmit asserts the enter_io_submit tracepoint fires for the AIO family workload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31test(aio): add io_setup end-to-end integration coveragePaul Buetow
The classic Linux AIO family (io_setup/io_submit/io_getevents/io_cancel/ io_destroy) had no integration coverage: family_test.go exercises only FS/Memory/IPC/Network/Process/Sched/Time, and iouring_test.go covers only the distinct io_uring_* family. io_setup is classified KindNull/FamilyAIO, which is correct by inspection against man 2 io_setup (nr_events is a count, ctx_idp an output pointer, so no fd/path is captured), so the tracer itself needed no change. Add an ioworkload AIO scenario that drives io_setup(2)/io_destroy(2) raw (no privileges, no libaio) plus an EINVAL variant, and integration tests that assert ior records the enter_io_setup tracepoint end-to-end, mirroring the existing iouring scenario/test pattern. 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-29utime/utimes: classify as FS family (fix Misc misclassification)Paul Buetow
utime(2) and utimes(2) change a file's access/modification times by a real filesystem path (filename at args[0]). The path was already captured (KindPathname), but both syscalls fell through to FamilyMisc instead of joining their siblings utimensat/futimesat in FamilyFS. Add them to fsSyscalls and regenerate; the only generated change is trace IDs 1034-1037 flipping FamilyMisc -> FamilyFS. Lock-in coverage: - family_test.go asserts utime/utimes/utimensat/futimesat are all FamilyFS. - classify_test.go + FormatUtime fixture assert utime is KindPathname with PathnameField "filename" (path captured even though it is a char* string, unlike domain/host name args). - New ioworkload scenarios utime-basic/utimes/enoent and integration tests TestUtimeBasic/Utimes/Enoent verify the path is captured at runtime, including on the ENOENT error path. Docs updated: moved utime/utimes from Misc to FS in docs/syscall-tracing-plan.md to keep the drift tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-28close_range: honor last bound and CLOSE_RANGE_CLOEXEC flagPaul Buetow
close_range was captured as a single-fd fd_event carrying only first, so the runtime evicted every tracked fd >= first, ignoring the last upper bound and the flags. Bounded calls wrongly dropped still-open higher fds, and CLOSE_RANGE_CLOEXEC (which keeps fds open) was treated as a full close. Reclassify close_range to the two_fd_event kind, mapping fd_a/fd_b/extra to first/last/flags. The runtime now closes only the inclusive [first, last] range (a negative last from ~0U means unbounded) and skips eviction when CLOSE_RANGE_CLOEXEC is set or the syscall fails. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27Stabilize integration test startupPaul Buetow
2026-05-231c guard ioworkload select fd_set against high fd valuesPaul Buetow
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21f7 wire eventfd kind for fd-from-air IPC syscallsPaul Buetow
2026-05-20task-47: add KindExec for execve pathsPaul Buetow
2026-05-20feat: add keyctl ptrace perf_event_open tracing (task 77)Paul Buetow
2026-05-20d7: add POSIX mq syscall kind/classification and coveragePaul Buetow
2026-05-20feat: add mount/fs management syscall tracing for c7Paul Buetow
2026-05-20task 27: add KindSleep and requested sleep metricPaul Buetow
2026-05-20task 07: add KindMem and separate address-space byte accountingPaul Buetow
2026-05-19z6: add KindPoll wiring for poll/select ready countsPaul Buetow