From a3254910884627f8ee62503dfa0de31c9b136c41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:39:38 +0000 Subject: [PATCH 1/4] Initial plan From f03210c39f777b8ab90c668631d0a719b6680930 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:51:40 +0000 Subject: [PATCH 2/4] Add pread/pwrite support and investigate mmap usage Co-authored-by: mtauraso <31012+mtauraso@users.noreply.github.com> --- src/iops_profiler/collector.py | 33 ++++++++++++++++++++++++++++----- tests/test_parsing.py | 14 ++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/iops_profiler/collector.py b/src/iops_profiler/collector.py index 47d8348..43f968c 100644 --- a/src/iops_profiler/collector.py +++ b/src/iops_profiler/collector.py @@ -62,8 +62,9 @@ def __init__(self, shell): self._strace_pattern = re.compile(r"^\s*(\d+)\s+(\w+)\([^)]+\)\s*=\s*(-?\d+)") # Pattern matches: B=0x[hex] in fs_usage output self._fs_usage_byte_pattern = re.compile(FS_USAGE_BYTE_PATTERN) - # Set of syscall names for I/O operations (lowercase) - self._io_syscalls = set(STRACE_IO_SYSCALLS) + # Set of syscall names for I/O operations (lowercase) - includes 32-bit variants + self._io_syscalls = set(STRACE_IO_SYSCALLS + ["pread", "pwrite"]) + @staticmethod def parse_fs_usage_line_static(line, byte_pattern=None, collect_ops=False): @@ -380,8 +381,9 @@ def measure_linux_strace(self, code, collect_ops=False): output_file = tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False).name # noqa: SIM115 try: - # Start strace in the background - syscalls_to_trace = ",".join(STRACE_IO_SYSCALLS) + # Try with all syscalls first, including 32-bit variants + syscalls_to_try = STRACE_IO_SYSCALLS + ["pread", "pwrite"] + syscalls_to_trace = ",".join(syscalls_to_try) strace_cmd = [ "strace", "-f", # Follow forks @@ -408,7 +410,28 @@ def measure_linux_strace(self, code, collect_ops=False): raise RuntimeError( "strace failed - ptrace not permitted. This may be due to kernel security settings." ) - raise RuntimeError(f"Failed to start strace: {stderr}") + # If it failed due to invalid syscall, retry without 32-bit variants + if "invalid system call" in stderr: + syscalls_to_trace = ",".join(STRACE_IO_SYSCALLS) + strace_cmd = [ + "strace", + "-f", + "-e", + f"trace={syscalls_to_trace}", + "-o", + output_file, + "-p", + str(pid), + ] + strace_proc = subprocess.Popen( + strace_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + time.sleep(STRACE_ATTACH_DELAY) + if strace_proc.poll() is not None: + stdout, stderr = strace_proc.communicate() + raise RuntimeError(f"Failed to start strace: {stderr}") + else: + raise RuntimeError(f"Failed to start strace: {stderr}") # Execute the code start_time = time.time() diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 58b1ffd..c403f8a 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -61,6 +61,20 @@ def test_pwrite64_operation(self, profiler): assert op_type == "write" assert bytes_transferred == 512 + def test_pread_operation(self, profiler): + """Test parsing a pread operation (32-bit)""" + line = '3385 pread(3, "...", 1024, 0) = 1024' + op_type, bytes_transferred = profiler.collector.parse_strace_line(line) + assert op_type == "read" + assert bytes_transferred == 1024 + + def test_pwrite_operation(self, profiler): + """Test parsing a pwrite operation (32-bit)""" + line = '3385 pwrite(4, "data", 512, 1024) = 512' + op_type, bytes_transferred = profiler.collector.parse_strace_line(line) + assert op_type == "write" + assert bytes_transferred == 512 + def test_readv_operation(self, profiler): """Test parsing a readv (vectored read) operation""" line = '3385 readv(5, [{iov_base="...", iov_len=1024}], 1) = 1024' From 7ff91b232b8e95928b0d6b40055f6d51bd9f595c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:57:11 +0000 Subject: [PATCH 3/4] Refactor strace startup logic to reduce code duplication Co-authored-by: mtauraso <31012+mtauraso@users.noreply.github.com> --- src/iops_profiler/collector.py | 35 +++++++++++----------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/iops_profiler/collector.py b/src/iops_profiler/collector.py index 43f968c..594629f 100644 --- a/src/iops_profiler/collector.py +++ b/src/iops_profiler/collector.py @@ -380,10 +380,9 @@ def measure_linux_strace(self, code, collect_ops=False): # Create temporary file for strace output - we need the name, not file handle output_file = tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False).name # noqa: SIM115 - try: - # Try with all syscalls first, including 32-bit variants - syscalls_to_try = STRACE_IO_SYSCALLS + ["pread", "pwrite"] - syscalls_to_trace = ",".join(syscalls_to_try) + def _start_strace(syscalls_list): + """Helper to start strace with given syscall list""" + syscalls_to_trace = ",".join(syscalls_list) strace_cmd = [ "strace", "-f", # Follow forks @@ -394,14 +393,16 @@ def measure_linux_strace(self, code, collect_ops=False): "-p", str(pid), ] - - # Start strace process - strace_proc = subprocess.Popen( + proc = subprocess.Popen( strace_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) - - # Give strace a moment to attach time.sleep(STRACE_ATTACH_DELAY) + return proc + + try: + # Try with all syscalls first, including 32-bit variants + syscalls_to_try = STRACE_IO_SYSCALLS + ["pread", "pwrite"] + strace_proc = _start_strace(syscalls_to_try) # Check if strace started successfully if strace_proc.poll() is not None: @@ -412,21 +413,7 @@ def measure_linux_strace(self, code, collect_ops=False): ) # If it failed due to invalid syscall, retry without 32-bit variants if "invalid system call" in stderr: - syscalls_to_trace = ",".join(STRACE_IO_SYSCALLS) - strace_cmd = [ - "strace", - "-f", - "-e", - f"trace={syscalls_to_trace}", - "-o", - output_file, - "-p", - str(pid), - ] - strace_proc = subprocess.Popen( - strace_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True - ) - time.sleep(STRACE_ATTACH_DELAY) + strace_proc = _start_strace(STRACE_IO_SYSCALLS) if strace_proc.poll() is not None: stdout, stderr = strace_proc.communicate() raise RuntimeError(f"Failed to start strace: {stderr}") From 2ee43b974bbc2d74f60d45dfccedf597ebd009af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:59:36 +0000 Subject: [PATCH 4/4] Fix retry logic and add clarifying comments Co-authored-by: mtauraso <31012+mtauraso@users.noreply.github.com> --- src/iops_profiler/collector.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/iops_profiler/collector.py b/src/iops_profiler/collector.py index 594629f..f976e57 100644 --- a/src/iops_profiler/collector.py +++ b/src/iops_profiler/collector.py @@ -62,10 +62,14 @@ def __init__(self, shell): self._strace_pattern = re.compile(r"^\s*(\d+)\s+(\w+)\([^)]+\)\s*=\s*(-?\d+)") # Pattern matches: B=0x[hex] in fs_usage output self._fs_usage_byte_pattern = re.compile(FS_USAGE_BYTE_PATTERN) - # Set of syscall names for I/O operations (lowercase) - includes 32-bit variants + # Set of syscall names for I/O operations (lowercase) + # Includes 32-bit variants (pread, pwrite) for completeness. + # Note: On 64-bit systems, these may not be traced (they don't exist), + # but including them in the parser is harmless - they simply won't appear in output. self._io_syscalls = set(STRACE_IO_SYSCALLS + ["pread", "pwrite"]) + @staticmethod def parse_fs_usage_line_static(line, byte_pattern=None, collect_ops=False): """Parse a single fs_usage output line for I/O operations (static version) @@ -414,9 +418,11 @@ def _start_strace(syscalls_list): # If it failed due to invalid syscall, retry without 32-bit variants if "invalid system call" in stderr: strace_proc = _start_strace(STRACE_IO_SYSCALLS) + # Check if retry succeeded if strace_proc.poll() is not None: stdout, stderr = strace_proc.communicate() - raise RuntimeError(f"Failed to start strace: {stderr}") + raise RuntimeError(f"Failed to start strace after retry: {stderr}") + # Retry succeeded - continue with execution else: raise RuntimeError(f"Failed to start strace: {stderr}")