summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-31 10:27:26 +0300
committerPaul Buetow <paul@buetow.org>2026-05-31 10:27:26 +0300
commit783c551f8e7f293b723e44386e50c4739075e2d4 (patch)
tree4e8f752123893483e1813b359e062fec426ea40f /cmd
parent7e58a0df8b994e4a3a53aec88dc05fe7f1b079bf (diff)
test(openat2): add end-to-end integration coverage for openat2
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>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/ioworkload/scenario_open.go59
-rw-r--r--cmd/ioworkload/scenarios.go1
2 files changed, 60 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_open.go b/cmd/ioworkload/scenario_open.go
index 1aebec1..8a18e35 100644
--- a/cmd/ioworkload/scenario_open.go
+++ b/cmd/ioworkload/scenario_open.go
@@ -127,6 +127,65 @@ func openPidFilter() error {
return nil
}
+// openHow mirrors the kernel's struct open_how (uapi/linux/openat2.h):
+// the resolve-time configuration that openat2(2) takes by pointer instead of
+// a plain flags int. flags carries the O_* open flags, mode the creation mode
+// (only meaningful with O_CREAT/O_TMPFILE), and resolve the RESOLVE_* bits.
+type openHow struct {
+ Flags uint64
+ Mode uint64
+ Resolve uint64
+}
+
+// sysOpenat2 is the openat2(2) syscall number on amd64.
+const sysOpenat2 = 437
+
+// openOpenat2 opens a file via the raw openat2(2) syscall. Go's syscall package
+// has no openat2 wrapper and routes Open/Openat through openat, so we must issue
+// the raw syscall to actually exercise the openat2 tracepoint. The path lives at
+// args[1] (after dirfd at args[0]); the open flags/mode live INSIDE the open_how
+// struct pointed to by args[2], not as a plain int argument — ior reads the path
+// from args[1] and intentionally does not decode flags out of the struct.
+func openOpenat2() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ dir, cleanup, err := makeTempDir("open-openat2")
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+
+ path := filepath.Join(dir, "openat2file.txt")
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return fmt.Errorf("path bytes: %w", err)
+ }
+
+ how := openHow{Flags: uint64(syscall.O_RDWR | syscall.O_CREAT), Mode: 0o644}
+ // Use a runtime int variable so the negative AT_FDCWD survives the uintptr
+ // conversion: converting the negative constant directly overflows uintptr.
+ dirfd := _AT_FDCWD
+ fd, _, errno := syscall.Syscall6(
+ sysOpenat2,
+ uintptr(dirfd),
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(unsafe.Pointer(&how)),
+ unsafe.Sizeof(how),
+ 0, 0,
+ )
+ runtime.KeepAlive(pathBytes)
+ runtime.KeepAlive(&how)
+ if errno != 0 {
+ return fmt.Errorf("openat2: %w", errno)
+ }
+ return syscall.Close(int(fd))
+}
+
+// _AT_FDCWD is the special dirfd value meaning "relative to the current working
+// directory"; openat2 with an absolute path ignores it but still requires it.
+const _AT_FDCWD int = -100
+
// openByHandleAt creates a file, resolves its handle via name_to_handle_at,
// then opens it via open_by_handle_at. Requires root (CAP_DAC_READ_SEARCH).
// LockOSThread prevents goroutine migration between the two syscalls so that
diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go
index 7f42106..821c8b2 100644
--- a/cmd/ioworkload/scenarios.go
+++ b/cmd/ioworkload/scenarios.go
@@ -9,6 +9,7 @@ import (
var scenarios = map[string]func() error{
"crash": crash,
"open-basic": openBasic,
+ "open-openat2": openOpenat2,
"open-creat": openCreat,
"open-by-handle-at": openByHandleAt,
"open-duration-gap": openDurationGap,