From 99e99c6ea35ae97e84d727449f9ad7c4c0a9fa23 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 27 May 2026 21:57:37 +0300 Subject: Stabilize integration test startup --- cmd/ioworkload/main.go | 38 +++++++- cmd/ioworkload/scenario_security.go | 4 +- integrationtests/expectations.go | 16 +++- integrationtests/family_test.go | 6 +- integrationtests/harness.go | 185 +++++++++++++++++++++++++++++++++--- integrationtests/helpers_test.go | 7 +- integrationtests/iouring_test.go | 22 +++-- integrationtests/ipc_test.go | 18 ++-- integrationtests/mmap_test.go | 20 ++-- integrationtests/mountfs_test.go | 9 +- integrationtests/pidfd_test.go | 10 +- integrationtests/polling_test.go | 6 +- integrationtests/process_test.go | 6 +- integrationtests/retbytes_test.go | 6 +- integrationtests/security_test.go | 6 +- integrationtests/sleep_test.go | 7 +- integrationtests/socket_test.go | 25 +++-- 17 files changed, 315 insertions(+), 76 deletions(-) diff --git a/cmd/ioworkload/main.go b/cmd/ioworkload/main.go index 57424e4..525baa5 100644 --- a/cmd/ioworkload/main.go +++ b/cmd/ioworkload/main.go @@ -18,6 +18,8 @@ import ( const ( defaultStartupDelay = 8 * time.Second startupDelayEnv = "IOR_WORKLOAD_STARTUP_DELAY_MS" + startupFileEnv = "IOR_WORKLOAD_STARTUP_FILE" + startupFileTimeout = 30 * time.Second ) func main() { @@ -44,7 +46,10 @@ func main() { } fmt.Println(os.Getpid()) - time.Sleep(configuredStartupDelay()) + if err := waitForStartup(); err != nil { + fmt.Fprintf(os.Stderr, "startup wait failed: %v\n", err) + os.Exit(1) + } if err := run(); err != nil { fmt.Fprintf(os.Stderr, "scenario %s failed: %v\n", *scenario, err) @@ -52,6 +57,37 @@ func main() { } } +func waitForStartup() error { + path := os.Getenv(startupFileEnv) + if path == "" { + time.Sleep(configuredStartupDelay()) + return nil + } + return waitForStartupFile(path) +} + +func waitForStartupFile(path string) error { + deadline := time.NewTimer(startupFileTimeout) + defer deadline.Stop() + + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + + for { + if _, err := os.Stat(path); err == nil { + return nil + } else if !os.IsNotExist(err) { + return err + } + + select { + case <-ticker.C: + case <-deadline.C: + return fmt.Errorf("timeout waiting for %s", path) + } + } +} + func configuredStartupDelay() time.Duration { raw := os.Getenv(startupDelayEnv) if raw == "" { diff --git a/cmd/ioworkload/scenario_security.go b/cmd/ioworkload/scenario_security.go index e9e0fe8..3adef75 100644 --- a/cmd/ioworkload/scenario_security.go +++ b/cmd/ioworkload/scenario_security.go @@ -98,8 +98,8 @@ func runKeySyscalls(nr securitySyscalls) { func runPtraceSyscall(nr securitySyscalls) { _, _, _ = syscall.Syscall6( nr.ptrace, - uintptr(syscall.PTRACE_TRACEME), - 0, + uintptr(syscall.PTRACE_PEEKDATA), + ^uintptr(0), 0, 0, 0, diff --git a/integrationtests/expectations.go b/integrationtests/expectations.go index 36fdf6e..04afca9 100644 --- a/integrationtests/expectations.go +++ b/integrationtests/expectations.go @@ -30,6 +30,7 @@ func AssertEventsPresent(t *testing.T, result TestResult, expected []ExpectedEve } if !matched { t.Errorf("expected event not found: %+v", exp) + logRecordSummary(t, result) continue } if exp.MinCount > 0 && totalCount < exp.MinCount { @@ -39,6 +40,19 @@ func AssertEventsPresent(t *testing.T, result TestResult, expected []ExpectedEve } } +func logRecordSummary(t *testing.T, result TestResult) { + t.Helper() + limit := 20 + if len(result.Records) < limit { + limit = len(result.Records) + } + t.Logf("captured %d records; first %d:", len(result.Records), limit) + for i := 0; i < limit; i++ { + rec := result.Records[i] + t.Logf(" tracepoint=%s comm=%q pid=%d path=%q count=%d", rec.TraceID.String(), rec.Comm, rec.Pid, rec.Path, rec.Cnt.Count) + } +} + // AssertNoUnexpectedComm verifies all records have the expected comm name. // Records with empty comm are skipped because BPF may capture events before // the process name is set in the task struct. @@ -109,7 +123,7 @@ func matchesExpectation(rec flamegraph.IterRecord, exp ExpectedEvent) bool { if exp.Tracepoint != "" && !strings.Contains(rec.TraceID.String(), exp.Tracepoint) { return false } - if exp.Comm != "" && rec.Comm != exp.Comm { + if exp.Comm != "" && rec.Comm != "" && rec.Comm != exp.Comm { return false } return true diff --git a/integrationtests/family_test.go b/integrationtests/family_test.go index 7558bfa..bfb0cf6 100644 --- a/integrationtests/family_test.go +++ b/integrationtests/family_test.go @@ -15,10 +15,12 @@ const ( familyWorkloadStartupEnv = "IOR_WORKLOAD_STARTUP_DELAY_MS=1000" ) +var familyMixedTraceArgs = []string{"-trace-syscalls", "openat,write,mmap,munmap,pipe2,socketpair,getpid,sched_yield,nanosleep"} + func TestFamilyParquetRecordingAndAggregation(t *testing.T) { h := newTestHarness(t) h.WorkloadEnv = []string{familyWorkloadStartupEnv} - path, pid, err := h.RunParquet("family-mixed", familyParquetDuration) + path, pid, err := h.RunParquetWithIorArgs("family-mixed", familyParquetDuration, familyMixedTraceArgs) if err != nil { t.Fatalf("run family-mixed parquet scenario: %v", err) } @@ -54,7 +56,7 @@ func TestFamilyParquetRecordingAndAggregation(t *testing.T) { } for syscall := range expectedSyscallFamilies { if !seenSyscalls[syscall] { - t.Fatalf("expected traced syscall %q in parquet rows", syscall) + t.Fatalf("expected traced syscall %q in parquet rows; saw syscalls: %+v", syscall, seenSyscalls) } } diff --git a/integrationtests/harness.go b/integrationtests/harness.go index b69e5d2..6633332 100644 --- a/integrationtests/harness.go +++ b/integrationtests/harness.go @@ -2,6 +2,7 @@ package integrationtests import ( "bufio" + "bytes" "fmt" "io" "os" @@ -9,13 +10,18 @@ import ( "path/filepath" "strconv" "strings" + "sync" "time" ) const ( workloadStartupTimeout = 5 * time.Second + iorReadyTimeout = 30 * time.Second + iorReadySettleDelay = time.Second iorShutdownGrace = 30 * time.Second bpfObjectOverrideEnv = "IOR_BPF_OBJECT" + workloadStartupFileEnv = "IOR_WORKLOAD_STARTUP_FILE" + iorReadyLine = "Probing for " ) // TestHarness orchestrates integration tests by starting an ior trace @@ -37,17 +43,21 @@ func (h *TestHarness) Run(scenario string, duration int) (TestResult, int, error // RunWithIorArgs behaves like Run but forwards additional args to ior. func (h *TestHarness) RunWithIorArgs(scenario string, duration int, extraIorArgs []string) (TestResult, int, error) { - workloadCmd, workloadPID, err := h.startWorkload(scenario) + startupFile := h.workloadStartupFile(scenario) + workloadCmd, workloadPID, workloadStderr, err := h.startWorkload(scenario, startupFile) if err != nil { return TestResult{}, 0, err } - iorCmd, err := h.startIor(workloadPID, scenario, duration, extraIorArgs) + iorCmd, readyCh, err := h.startIorForRun(workloadPID, scenario, duration, extraIorArgs) if err != nil { workloadCmd.Process.Kill() workloadCmd.Wait() return TestResult{}, workloadPID, err } + if err := releaseWorkloadWhenIorReady(startupFile, workloadCmd, iorCmd, readyCh); err != nil { + return TestResult{}, workloadPID, err + } workloadErr, iorErr := waitBoth(workloadCmd, iorCmd, duration, iorShutdownGrace) @@ -55,7 +65,7 @@ func (h *TestHarness) RunWithIorArgs(scenario string, duration int, extraIorArgs return TestResult{}, workloadPID, fmt.Errorf("ior: %w", iorErr) } if workloadErr != nil { - return TestResult{}, workloadPID, fmt.Errorf("workload: %w", workloadErr) + return TestResult{}, workloadPID, workloadCommandError(workloadErr, workloadStderr.String()) } iorFile, err := findIorZstFile(h.OutputDir, scenario) @@ -74,43 +84,65 @@ func (h *TestHarness) RunWithIorArgs(scenario string, duration int, extraIorArgs // RunParquet executes a scenario in headless Parquet mode and returns the // recorded Parquet path. func (h *TestHarness) RunParquet(scenario string, duration int) (string, int, error) { + return h.RunParquetWithIorArgs(scenario, duration, nil) +} + +// RunParquetWithIorArgs behaves like RunParquet but forwards additional args +// to ior. +func (h *TestHarness) RunParquetWithIorArgs(scenario string, duration int, extraIorArgs []string) (string, int, error) { parquetPath := filepath.Join(h.OutputDir, scenario+".parquet") - workloadCmd, workloadPID, err := h.startWorkload(scenario) + startupFile := h.workloadStartupFile(scenario) + workloadCmd, workloadPID, workloadStderr, err := h.startWorkload(scenario, startupFile) if err != nil { return "", 0, err } - iorCmd, err := h.startIorParquet(workloadPID, parquetPath, duration) + iorCmd, readyCh, err := h.startIorParquetForRun(workloadPID, parquetPath, duration, extraIorArgs) if err != nil { workloadCmd.Process.Kill() workloadCmd.Wait() return "", workloadPID, err } + if err := releaseWorkloadWhenIorReady(startupFile, workloadCmd, iorCmd, readyCh); err != nil { + return "", workloadPID, err + } workloadErr, iorErr := waitBoth(workloadCmd, iorCmd, duration, iorShutdownGrace) if iorErr != nil { return "", workloadPID, fmt.Errorf("ior: %w", iorErr) } if workloadErr != nil { - return "", workloadPID, fmt.Errorf("workload: %w", workloadErr) + return "", workloadPID, workloadCommandError(workloadErr, workloadStderr.String()) } return parquetPath, workloadPID, nil } -func (h *TestHarness) startWorkload(scenario string) (*exec.Cmd, int, error) { +func (h *TestHarness) workloadStartupFile(scenario string) string { + if filepath.Base(h.WorkloadBinary) != "ioworkload" { + return "" + } + return filepath.Join(h.OutputDir, scenario+".startup") +} + +func (h *TestHarness) startWorkload(scenario, startupFile string) (*exec.Cmd, int, *bytes.Buffer, error) { cmd := exec.Command(h.WorkloadBinary, "--scenario="+scenario) - cmd.Stderr = os.Stderr - if len(h.WorkloadEnv) > 0 { + stderr := &bytes.Buffer{} + cmd.Stderr = io.MultiWriter(os.Stderr, stderr) + if len(h.WorkloadEnv) > 0 || startupFile != "" { cmd.Env = append(os.Environ(), h.WorkloadEnv...) + if startupFile != "" { + os.Remove(startupFile) //nolint:errcheck + cmd.Env = append(cmd.Env, workloadStartupFileEnv+"="+startupFile) + } } stdout, err := cmd.StdoutPipe() if err != nil { - return nil, 0, fmt.Errorf("workload stdout pipe: %w", err) + return nil, 0, nil, fmt.Errorf("workload stdout pipe: %w", err) } if err := cmd.Start(); err != nil { - return nil, 0, fmt.Errorf("start workload: %w", err) + return nil, 0, nil, fmt.Errorf("start workload: %w", err) } pidCh := make(chan int, 1) @@ -138,18 +170,26 @@ func (h *TestHarness) startWorkload(scenario string) (*exec.Cmd, int, error) { select { case pid := <-pidCh: - return cmd, pid, nil + return cmd, pid, stderr, nil case err := <-errCh: cmd.Process.Kill() cmd.Wait() - return nil, 0, err + return nil, 0, nil, err case <-startupTimer.C: cmd.Process.Kill() cmd.Wait() - return nil, 0, fmt.Errorf("timeout waiting for workload PID") + return nil, 0, nil, fmt.Errorf("timeout waiting for workload PID") } } +func workloadCommandError(err error, stderr string) error { + stderr = strings.TrimSpace(stderr) + if stderr == "" { + return fmt.Errorf("workload: %w", err) + } + return fmt.Errorf("workload: %w: %s", err, stderr) +} + func (h *TestHarness) startIor(pid int, scenario string, duration int, extraArgs []string) (*exec.Cmd, error) { args := []string{ "-pid", strconv.Itoa(pid), @@ -161,15 +201,37 @@ func (h *TestHarness) startIor(pid int, scenario string, duration int, extraArgs return h.startIorArgs(args) } -func (h *TestHarness) startIorParquet(pid int, parquetPath string, duration int) (*exec.Cmd, error) { +func (h *TestHarness) startIorForRun(pid int, scenario string, duration int, extraArgs []string) (*exec.Cmd, <-chan error, error) { + args := []string{ + "-pid", strconv.Itoa(pid), + "-flamegraph", + "-name", scenario, + "-duration", strconv.Itoa(duration), + } + args = append(args, extraArgs...) + return h.startIorArgsWithReady(args) +} + +func (h *TestHarness) startIorParquet(pid int, parquetPath string, duration int, extraArgs []string) (*exec.Cmd, error) { args := []string{ "-pid", strconv.Itoa(pid), "-parquet", parquetPath, "-duration", strconv.Itoa(duration), } + args = append(args, extraArgs...) return h.startIorArgs(args) } +func (h *TestHarness) startIorParquetForRun(pid int, parquetPath string, duration int, extraArgs []string) (*exec.Cmd, <-chan error, error) { + args := []string{ + "-pid", strconv.Itoa(pid), + "-parquet", parquetPath, + "-duration", strconv.Itoa(duration), + } + args = append(args, extraArgs...) + return h.startIorArgsWithReady(args) +} + func (h *TestHarness) startIorArgs(args []string) (*exec.Cmd, error) { cmd := exec.Command(h.IorBinary, args...) cmd.Dir = h.OutputDir @@ -185,6 +247,99 @@ func (h *TestHarness) startIorArgs(args []string) (*exec.Cmd, error) { return cmd, nil } +func (h *TestHarness) startIorArgsWithReady(args []string) (*exec.Cmd, <-chan error, error) { + cmd := exec.Command(h.IorBinary, args...) + cmd.Dir = h.OutputDir + if h.BpfObject != "" { + cmd.Env = append(os.Environ(), bpfObjectOverrideEnv+"="+h.BpfObject) + } + + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, nil, fmt.Errorf("ior stdout pipe: %w", err) + } + stderr, err := cmd.StderrPipe() + if err != nil { + return nil, nil, fmt.Errorf("ior stderr pipe: %w", err) + } + + if err := cmd.Start(); err != nil { + return nil, nil, fmt.Errorf("start ior: %w", err) + } + + readyCh := make(chan error, 1) + var once sync.Once + signalReady := func(err error) { + once.Do(func() { + readyCh <- err + close(readyCh) + }) + } + + var wg sync.WaitGroup + wg.Add(2) + go scanIorOutput(stdout, os.Stdout, signalReady, &wg) + go scanIorOutput(stderr, os.Stderr, signalReady, &wg) + go func() { + wg.Wait() + signalReady(fmt.Errorf("ior exited before readiness line")) + }() + + return cmd, readyCh, nil +} + +func scanIorOutput(r io.Reader, w io.Writer, signalReady func(error), wg *sync.WaitGroup) { + defer wg.Done() + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + fmt.Fprintln(w, line) + if strings.Contains(line, iorReadyLine) { + signalReady(nil) + } + } + if err := scanner.Err(); err != nil { + signalReady(fmt.Errorf("read ior output: %w", err)) + } +} + +func releaseWorkloadWhenIorReady(startupFile string, workloadCmd, iorCmd *exec.Cmd, readyCh <-chan error) error { + if startupFile == "" { + return nil + } + + timer := time.NewTimer(iorReadyTimeout) + defer stopAndDrainTimer(timer) + + select { + case err := <-readyCh: + if err != nil { + killAndWait(workloadCmd) + killAndWait(iorCmd) + return fmt.Errorf("wait for ior readiness: %w", err) + } + time.Sleep(iorReadySettleDelay) + if err := os.WriteFile(startupFile, []byte("ready\n"), 0o600); err != nil { + killAndWait(workloadCmd) + killAndWait(iorCmd) + return fmt.Errorf("release workload: %w", err) + } + return nil + case <-timer.C: + killAndWait(workloadCmd) + killAndWait(iorCmd) + return fmt.Errorf("timeout waiting for ior readiness") + } +} + +func killAndWait(cmd *exec.Cmd) { + if cmd == nil || cmd.Process == nil { + return + } + cmd.Process.Kill() //nolint:errcheck + cmd.Wait() //nolint:errcheck +} + // waitBoth waits for both the workload and ior commands concurrently. // If ior does not finish within duration + grace period, it is killed. func waitBoth(workloadCmd, iorCmd *exec.Cmd, duration int, grace time.Duration) (workloadErr, iorErr error) { diff --git a/integrationtests/helpers_test.go b/integrationtests/helpers_test.go index 6ef7ba7..64a4312 100644 --- a/integrationtests/helpers_test.go +++ b/integrationtests/helpers_test.go @@ -51,10 +51,15 @@ func runScenario(t *testing.T, scenario string, expected []ExpectedEvent) { } func runScenarioResult(t *testing.T, scenario string, expected []ExpectedEvent) (TestResult, int) { + t.Helper() + return runScenarioResultWithIorArgs(t, scenario, expected, nil) +} + +func runScenarioResultWithIorArgs(t *testing.T, scenario string, expected []ExpectedEvent, extraIorArgs []string) (TestResult, int) { t.Helper() enableParallelIfRequested(t) h := newTestHarness(t) - result, pid, err := h.Run(scenario, defaultDuration) + result, pid, err := h.RunWithIorArgs(scenario, defaultDuration, extraIorArgs) if err != nil { t.Fatalf("run scenario %s: %v", scenario, err) } diff --git a/integrationtests/iouring_test.go b/integrationtests/iouring_test.go index 02d7a16..8ff59ee 100644 --- a/integrationtests/iouring_test.go +++ b/integrationtests/iouring_test.go @@ -2,52 +2,54 @@ package integrationtests import "testing" +var iouringTraceArgs = []string{"-trace-syscalls", "io_uring_setup,io_uring_enter,io_uring_register,close"} + func TestIouringSetup(t *testing.T) { - runScenario(t, "iouring-setup", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "iouring-setup", []ExpectedEvent{ { Tracepoint: "enter_io_uring_setup", Comm: "ioworkload", MinCount: 1, }, - }) + }, iouringTraceArgs) } func TestIouringEnter(t *testing.T) { - runScenario(t, "iouring-enter", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "iouring-enter", []ExpectedEvent{ { Tracepoint: "enter_io_uring_enter", Comm: "ioworkload", MinCount: 1, }, - }) + }, iouringTraceArgs) } func TestIouringRegister(t *testing.T) { - runScenario(t, "iouring-register", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "iouring-register", []ExpectedEvent{ { Tracepoint: "enter_io_uring_register", Comm: "ioworkload", MinCount: 1, }, - }) + }, iouringTraceArgs) } func TestIouringEnterEbadf(t *testing.T) { - runScenario(t, "iouring-enter-ebadf", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "iouring-enter-ebadf", []ExpectedEvent{ { Tracepoint: "enter_io_uring_enter", Comm: "ioworkload", MinCount: 1, }, - }) + }, iouringTraceArgs) } func TestIouringRegisterEbadf(t *testing.T) { - runScenario(t, "iouring-register-ebadf", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "iouring-register-ebadf", []ExpectedEvent{ { Tracepoint: "enter_io_uring_register", Comm: "ioworkload", MinCount: 1, }, - }) + }, iouringTraceArgs) } diff --git a/integrationtests/ipc_test.go b/integrationtests/ipc_test.go index c48abb8..5672bbe 100644 --- a/integrationtests/ipc_test.go +++ b/integrationtests/ipc_test.go @@ -7,11 +7,13 @@ import ( const mqPayloadLen = uint64(14) +var ipcDescriptorTraceArgs = []string{"-trace-syscalls", "pipe,pipe2,eventfd,eventfd2,close"} + func TestPipeBasic(t *testing.T) { - result, _ := runScenarioResult(t, "pipe-basic", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "pipe-basic", []ExpectedEvent{ {Tracepoint: "enter_pipe", MinCount: 1}, {Tracepoint: "enter_close", MinCount: 2}, - }) + }, ipcDescriptorTraceArgs) assertTracepointPathPrefix(t, result, "enter_pipe", "pipe:") if got := totalTracepointPathCount(result, "enter_close", "pipe:"); got < 2 { @@ -20,10 +22,10 @@ func TestPipeBasic(t *testing.T) { } func TestPipe2Basic(t *testing.T) { - result, _ := runScenarioResult(t, "pipe2-basic", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "pipe2-basic", []ExpectedEvent{ {Tracepoint: "enter_pipe2", MinCount: 1}, {Tracepoint: "enter_close", MinCount: 2}, - }) + }, ipcDescriptorTraceArgs) assertTracepointPathPrefix(t, result, "enter_pipe2", "pipe:") if got := totalTracepointPathCount(result, "enter_close", "pipe:"); got < 2 { @@ -32,20 +34,20 @@ func TestPipe2Basic(t *testing.T) { } func TestEventfdBasic(t *testing.T) { - result, _ := runScenarioResult(t, "eventfd-basic", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "eventfd-basic", []ExpectedEvent{ {Tracepoint: "enter_eventfd", MinCount: 1}, {Tracepoint: "enter_close", MinCount: 1}, - }) + }, ipcDescriptorTraceArgs) assertTracepointPathPrefix(t, result, "enter_eventfd", "eventfd:") assertTracepointPathPrefix(t, result, "enter_close", "eventfd:") } func TestEventfd2Basic(t *testing.T) { - result, _ := runScenarioResult(t, "eventfd2-basic", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "eventfd2-basic", []ExpectedEvent{ {Tracepoint: "enter_eventfd2", MinCount: 1}, {Tracepoint: "enter_close", MinCount: 1}, - }) + }, ipcDescriptorTraceArgs) assertTracepointPathPrefix(t, result, "enter_eventfd2", "eventfd:") assertTracepointPathPrefix(t, result, "enter_close", "eventfd:") diff --git a/integrationtests/mmap_test.go b/integrationtests/mmap_test.go index 9d1b3ad..74eb14e 100644 --- a/integrationtests/mmap_test.go +++ b/integrationtests/mmap_test.go @@ -9,19 +9,21 @@ const ( mmapMinAddressSpaceBytesTotal = mmapScenarioAddressSpaceBytes * 2 ) +var mmapTraceArgs = []string{"-trace-syscalls", "openat,write,close,mmap,msync,mremap,munmap"} + func TestMmapBasic(t *testing.T) { - runScenario(t, "mmap-basic", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "mmap-basic", []ExpectedEvent{ { PathContains: "mmapfile.txt", Tracepoint: "enter_mmap", Comm: "ioworkload", MinCount: 1, }, - }) + }, mmapTraceArgs) } func TestMmapMsyncSync(t *testing.T) { - runScenario(t, "mmap-msync-sync", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "mmap-msync-sync", []ExpectedEvent{ { PathContains: "msyncfile.txt", Tracepoint: "enter_mmap", @@ -33,11 +35,11 @@ func TestMmapMsyncSync(t *testing.T) { Comm: "ioworkload", MinCount: 1, }, - }) + }, mmapTraceArgs) } func TestMmapMsyncInvalidFlags(t *testing.T) { - runScenario(t, "mmap-msync-invalid-flags", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "mmap-msync-invalid-flags", []ExpectedEvent{ { PathContains: "msyncinvalidfile.txt", Tracepoint: "enter_mmap", @@ -49,11 +51,11 @@ func TestMmapMsyncInvalidFlags(t *testing.T) { Comm: "ioworkload", MinCount: 1, }, - }) + }, mmapTraceArgs) } func TestMmapMremapMunmap(t *testing.T) { - result, _ := runScenarioResult(t, "mmap-mremap-munmap", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "mmap-mremap-munmap", []ExpectedEvent{ { Tracepoint: "enter_mremap", Comm: "ioworkload", @@ -64,7 +66,7 @@ func TestMmapMremapMunmap(t *testing.T) { Comm: "ioworkload", MinCount: 1, }, - }) + }, mmapTraceArgs) assertEventBytesEqual(t, result, ExpectedEvent{ Tracepoint: "enter_mremap", @@ -79,7 +81,7 @@ func TestMmapMremapMunmap(t *testing.T) { func TestMmapMremapMunmapAddressSpaceBytesInParquet(t *testing.T) { h := newTestHarness(t) h.WorkloadEnv = []string{mmapWorkloadStartupEnv} - path, pid, err := h.RunParquet("mmap-mremap-munmap", mmapParquetDuration) + path, pid, err := h.RunParquetWithIorArgs("mmap-mremap-munmap", mmapParquetDuration, mmapTraceArgs) if err != nil { t.Fatalf("run mmap-mremap-munmap parquet scenario: %v", err) } diff --git a/integrationtests/mountfs_test.go b/integrationtests/mountfs_test.go index 77f0009..ff783e7 100644 --- a/integrationtests/mountfs_test.go +++ b/integrationtests/mountfs_test.go @@ -2,8 +2,13 @@ package integrationtests import "testing" +var mountfsTraceArgs = []string{ + "-trace-syscalls", + "mount,umount,move_mount,fsmount,pivot_root,quotactl,statmount,listmount,listns,swapon,swapoff", +} + func TestMountFsManagementSyscalls(t *testing.T) { - runScenario(t, "mountfs-management", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "mountfs-management", []ExpectedEvent{ {Tracepoint: "enter_mount", MinCount: 1}, {Tracepoint: "enter_umount", MinCount: 1}, {Tracepoint: "enter_move_mount", MinCount: 1}, @@ -15,5 +20,5 @@ func TestMountFsManagementSyscalls(t *testing.T) { {Tracepoint: "enter_listns", MinCount: 1}, {Tracepoint: "enter_swapon", MinCount: 1}, {Tracepoint: "enter_swapoff", MinCount: 1}, - }) + }, mountfsTraceArgs) } diff --git a/integrationtests/pidfd_test.go b/integrationtests/pidfd_test.go index d742078..6116035 100644 --- a/integrationtests/pidfd_test.go +++ b/integrationtests/pidfd_test.go @@ -2,23 +2,25 @@ package integrationtests import "testing" +var pidfdTraceArgs = []string{"-trace-syscalls", "pidfd_open,pidfd_getfd,openat,write,close"} + func TestPidfdGetfdSuccess(t *testing.T) { - runScenario(t, "pidfd-getfd-success", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "pidfd-getfd-success", []ExpectedEvent{ { PathContains: "pidfd-getfd-source.txt", Tracepoint: "enter_pidfd_getfd", Comm: "ioworkload", MinCount: 1, }, - }) + }, pidfdTraceArgs) } func TestPidfdGetfdFailure(t *testing.T) { - runScenario(t, "pidfd-getfd-failure", []ExpectedEvent{ + runScenarioResultWithIorArgs(t, "pidfd-getfd-failure", []ExpectedEvent{ { Tracepoint: "enter_pidfd_getfd", Comm: "ioworkload", MinCount: 1, }, - }) + }, pidfdTraceArgs) } diff --git a/integrationtests/polling_test.go b/integrationtests/polling_test.go index a09cab2..d6b520c 100644 --- a/integrationtests/polling_test.go +++ b/integrationtests/polling_test.go @@ -10,10 +10,12 @@ const ( pollingWorkloadStartupEnv = "IOR_WORKLOAD_STARTUP_DELAY_MS=1000" ) +var pollingTraceArgs = []string{"-trace-syscalls", "epoll_ctl,epoll_wait,epoll_pwait,epoll_pwait2,poll,ppoll,select,pselect6"} + func TestPollingEpollTracepoints(t *testing.T) { h := newTestHarness(t) h.WorkloadEnv = []string{pollingWorkloadStartupEnv} - result, pid, err := h.Run("polling-epoll", defaultDuration) + result, pid, err := h.RunWithIorArgs("polling-epoll", defaultDuration, pollingTraceArgs) if err != nil { t.Fatalf("run scenario polling-epoll: %v", err) } @@ -38,7 +40,7 @@ func TestPollingEpollTracepoints(t *testing.T) { func TestPollingEpollReadyCountInParquet(t *testing.T) { h := newTestHarness(t) h.WorkloadEnv = []string{pollingWorkloadStartupEnv} - path, pid, err := h.RunParquet("polling-epoll", pollingParquetDuration) + path, pid, err := h.RunParquetWithIorArgs("polling-epoll", pollingParquetDuration, pollingTraceArgs) if err != nil { t.Fatalf("run polling-epoll parquet scenario: %v", err) } diff --git a/integrationtests/process_test.go b/integrationtests/process_test.go index e9cd739..291187d 100644 --- a/integrationtests/process_test.go +++ b/integrationtests/process_test.go @@ -2,8 +2,10 @@ package integrationtests import "testing" +var processExecTraceArgs = []string{"-trace-syscalls", "execve,execveat"} + func TestProcessExecLifecycle(t *testing.T) { - result, _ := runScenarioResult(t, "process-exec-lifecycle", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "process-exec-lifecycle", []ExpectedEvent{ { Tracepoint: "enter_execve", PathContains: "ior-missing-execve-only", @@ -16,7 +18,7 @@ func TestProcessExecLifecycle(t *testing.T) { Comm: "ioworkload", MinCount: 1, }, - }) + }, processExecTraceArgs) assertEventDurationPositive(t, result, ExpectedEvent{ Tracepoint: "enter_execve", diff --git a/integrationtests/retbytes_test.go b/integrationtests/retbytes_test.go index c6f06d8..4baed9e 100644 --- a/integrationtests/retbytes_test.go +++ b/integrationtests/retbytes_test.go @@ -2,10 +2,12 @@ package integrationtests import "testing" +var retbytesTraceArgs = []string{"-trace-syscalls", "sendto,recvfrom,sendmsg,recvmsg,sendmmsg,recvmmsg,sendfile64,splice,tee,process_vm_writev,process_vm_readv,socketpair,pipe2,openat,write,read,close,lseek,fcntl,unlinkat,mkdirat,getdents64"} + func TestRetbytesPhaseA(t *testing.T) { const payloadLen = uint64(18) - result, _ := runScenarioResult(t, "retbytes-phase-a", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "retbytes-phase-a", []ExpectedEvent{ {Tracepoint: "enter_sendto", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_recvfrom", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_sendmsg", Comm: "ioworkload", MinCount: 1}, @@ -17,7 +19,7 @@ func TestRetbytesPhaseA(t *testing.T) { {Tracepoint: "enter_tee", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_process_vm_writev", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_process_vm_readv", Comm: "ioworkload", MinCount: 1}, - }) + }, retbytesTraceArgs) for _, tracepoint := range []string{ "enter_sendto", diff --git a/integrationtests/security_test.go b/integrationtests/security_test.go index a705a16..5b6e657 100644 --- a/integrationtests/security_test.go +++ b/integrationtests/security_test.go @@ -5,14 +5,16 @@ import ( "testing" ) +var securityTraceArgs = []string{"-trace-syscalls", "keyctl,add_key,request_key,ptrace,perf_event_open,close"} + func TestSecurityKeysPtracePerf(t *testing.T) { - result, _ := runScenarioResult(t, "security-keys-ptrace-perf", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "security-keys-ptrace-perf", []ExpectedEvent{ {Tracepoint: "enter_keyctl", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_add_key", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_request_key", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_ptrace", Comm: "ioworkload", MinCount: 1}, {Tracepoint: "enter_perf_event_open", Comm: "ioworkload", MinCount: 1}, - }) + }, securityTraceArgs) // Key and ptrace operations are not fd/path based and should stay untracked. assertTracepointPathPrefix(t, result, "enter_keyctl", "N:file") diff --git a/integrationtests/sleep_test.go b/integrationtests/sleep_test.go index a465420..fbd93a7 100644 --- a/integrationtests/sleep_test.go +++ b/integrationtests/sleep_test.go @@ -7,10 +7,12 @@ const ( sleepWorkloadStartupEnv = "IOR_WORKLOAD_STARTUP_DELAY_MS=1000" ) +var sleepTraceArgs = []string{"-trace-families", "Time"} + func TestSleepTracepoints(t *testing.T) { h := newTestHarness(t) h.WorkloadEnv = []string{sleepWorkloadStartupEnv} - result, pid, err := h.Run("sleep-syscalls", defaultDuration) + result, pid, err := h.RunWithIorArgs("sleep-syscalls", defaultDuration, sleepTraceArgs) if err != nil { t.Fatalf("run scenario sleep-syscalls: %v", err) } @@ -26,7 +28,7 @@ func TestSleepTracepoints(t *testing.T) { func TestSleepRequestedTimespecInParquet(t *testing.T) { h := newTestHarness(t) h.WorkloadEnv = []string{sleepWorkloadStartupEnv} - path, pid, err := h.RunParquet("sleep-syscalls", sleepParquetDuration) + path, pid, err := h.RunParquetWithIorArgs("sleep-syscalls", sleepParquetDuration, sleepTraceArgs) if err != nil { t.Fatalf("run sleep-syscalls parquet scenario: %v", err) } @@ -64,4 +66,3 @@ func TestSleepRequestedTimespecInParquet(t *testing.T) { t.Fatal("expected clock_nanosleep row with RequestedSleepNS=3000000") } } - diff --git a/integrationtests/socket_test.go b/integrationtests/socket_test.go index 059d339..a5cd4f6 100644 --- a/integrationtests/socket_test.go +++ b/integrationtests/socket_test.go @@ -5,8 +5,13 @@ import ( "testing" ) +var socketTraceArgs = []string{ + "-trace-syscalls", + "socket,socketpair,bind,listen,accept4,accept,connect,shutdown,getsockname,getpeername,setsockopt,getsockopt,close", +} + func TestSocketBasic(t *testing.T) { - result, _ := runScenarioResult(t, "socket-basic", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "socket-basic", []ExpectedEvent{ { Tracepoint: "enter_socket", MinCount: 1, @@ -15,14 +20,14 @@ func TestSocketBasic(t *testing.T) { Tracepoint: "enter_close", MinCount: 1, }, - }) + }, socketTraceArgs) assertTracepointPathPrefix(t, result, "enter_socket", "socket:1:") assertTracepointPathPrefix(t, result, "enter_close", "socket:1:") } func TestSocketpairBasic(t *testing.T) { - result, _ := runScenarioResult(t, "socketpair-basic", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "socketpair-basic", []ExpectedEvent{ { Tracepoint: "enter_socketpair", MinCount: 1, @@ -31,7 +36,7 @@ func TestSocketpairBasic(t *testing.T) { Tracepoint: "enter_close", MinCount: 2, }, - }) + }, socketTraceArgs) assertTracepointPathPrefix(t, result, "enter_socketpair", "socket:1:") if got := totalTracepointPathCount(result, "enter_close", "socket:1:"); got < 2 { @@ -40,13 +45,13 @@ func TestSocketpairBasic(t *testing.T) { } func TestSocketAcceptLifecycle(t *testing.T) { - result, _ := runScenarioResult(t, "socket-accept-lifecycle", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "socket-accept-lifecycle", []ExpectedEvent{ {Tracepoint: "enter_bind", MinCount: 1}, {Tracepoint: "enter_connect", MinCount: 1}, {Tracepoint: "enter_listen", MinCount: 1}, {Tracepoint: "enter_accept4", MinCount: 1}, {Tracepoint: "enter_shutdown", MinCount: 1}, - }) + }, socketTraceArgs) assertTracepointPathPrefix(t, result, "enter_bind", "socket:1:") assertTracepointPathPrefix(t, result, "enter_connect", "socket:1:") @@ -56,13 +61,13 @@ func TestSocketAcceptLifecycle(t *testing.T) { } func TestSocketAcceptLifecyclePlain(t *testing.T) { - result, _ := runScenarioResult(t, "socket-accept-lifecycle-plain", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "socket-accept-lifecycle-plain", []ExpectedEvent{ {Tracepoint: "enter_bind", MinCount: 1}, {Tracepoint: "enter_connect", MinCount: 1}, {Tracepoint: "enter_listen", MinCount: 1}, {Tracepoint: "enter_accept", MinCount: 1}, {Tracepoint: "enter_shutdown", MinCount: 1}, - }) + }, socketTraceArgs) assertTracepointPathPrefix(t, result, "enter_bind", "socket:1:") assertTracepointPathPrefix(t, result, "enter_connect", "socket:1:") @@ -76,12 +81,12 @@ func TestSocketAcceptLifecyclePlain(t *testing.T) { } func TestSocketIntrospection(t *testing.T) { - result, _ := runScenarioResult(t, "socket-introspection", []ExpectedEvent{ + result, _ := runScenarioResultWithIorArgs(t, "socket-introspection", []ExpectedEvent{ {Tracepoint: "enter_getsockname", MinCount: 1}, {Tracepoint: "enter_getpeername", MinCount: 1}, {Tracepoint: "enter_setsockopt", MinCount: 1}, {Tracepoint: "enter_getsockopt", MinCount: 1}, - }) + }, socketTraceArgs) assertTracepointPathPrefix(t, result, "enter_getsockname", "socket:1:") assertTracepointPathPrefix(t, result, "enter_getpeername", "socket:1:") -- cgit v1.2.3