summaryrefslogtreecommitdiff
path: root/integrationtests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-24 20:36:26 +0300
committerPaul Buetow <paul@buetow.org>2026-04-24 20:36:26 +0300
commit92a36a8c5f23756b8c6d721e89450752409ddd75 (patch)
tree52adee49828831feb0ca557e7df736726faedac3 /integrationtests
parentfadbf135d0b251387fd785083df79e27d1025cac (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')
-rw-r--r--integrationtests/README.md2
-rw-r--r--integrationtests/cmd/ioworkload/main.go49
-rw-r--r--integrationtests/cmd/ioworkload/scenario_close.go117
-rw-r--r--integrationtests/cmd/ioworkload/scenario_copy_file_range.go81
-rw-r--r--integrationtests/cmd/ioworkload/scenario_dir.go224
-rw-r--r--integrationtests/cmd/ioworkload/scenario_dup.go151
-rw-r--r--integrationtests/cmd/ioworkload/scenario_fcntl.go134
-rw-r--r--integrationtests/cmd/ioworkload/scenario_iouring.go133
-rw-r--r--integrationtests/cmd/ioworkload/scenario_link.go391
-rw-r--r--integrationtests/cmd/ioworkload/scenario_mmap.go110
-rw-r--r--integrationtests/cmd/ioworkload/scenario_open.go270
-rw-r--r--integrationtests/cmd/ioworkload/scenario_pidfd.go133
-rw-r--r--integrationtests/cmd/ioworkload/scenario_pidfd_test.go57
-rw-r--r--integrationtests/cmd/ioworkload/scenario_readwrite.go263
-rw-r--r--integrationtests/cmd/ioworkload/scenario_rename.go253
-rw-r--r--integrationtests/cmd/ioworkload/scenario_stat.go286
-rw-r--r--integrationtests/cmd/ioworkload/scenario_sync.go137
-rw-r--r--integrationtests/cmd/ioworkload/scenario_truncate.go93
-rw-r--r--integrationtests/cmd/ioworkload/scenario_unlink.go193
-rw-r--r--integrationtests/cmd/ioworkload/scenarios.go118
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(&params[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.