| Age | Commit message (Collapse) | Author |
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
TestCopyFileRangeBasic previously asserted only the enter event and source
path (copyrangesrc.txt) but not the copied byte count on the
TRANSFER_CLASSIFIED exit, unlike sibling sendfile64/splice/tee coverage in
retbytes_test.go. The basic scenario copies exactly the 32-byte payload
"copy_file_range integration data" in a single copy_file_range(2) call, so
the exit byte count is deterministic.
Switch to runScenarioResult and add assertEventBytesEqual(...,32) plus
assertEventDurationPositive to lock in the transfer attribution (FamilyFS,
fd_in@args[0], ret TransferClassified). Coverage hardening only; no defect.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
The xattr integration tests previously only asserted on getxattrat/
listxattrat (path/dirfd READ-classified variants). The path-based
setxattr(2) was traced by the existing xattr-getxattrat scenario
(workload calls syscall.Setxattr; xattrTraceArgs includes setxattr) but
never asserted. Add TestXattrSetxattr to verify end-to-end that:
- enter_setxattr captures the filesystem PATH at args[0] (kind=pathname),
never the xattr NAME at args[1];
- exit_setxattr is UNCLASSIFIED: setxattr returns a 0/-1 status, not a
byte count (the size arg is the INPUT value length), so accounted
bytes must be exactly zero. This guards against the msgsnd-style bug
of treating a status return as bytes written, and contrasts with
getxattr/listxattr which DO return byte counts (READ-classified).
This is the PATH-based set complement to filed task 8i0 (fd-based
fsetxattr/fgetxattr/flistxattr/fremovexattr); it does not duplicate it.
Classification (FamilyFS, KindPathname enter, UNCLASSIFIED ret) is
verified by inspection per task guidance, not by a unit test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The sched-basic scenario already issues sched_rr_get_interval(0, &ts)
via schedQueryPriorityRange, but TestSchedBasic only asserted the other
sched_get* queries. Add an explicit enter_sched_rr_get_interval
assertion to lock in its KindNull/UNCLASSIFIED tracing end-to-end.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
TestCleanupLeakedWorkloadTempDirCaughtByAssertion was order/concurrency
dependent: passed in isolation but its detection scanned the shared system
temp root (os.TempDir) for ANY "ioworkload-" directory via a before/after
diff. Real ioworkload scenario workloads legitimately create
ioworkload-<scenario>-* dirs in that same root while they run (in parallel
under `mage integrationTest`), so this test both falsely attributed them as
leaks and, worse, RemoveAll'd them out from under the still-running tests.
Fix: scope both detection and cleanup to a prefix unique to this single test
invocation (PID + sanitized t.Name()) via listWorkloadTempDirsWithPrefix and
newLeakedDirs. Other tests' temp dirs can no longer be observed or deleted.
Also fixed a latent detection bug: the intentional-leak workload script
hard-coded /tmp instead of os.TempDir(), so detection silently failed whenever
$TMPDIR differed from /tmp. The mktemp template now uses os.TempDir().
Removed the dead assertNoNewWorkloadTempDirs helper (defined, never called).
Verified: mage build OK; mage test green across repeated runs; mage generate
produces no diff; gofmt clean. A stress run with decoy ioworkload-<scenario>-*
dirs confirms they survive untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
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>
|
|
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>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|