| Age | Commit message (Collapse) | Author |
|
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>
|
|
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>
|
|
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>
|
|
|
|
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Split 22 production files across the codebase — event loop, TUI models,
probe manager, dashboard, export, flag parsing, code generation, and
ioworkload scenarios — so that no function body exceeds 50 lines. Each
extracted helper carries its own comment explaining its role.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Panics in non-setup code violate the AGENTS.md convention. Replace all
three panic(err) calls with fmt.Fprintf(os.Stderr, ...) + os.Exit(1) so
the process exits cleanly with a human-readable message instead of a
stack trace.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Remove the global atomic.Pointer[Config], sync.Once, and parseErr fields
from the flags package. Parse() now returns (Config, error) directly,
eliminating all global-state accessors (Get, SetPidFilter, SetTidFilter,
SetTUIExportEnable, setCurrent, updateCurrent). The internal parse logic
is factored into parseFromFlagSet so tests can inject a fresh FlagSet
without touching os.Args or package-level state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Introduce internal/runtime as a neutral contract package that both the
core engine (internal) and the TUI layer (internal/tui) depend on.
- internal/runtime: defines TraceStarter, StreamSource, EventSink,
LiveTrieSource, SnapshotSource, ProbeManager, RuntimePublisher,
RuntimeState, TraceRuntimeBindings, and all context key/helper
functions (RuntimeBindingsFromContext, RuntimePublisherFromContext,
ContextWithRuntimeBindings, ContextWithTraceFilters,
TraceFiltersFromContext). These were previously defined in
internal/tui, forcing the core package to import the TUI layer.
- internal/streamrow: add RingBuffer (moved from internal/tui/eventstream)
so the core tracing engine can use the ring buffer without importing
the TUI layer.
- internal/tui/eventstream/ringbuffer.go: change to a thin re-export of
streamrow.RingBuffer via a type alias, preserving the existing API for
all callers within the TUI layer.
- internal/tui/tui.go: replace locally-defined interface declarations
(TraceStarter, SnapshotSource, ProbeManager, RuntimePublisher,
RuntimeState, TraceRuntimeBindings) with type aliases from
internal/runtime. Delegate all context helper functions to runtime.
- internal/ior.go, internal/ior_bpfsetup.go: remove imports of
internal/tui and internal/tui/eventstream; use internal/runtime and
internal/streamrow instead. Add SetTUIRunners injection point so the
concrete TUI runner functions are wired in by cmd/ior/main.go.
- cmd/ior/main.go: call internal.SetTUIRunners with the concrete TUI
runner functions before internal.Run, completing the wiring without
creating a cycle.
- Test files (internal/ior_mode_test.go, internal/bench_pipeline_test.go):
updated to use runtime.* and streamrow.* types in place of tui.* and
eventstream.* types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Relocates the two non-canonical main packages so every binary in the repo
lives at ./cmd/<BINARY>/main.go:
- tools/filewriter/ -> cmd/filewriter/
- integrationtests/cmd/ioworkload/ (20 files) -> cmd/ioworkload/
Consumers updated:
- Magefile.go: workloadSourcePath now ./cmd/ioworkload
- integrationtests/README.md: structure note points at ../cmd/ioworkload
Files moved with git mv so git log --follow history is preserved.
cmd/ior/main.go was already canonical and is untouched.
Verified: mage build produces the ior binary; go build ./cmd/...
builds filewriter and ioworkload; go test ./cmd/ioworkload passes;
go vet ./cmd/filewriter ./cmd/ioworkload is clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
(task 429)
flags.Get() (global mutable singleton) was called inside library packages,
coupling them to global state and making tests fragile (DIP violation).
- internal.Run() now takes an explicit flags.Config; main.go calls
flags.Get() once at the CLI boundary and passes it in.
- tui.Run(), RunWithTraceStarter(), RunTestFlamesWithTraceStarter() removed;
callers already used the WithConfig variants directly.
- tui.NewModel() preserved for test ergonomics but now uses flags.NewFlags()
(pure defaults) instead of flags.Get() (global state).
- Tests updated to use NewModelWithConfig() with explicit config structs
instead of flags.Set*() + NewModel(), eliminating all test-level
global-state mutation.
flags.Get() is now called only in cmd/ior/main.go, the correct boundary.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
|
|
|