Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions Lib/test/support/strace_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,27 @@ def sections(self):
return sections


def filter_memory(syscalls):
"""Filter out memory allocation calls from File I/O calls.

Some calls (mmap, munmap, etc) can be used on files or to just get a block
of memory. Use this function to filter out the memory related calls from
other calls."""

def _filter(call):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this function at the module level? Maybe rename it to _filter_memory_call().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can move, my thought initially was "Track the address mmap returns through munmap" and the set/dictionary of "known addresses" would be in the outer scope, but I don't think mmap in io is likely (there's the mmap module if it's better for a particular use case)

# mmap can operate on a fd or "MAP_ANON" which gives a block of memory.
# Ignore the "MAP_ANON" ones.
if call.syscall == "mmap" and "MAP_ANON" in call.args[3]:
return False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to return True if the syscall should be filtered, and False otherwise? In short, replace if _filter(call) with if not _filter(call).


if call.syscall in ("munmap", "mprotect"):
return False

return True

return [call for call in syscalls if _filter(call)]


@support.requires_subprocess()
def strace_python(code, strace_flags, check=True):
"""Run strace and return the trace.
Expand All @@ -93,8 +114,6 @@ def _make_error(reason, details):
"-c",
textwrap.dedent(code),
__run_using_command=[_strace_binary] + strace_flags,
# Don't want to trace our JIT's own mmap and mprotect calls:
PYTHON_JIT="0",
)
except OSError as err:
return _make_error("Caught OSError", err)
Expand Down Expand Up @@ -145,9 +164,14 @@ def get_events(code, strace_flags, prelude, cleanup):
return all_sections['code']


def get_syscalls(code, strace_flags, prelude="", cleanup=""):
def get_syscalls(code, strace_flags, prelude="", cleanup="",
ignore_memory=True):
"""Get the syscalls which a given chunk of python code generates"""
events = get_events(code, strace_flags, prelude=prelude, cleanup=cleanup)

if ignore_memory:
events = filter_memory(events)

return [ev.syscall for ev in events]


Expand Down Expand Up @@ -177,5 +201,5 @@ def requires_strace():
return unittest.skipUnless(_can_strace(), "Requires working strace")


__all__ = ["get_events", "get_syscalls", "requires_strace", "strace_python",
"StraceEvent", "StraceResult"]
__all__ = ["filter_memory", "get_events", "get_syscalls", "requires_strace",
"strace_python", "StraceEvent", "StraceResult"]
8 changes: 6 additions & 2 deletions Lib/test/test_fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,7 @@ def testErrnoOnClosedReadinto(self, f):

@strace_helper.requires_strace()
def test_syscalls_read(self):
"""Check that the set of system calls produced by the I/O stack is what
is expected for various read cases.
"""Check set of system calls during common I/O patterns

It's expected as bits of the I/O implementation change, this will need
to change. The goal is to catch changes that unintentionally add
Expand All @@ -383,6 +382,11 @@ def check_readall(name, code, prelude="", cleanup="",
prelude=prelude,
cleanup=cleanup)

# Some system calls (ex. mmap) can be used for both File I/O and
# memory allocation. Filter out the ones used for memory
# allocation.
syscalls = strace_helper.filter_memory(syscalls)

# The first call should be an open that returns a
# file descriptor (fd). Afer that calls may vary. Once the file
# is opened, check calls refer to it by fd as the filename
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Filter out memory-related ``mmap``, ``munmap``, and ``mprotect`` calls from
file-related ones when testing :mod:`io` behavior using strace.
Loading