diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-24 20:36:26 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-24 20:36:26 +0300 |
| commit | 92a36a8c5f23756b8c6d721e89450752409ddd75 (patch) | |
| tree | 52adee49828831feb0ca557e7df736726faedac3 /integrationtests | |
| parent | fadbf135d0b251387fd785083df79e27d1025cac (diff) | |
task a8: move all binaries under ./cmd/<name>/main.go
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>
Diffstat (limited to 'integrationtests')
20 files changed, 1 insertions, 3194 deletions
diff --git a/integrationtests/README.md b/integrationtests/README.md index 0036019..c397f8b 100644 --- a/integrationtests/README.md +++ b/integrationtests/README.md @@ -58,7 +58,7 @@ If the suite fails before tracing starts, check for these common causes: ## Structure -- `cmd/ioworkload/` — Standalone binary performing known I/O patterns +- `../cmd/ioworkload/` — Standalone binary performing known I/O patterns (lives at repo-root `cmd/`) - `harness.go` — Test orchestration (start ior + workload, collect output) - `parse.go` — Parse `.ior.zst` into assertable `TestResult` - `expectations.go` — `ExpectedEvent` type and assertion helpers diff --git a/integrationtests/cmd/ioworkload/main.go b/integrationtests/cmd/ioworkload/main.go deleted file mode 100644 index 0276a9c..0000000 --- a/integrationtests/cmd/ioworkload/main.go +++ /dev/null @@ -1,49 +0,0 @@ -// ioworkload is a standalone binary that performs deterministic I/O operations -// for integration testing of ior. It prints its PID to stdout, sleeps to allow -// ior to attach BPF tracepoints, then executes the requested I/O scenario. -package main - -import ( - "flag" - "fmt" - "os" - "slices" - "time" -) - -// Give ior enough time to attach tracepoints before scenarios emit syscalls. -// Under slower CI or locally saturated systems, 5s can still miss first-call -// events for single-shot scenarios. Use a slightly larger delay for stability. -const startupDelay = 8 * time.Second - -func main() { - scenario := flag.String("scenario", "", "I/O scenario to execute") - flag.Parse() - - if *scenario == "" { - fmt.Fprintln(os.Stderr, "usage: ioworkload --scenario=<name>") - os.Exit(2) - } - - run, ok := scenarios[*scenario] - if !ok { - fmt.Fprintf(os.Stderr, "unknown scenario: %s\navailable scenarios:\n", *scenario) - var names []string - for name := range scenarios { - names = append(names, name) - } - slices.Sort(names) - for _, name := range names { - fmt.Fprintf(os.Stderr, " %s\n", name) - } - os.Exit(2) - } - - fmt.Println(os.Getpid()) - time.Sleep(startupDelay) - - if err := run(); err != nil { - fmt.Fprintf(os.Stderr, "scenario %s failed: %v\n", *scenario, err) - os.Exit(1) - } -} diff --git a/integrationtests/cmd/ioworkload/scenario_close.go b/integrationtests/cmd/ioworkload/scenario_close.go deleted file mode 100644 index fc5044c..0000000 --- a/integrationtests/cmd/ioworkload/scenario_close.go +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import ( - "fmt" - "path/filepath" - "syscall" -) - -const sysCloseRange = 436 - -// closeBasic opens multiple files and closes them. -func closeBasic() error { - dir, cleanup, err := makeTempDir("close-basic") - if err != nil { - return err - } - defer cleanup() - - var fds []int - for i := range 3 { - path := filepath.Join(dir, fmt.Sprintf("closefile-%d.txt", i)) - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open %d: %w", i, err) - } - fds = append(fds, fd) - } - for _, fd := range fds { - if err := syscall.Close(fd); err != nil { - return fmt.Errorf("close fd %d: %w", fd, err) - } - } - return nil -} - -// closeRange opens multiple files and closes a range of them via close_range(2). -func closeRange() error { - dir, cleanup, err := makeTempDir("close-range") - if err != nil { - return err - } - defer cleanup() - - var fds []int - for i := range 3 { - path := filepath.Join(dir, fmt.Sprintf("closerangefile-%d.txt", i)) - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open %d: %w", i, err) - } - fds = append(fds, fd) - } - - if fds[2]-fds[0] != 2 { - return fmt.Errorf("fds not contiguous: %v", fds) - } - - first := uintptr(fds[0]) - last := uintptr(fds[len(fds)-1]) - _, _, errno := syscall.Syscall(sysCloseRange, first, last, 0) - if errno != 0 { - return fmt.Errorf("close_range: %w", errno) - } - return nil -} - -// closeInvalidFd attempts to close a very high fd number that is not open. -// The close fails with EBADF, but ior should capture the enter_close tracepoint -// because arguments are read on syscall entry before the kernel returns an error. -func closeInvalidFd() error { - err := syscall.Close(99999) - if err == nil { - return fmt.Errorf("expected close of invalid fd to fail") - } - return nil -} - -// closeDoubleClose opens a file, closes it normally, then closes the same fd again. -// The second close fails with EBADF, but ior should capture both enter_close -// tracepoints because arguments are read on syscall entry. -func closeDoubleClose() error { - dir, cleanup, err := makeTempDir("close-double-close") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "doubleclosefile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - - if err := syscall.Close(fd); err != nil { - return fmt.Errorf("first close: %w", err) - } - - err = syscall.Close(fd) - if err == nil { - return fmt.Errorf("expected second close of same fd to fail") - } - return nil -} - -// closeRangeEmpty calls close_range(2) with a range of very high fd numbers -// (9000–9999) where no fds are open. The syscall succeeds (empty range is valid), -// and ior should capture the enter_close_range tracepoint. -func closeRangeEmpty() error { - // Retry a few times to reduce event-loss flakiness under heavy test load. - for i := 0; i < 5; i++ { - _, _, errno := syscall.Syscall(sysCloseRange, 9000, 9999, 0) - if errno != 0 { - return fmt.Errorf("close_range: %w", errno) - } - } - return nil -} diff --git a/integrationtests/cmd/ioworkload/scenario_copy_file_range.go b/integrationtests/cmd/ioworkload/scenario_copy_file_range.go deleted file mode 100644 index ce0524e..0000000 --- a/integrationtests/cmd/ioworkload/scenario_copy_file_range.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "fmt" - "path/filepath" - "syscall" -) - -// SYS_COPY_FILE_RANGE on x86_64 Linux. -const sysCopyFileRange = 326 - -// copyFileRangeBasic copies bytes from a source file to a destination file -// using copy_file_range(2) with flags=0 as required by the manpage. -func copyFileRangeBasic() error { - dir, cleanup, err := makeTempDir("copy-file-range-basic") - if err != nil { - return err - } - defer cleanup() - - srcPath := filepath.Join(dir, "copyrangesrc.txt") - dstPath := filepath.Join(dir, "copyrangedst.txt") - - srcFd, err := syscall.Open(srcPath, syscall.O_RDWR|syscall.O_CREAT|syscall.O_TRUNC, 0o644) - if err != nil { - return fmt.Errorf("open source: %w", err) - } - defer syscall.Close(srcFd) - - dstFd, err := syscall.Open(dstPath, syscall.O_RDWR|syscall.O_CREAT|syscall.O_TRUNC, 0o644) - if err != nil { - return fmt.Errorf("open destination: %w", err) - } - defer syscall.Close(dstFd) - - data := []byte("copy_file_range integration data") - if _, err := syscall.Write(srcFd, data); err != nil { - return fmt.Errorf("write source: %w", err) - } - if _, err := syscall.Seek(srcFd, 0, 0); err != nil { - return fmt.Errorf("seek source: %w", err) - } - - n, _, errno := syscall.Syscall6(uintptr(sysCopyFileRange), uintptr(srcFd), 0, uintptr(dstFd), 0, uintptr(len(data)), 0) - if errno != 0 { - return fmt.Errorf("copy_file_range: %w", errno) - } - if n == 0 { - return fmt.Errorf("copy_file_range copied 0 bytes") - } - - return nil -} - -// copyFileRangeBadDstFd calls copy_file_range(2) with an invalid destination fd. -// The syscall should fail with EBADF, while still emitting the enter tracepoint. -func copyFileRangeBadDstFd() error { - dir, cleanup, err := makeTempDir("copy-file-range-bad-dst") - if err != nil { - return err - } - defer cleanup() - - srcPath := filepath.Join(dir, "copyrangeebadfsrc.txt") - srcFd, err := syscall.Open(srcPath, syscall.O_RDWR|syscall.O_CREAT|syscall.O_TRUNC, 0o644) - if err != nil { - return fmt.Errorf("open source: %w", err) - } - defer syscall.Close(srcFd) - - if _, err := syscall.Write(srcFd, []byte("copy_file_range ebadf data")); err != nil { - return fmt.Errorf("write source: %w", err) - } - - _, _, errno := syscall.Syscall6(uintptr(sysCopyFileRange), uintptr(srcFd), 0, uintptr(99999), 0, uintptr(16), 0) - if errno != syscall.EBADF { - return fmt.Errorf("expected EBADF from copy_file_range with invalid dst fd, got %v", errno) - } - - return nil -} diff --git a/integrationtests/cmd/ioworkload/scenario_dir.go b/integrationtests/cmd/ioworkload/scenario_dir.go deleted file mode 100644 index 7a78716..0000000 --- a/integrationtests/cmd/ioworkload/scenario_dir.go +++ /dev/null @@ -1,224 +0,0 @@ -package main - -import ( - "fmt" - "os" - "path/filepath" - "runtime" - "syscall" - "time" - "unsafe" -) - -// dirBasic creates a directory via raw SYS_MKDIR, checks access, then removes it -// via raw SYS_RMDIR. We use raw syscalls because Go's syscall.Mkdir wraps mkdirat -// and syscall.Rmdir wraps unlinkat on amd64. -func dirBasic() error { - dir, cleanup, err := makeTempDir("dir-basic") - if err != nil { - return err - } - defer cleanup() - - subDir := filepath.Join(dir, "subdir") - pathBytes, err := syscall.BytePtrFromString(subDir) - if err != nil { - return fmt.Errorf("path bytes: %w", err) - } - _, _, errno := syscall.Syscall(syscall.SYS_MKDIR, uintptr(unsafe.Pointer(pathBytes)), 0o755, 0) - runtime.KeepAlive(pathBytes) - if errno != 0 { - return fmt.Errorf("mkdir: %w", errno) - } - - if err := syscall.Access(subDir, syscall.F_OK); err != nil { - return fmt.Errorf("access: %w", err) - } - - pathBytes2, err := syscall.BytePtrFromString(subDir) - if err != nil { - return fmt.Errorf("path bytes: %w", err) - } - _, _, errno = syscall.Syscall(syscall.SYS_RMDIR, uintptr(unsafe.Pointer(pathBytes2)), 0, 0) - runtime.KeepAlive(pathBytes2) - if errno != 0 { - return fmt.Errorf("rmdir: %w", errno) - } - return nil -} - -// dirMkdirat creates a directory via mkdirat(2) using Go's syscall.Mkdir -// which wraps mkdirat with AT_FDCWD on amd64. -func dirMkdirat() error { - dir, cleanup, err := makeTempDir("dir-mkdirat") - if err != nil { - return err - } - defer cleanup() - - subDir := filepath.Join(dir, "mkdirat-subdir") - if err := syscall.Mkdir(subDir, 0o755); err != nil { - return fmt.Errorf("mkdirat: %w", err) - } - return nil -} - -// dirChdir creates a temp directory, then changes to it via chdir(2). -// Restores the original working directory afterward. -func dirChdir() error { - origDir, err := os.Getwd() - if err != nil { - return fmt.Errorf("getwd: %w", err) - } - - dir, cleanup, err := makeTempDir("dir-chdir") - if err != nil { - return err - } - defer cleanup() - defer syscall.Chdir(origDir) - - if err := syscall.Chdir(dir); err != nil { - return fmt.Errorf("chdir: %w", err) - } - return nil -} - -// dirGetcwd changes into a temp directory and calls getcwd(2) directly. -func dirGetcwd() error { - origDir, err := os.Getwd() - if err != nil { - return fmt.Errorf("getwd: %w", err) - } - - dir, cleanup, err := makeTempDir("dir-getcwd") - if err != nil { - return err - } - defer cleanup() - defer syscall.Chdir(origDir) - - if err := syscall.Chdir(dir); err != nil { - return fmt.Errorf("chdir: %w", err) - } - - buf := make([]byte, 4096) - _, _, errno := syscall.Syscall(syscall.SYS_GETCWD, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0) - runtime.KeepAlive(buf) - if errno != 0 { - return fmt.Errorf("getcwd: %w", errno) - } - // Keep cwd unchanged long enough for ior to process enter/exit pairing. - time.Sleep(300 * time.Millisecond) - return nil -} - -// dirGetdents opens a directory and reads its entries via getdents64(2). -func dirGetdents() error { - dir, cleanup, err := makeTempDir("dir-getdents") - if err != nil { - return err - } - defer cleanup() - - // Create a file so getdents has something to return. - filePath := filepath.Join(dir, "getdents-file.txt") - fd, err := syscall.Open(filePath, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open file: %w", err) - } - syscall.Close(fd) - - dirFD, err := syscall.Open(dir, syscall.O_RDONLY|syscall.O_DIRECTORY, 0) - if err != nil { - return fmt.Errorf("open dir: %w", err) - } - defer syscall.Close(dirFD) - - buf := make([]byte, 4096) - _, _, errno := syscall.Syscall(syscall.SYS_GETDENTS64, uintptr(dirFD), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) - runtime.KeepAlive(buf) - if errno != 0 { - return fmt.Errorf("getdents64: %w", errno) - } - return nil -} - -// dirMkdirEexist attempts to create a directory that already exists via raw -// SYS_MKDIR. The syscall fails with EEXIST, but ior captures the tracepoint -// on entry. -func dirMkdirEexist() error { - dir, cleanup, err := makeTempDir("dir-mkdir-eexist") - if err != nil { - return err - } - defer cleanup() - - subDir := filepath.Join(dir, "mkdir-eexist-subdir") - pathBytes, err := syscall.BytePtrFromString(subDir) - if err != nil { - return fmt.Errorf("path bytes: %w", err) - } - - // Create the directory first so the second attempt fails. - _, _, errno := syscall.Syscall(syscall.SYS_MKDIR, uintptr(unsafe.Pointer(pathBytes)), 0o755, 0) - runtime.KeepAlive(pathBytes) - if errno != 0 { - return fmt.Errorf("first mkdir: %w", errno) - } - - // Second mkdir on the same path should fail with EEXIST. - pathBytes2, err := syscall.BytePtrFromString(subDir) - if err != nil { - return fmt.Errorf("path bytes: %w", err) - } - _, _, errno = syscall.Syscall(syscall.SYS_MKDIR, uintptr(unsafe.Pointer(pathBytes2)), 0o755, 0) - runtime.KeepAlive(pathBytes2) - if errno == 0 { - return fmt.Errorf("expected EEXIST, but mkdir succeeded") - } - return nil -} - -// dirChdirEnoent attempts to change to a nonexistent directory via raw -// SYS_CHDIR. The syscall fails with ENOENT, but ior captures the tracepoint -// on entry. -func dirChdirEnoent() error { - dir, cleanup, err := makeTempDir("dir-chdir-enoent") - if err != nil { - return err - } - defer cleanup() - - badPath := filepath.Join(dir, "chdir-enoent-missing") - pathBytes, err := syscall.BytePtrFromString(badPath) - if err != nil { - return fmt.Errorf("path bytes: %w", err) - } - // Retry a few times to reduce dropped-event flakiness under high load. - for i := 0; i < 5; i++ { - _, _, errno := syscall.Syscall(syscall.SYS_CHDIR, uintptr(unsafe.Pointer(pathBytes)), 0, 0) - runtime.KeepAlive(pathBytes) - if errno == 0 { - return fmt.Errorf("expected ENOENT, but chdir succeeded") - } - } - return nil -} - -// dirGetdentsEbadf calls getdents64(2) with an invalid file descriptor. -// The syscall fails with EBADF, but ior captures the tracepoint on entry. -func dirGetdentsEbadf() error { - buf := make([]byte, 4096) - // Keep issuing the syscall for a short window so ior has enough time to - // attach under high parallel integration load. - for i := 0; i < 40; i++ { - _, _, errno := syscall.Syscall(syscall.SYS_GETDENTS64, uintptr(9999), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) - runtime.KeepAlive(buf) - if errno == 0 { - return fmt.Errorf("expected EBADF, but getdents64 succeeded") - } - time.Sleep(25 * time.Millisecond) - } - return nil -} diff --git a/integrationtests/cmd/ioworkload/scenario_dup.go b/integrationtests/cmd/ioworkload/scenario_dup.go deleted file mode 100644 index 6a89970..0000000 --- a/integrationtests/cmd/ioworkload/scenario_dup.go +++ /dev/null @@ -1,151 +0,0 @@ -package main - -import ( - "fmt" - "path/filepath" - "syscall" -) - -// dupBasic opens a file, dups the fd, writes via the dup, closes both. -func dupBasic() error { - dir, cleanup, err := makeTempDir("dup-basic") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "dupfile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - newFd, err := syscall.Dup(fd) - if err != nil { - return fmt.Errorf("dup: %w", err) - } - defer syscall.Close(newFd) - - if _, err := syscall.Write(newFd, []byte("via dup")); err != nil { - return fmt.Errorf("write via dup: %w", err) - } - return nil -} - -// dupDup2 opens a file and duplicates the fd onto a specific target fd via dup2. -func dupDup2() error { - dir, cleanup, err := makeTempDir("dup-dup2") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "dup2file.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - // Use a high fd number to avoid collisions. - targetFd := 500 - if err := syscall.Dup2(fd, targetFd); err != nil { - return fmt.Errorf("dup2: %w", err) - } - defer syscall.Close(targetFd) - - if _, err := syscall.Write(targetFd, []byte("via dup2")); err != nil { - return fmt.Errorf("write via dup2: %w", err) - } - return nil -} - -// dupDup3 opens a file and duplicates the fd onto a specific target fd via dup3 -// with O_CLOEXEC flag. -func dupDup3() error { - dir, cleanup, err := makeTempDir("dup-dup3") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "dup3file.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - // Use a high fd number to avoid collisions. - targetFd := 501 - if err := syscall.Dup3(fd, targetFd, syscall.O_CLOEXEC); err != nil { - return fmt.Errorf("dup3: %w", err) - } - defer syscall.Close(targetFd) - - if _, err := syscall.Write(targetFd, []byte("via dup3")); err != nil { - return fmt.Errorf("write via dup3: %w", err) - } - return nil -} - -// dupInvalidFd attempts to dup a very high invalid fd number. -// The syscall fails with EBADF, but ior should capture the enter_dup -// tracepoint because arguments are read on syscall entry. -func dupInvalidFd() error { - _, err := syscall.Dup(99999) - if err == nil { - return fmt.Errorf("expected dup of invalid fd to fail") - } - return nil -} - -// dup2SameFd calls dup2 with the same fd for both oldfd and newfd. -// Per POSIX, dup2(fd, fd) is a no-op that returns fd without closing -// and reopening. ior should capture the enter_dup2 tracepoint. -func dup2SameFd() error { - dir, cleanup, err := makeTempDir("dup2-same-fd") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "dup2samefile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - if err := syscall.Dup2(fd, fd); err != nil { - return fmt.Errorf("dup2 same fd: %w", err) - } - return nil -} - -// dup3InvalidFlags calls dup3 with an invalid flags value. -// dup3 only accepts O_CLOEXEC; any other flag causes EINVAL. -// ior should capture the enter_dup3 tracepoint. -func dup3InvalidFlags() error { - dir, cleanup, err := makeTempDir("dup3-invalid-flags") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "dup3flagsfile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - targetFd := 502 - _, _, errno := syscall.Syscall(syscall.SYS_DUP3, uintptr(fd), uintptr(targetFd), 0xBAD) - if errno == 0 { - syscall.Close(targetFd) - return fmt.Errorf("expected dup3 with invalid flags to fail") - } - return nil -} diff --git a/integrationtests/cmd/ioworkload/scenario_fcntl.go b/integrationtests/cmd/ioworkload/scenario_fcntl.go deleted file mode 100644 index 0c97002..0000000 --- a/integrationtests/cmd/ioworkload/scenario_fcntl.go +++ /dev/null @@ -1,134 +0,0 @@ -package main - -import ( - "fmt" - "path/filepath" - "syscall" -) - -// fcntlDupfd uses fcntl F_DUPFD to duplicate a file descriptor. -func fcntlDupfd() error { - dir, cleanup, err := makeTempDir("fcntl-dupfd") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "fcntlfile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - newFd, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD, 0) - if errno != 0 { - return fmt.Errorf("fcntl F_DUPFD: %w", errno) - } - defer syscall.Close(int(newFd)) - - if _, err := syscall.Write(int(newFd), []byte("via fcntl")); err != nil { - return fmt.Errorf("write via fcntl dup: %w", err) - } - return nil -} - -// fcntlSetfl uses fcntl F_GETFL/F_SETFL to read and modify file status flags. -func fcntlSetfl() error { - dir, cleanup, err := makeTempDir("fcntl-setfl") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "fcntlsetflfile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - flags, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_GETFL, 0) - if errno != 0 { - return fmt.Errorf("fcntl F_GETFL: %w", errno) - } - - _, _, errno = syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_SETFL, flags|syscall.O_APPEND) - if errno != 0 { - return fmt.Errorf("fcntl F_SETFL: %w", errno) - } - - if _, err := syscall.Write(fd, []byte("appended via fcntl setfl")); err != nil { - return fmt.Errorf("write: %w", err) - } - return nil -} - -// fcntlDupfdCloexec uses fcntl F_DUPFD_CLOEXEC to duplicate a file descriptor -// with the close-on-exec flag set. -func fcntlDupfdCloexec() error { - dir, cleanup, err := makeTempDir("fcntl-dupfd-cloexec") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "fcntlcloexecfile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - newFd, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0) - if errno != 0 { - return fmt.Errorf("fcntl F_DUPFD_CLOEXEC: %w", errno) - } - defer syscall.Close(int(newFd)) - - if _, err := syscall.Write(int(newFd), []byte("via fcntl dupfd cloexec")); err != nil { - return fmt.Errorf("write via fcntl dup cloexec: %w", err) - } - return nil -} - -// fcntlInvalidFd calls fcntl F_GETFL on an invalid fd (99999). -// The syscall fails with EBADF, but ior should capture the enter_fcntl -// tracepoint because it is recorded on syscall entry. -func fcntlInvalidFd() error { - for i := 0; i < 5; i++ { - _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, 99999, syscall.F_GETFL, 0) - if errno == 0 { - return fmt.Errorf("expected fcntl on invalid fd to fail") - } - } - return nil -} - -// fcntlDupfdMax opens a file and calls fcntl F_DUPFD with a minfd value -// that exceeds the process RLIMIT_NOFILE. The kernel rejects this with -// EINVAL, but ior should capture the enter_fcntl tracepoint. -func fcntlDupfdMax() error { - dir, cleanup, err := makeTempDir("fcntl-dupfd-max") - if err != nil { - return err - } - defer cleanup() - - path := filepath.Join(dir, "fcntldupfdmaxfile.txt") - fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644) - if err != nil { - return fmt.Errorf("open: %w", err) - } - defer syscall.Close(fd) - - // Retry the failing fcntl a few times to avoid a single one-shot call - // racing early trace capture under parallel integration load. - for i := 0; i < 5; i++ { - _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD, 1<<30) - if errno == 0 { - return fmt.Errorf("expected fcntl F_DUPFD with extreme minfd to fail") - } - } - return nil -} diff --git a/integrationtests/cmd/ioworkload/scenario_iouring.go b/integrationtests/cmd/ioworkload/scenario_iouring.go deleted file mode 100644 index a16d59a..0000000 --- a/integrationtests/cmd/ioworkload/scenario_iouring.go +++ /dev/null @@ -1,133 +0,0 @@ -package main - -import ( - "fmt" - "runtime" - "syscall" - "unsafe" -) - -const ( - sysIoUringSetup = 425 - sysIoUringEnter = 426 - sysIoUringRegister = 427 - - // io_uring_params struct size: 10 x uint32 + io_sqring_offsets(40) + io_cqring_offsets(40) = 120 bytes. - ioUringParamsSize = 120 - - ioringRegisterProbe = 8 // IORING_REGISTER_PROBE -) - -// iouringSetup creates an io_uring instance via io_uring_setup(2) and closes the fd. -func iouringSetup() error { - fd, err := ioUringSetupRing(1) - if err != nil { - return err - } - return syscall.Close(fd) -} - -// iouringEnter creates an io_uring instance, then calls io_uring_enter(2) -// with zero submissions/completions to exercise the enter tracepoint. -func iouringEnter() error { - fd, err := ioUringSetupRing(1) - if err != nil { - return err - } - defer syscall.Close(fd) - - _, _, errno := syscall.Syscall6( - sysIoUringEnter, - uintptr(fd), - 0, // to_submit - 0, // min_complete - 0, // flags - 0, // sig - 0, // sz - ) - if errno != 0 { - return fmt.Errorf("io_uring_enter: %w", errno) - } - return nil -} - -// iouringRegister creates an io_uring instance, then calls io_uring_register(2) -// with IORING_REGISTER_PROBE to exercise the register tracepoint. -func iouringRegister() error { - fd, err := ioUringSetupRing(1) - if err != nil { - return err - } - defer syscall.Close(fd) - - // io_uring_probe header is 16 bytes; we don't need probe_op entries. - var probeBuf [16]byte - _, _, errno := syscall.Syscall6( - sysIoUringRegister, - uintptr(fd), - ioringRegisterProbe, - uintptr(unsafe.Pointer(&probeBuf[0])), - 0, // nr_args (0 ops requested) - 0, 0, - ) - runtime.KeepAlive(probeBuf) - if errno != 0 { - return fmt.Errorf("io_uring_register: %w", errno) - } - return nil -} - -// iouringEnterEbadf calls io_uring_enter on an invalid fd. -// The syscall fails with EBADF, but ior captures the enter_io_uring_enter tracepoint. -func iouringEnterEbadf() error { - for i := 0; i < 5; i++ { - _, _, errno := syscall.Syscall6( - sysIoUringEnter, - 99999, // invalid fd - 0, // to_submit - 0, // min_complete - 0, // flags - 0, // sig - 0, // sz - ) - if errno == 0 { - return fmt.Errorf("expected EBADF, but io_uring_enter succeeded") - } - } - return nil -} - -// iouringRegisterEbadf calls io_uring_register on an invalid fd. -// The syscall fails with EBADF, but ior captures the enter_io_uring_register tracepoint. -func iouringRegisterEbadf() error { - for i := 0; i < 5; i++ { - _, _, errno := syscall.Syscall6( - sysIoUringRegister, - 99999, // invalid fd - ioringRegisterProbe, - 0, // arg (NULL) - 0, // nr_args - 0, 0, - ) - if errno == 0 { - return fmt.Errorf("expected EBADF, but io_uring_register succeeded") - } - } - return nil -} - -// ioUringSetupRing calls io_uring_setup(2) and returns the ring fd. -func ioUringSetupRing(entries uint32) (int, error) { - var params [ioUringParamsSize]byte - fd, _, errno := syscall.Syscall( - sysIoUringSetup, - uintptr(entries), - uintptr(unsafe.Pointer(¶ms[0])), - 0, - ) - runtime.KeepAlive(params) - if errno != 0 { - return 0, fmt.Errorf("io_uring_setup: %w", errno) - } - return int(fd), nil -} diff --git a/integrationtests/cmd/ioworkload/scenario_link.go b/integrationtests/cmd/ioworkload/scenario_link.go deleted file mode 100644 index beb49a0..0000000 --- a/integrationtests/cmd/ioworkload/scenario_link.go +++ /dev/null @@ -1,391 +0,0 @@ -package main - -import ( - "fmt" - "path/filepath" - "runtime" - "syscall" - "time" - "unsafe" -) - -// linkBasic creates a file, hard links it via link(2), symlinks it via -// symlink(2), and reads the symlink via readlink(2). -// Uses raw SYS_LINK, SYS_SYMLINK, SYS_READLINK because Go's syscall wrappers -// delegate to linkat/symlinkat/readlinkat on amd64. |
