From 7a72e31c5e73648a924292c3d7fe0485a0c44f90 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 25 May 2026 16:18:25 +0200 Subject: [PATCH 1/4] gh-150114: Use get_process_memory_usage() in memory watchdog In practice, this change adds Windows and FreeBSD support to the memory watchdog. --- Lib/test/memory_watchdog.py | 39 +++++++++++++++++++++++++++--------- Lib/test/support/__init__.py | 27 +++++++++---------------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/Lib/test/memory_watchdog.py b/Lib/test/memory_watchdog.py index fee062ecc9b300..3ef6af592c11a2 100644 --- a/Lib/test/memory_watchdog.py +++ b/Lib/test/memory_watchdog.py @@ -8,14 +8,35 @@ import sys import time from test.support import get_pagesize +from test.libregrtest.utils import get_process_memory_usage -while True: - page_size = get_pagesize() - sys.stdin.seek(0) - statm = sys.stdin.read() - data = int(statm.split()[5]) - sys.stdout.write(" ... process data size: {data:.1f}G\n" - .format(data=data * page_size / (1024 ** 3))) - sys.stdout.flush() - time.sleep(1) +ONE_GIB = (1024 ** 3) + + +def watchdog(pid): + while True: + mem = get_process_memory_usage(pid) + if mem is None: + # get_process_memory_usage() is not supported on the platform, + # or something went wrong. Exit since the next call is likely to + # fail the same way. + return + + sys.stdout.write(f" ... process data size: {mem / ONE_GIB:.1f} GiB\n") + sys.stdout.flush() + time.sleep(1) + +def main(): + if len(sys.argv) != 2: + print("usage: python {sys.argv[0]) pid") + sys.exit(1) + pid = int(sys.argv[1]) + + try: + watchdog(pid) + except KeyboardInterrupt: + pass + +if __name__ == "__main__": + main() diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 2cac70f4ab2afb..87082ff37d1e58 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1241,29 +1241,20 @@ class _MemoryWatchdog: """ def __init__(self): - self.procfile = '/proc/{pid}/statm'.format(pid=os.getpid()) self.started = False def start(self): - try: - f = open(self.procfile, 'r') - except OSError as e: - logging.getLogger(__name__).warning('/proc not available for stats: %s', e, exc_info=e) - sys.stderr.flush() - return - import subprocess - with f: - watchdog_script = findfile("memory_watchdog.py") - self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script], - stdin=f, - stderr=subprocess.DEVNULL) + watchdog_script = findfile("memory_watchdog.py") + cmd = [sys.executable, watchdog_script, str(os.getpid())] + self.mem_watchdog = subprocess.Popen(cmd) self.started = True def stop(self): - if self.started: - self.mem_watchdog.terminate() - self.mem_watchdog.wait() + if not self.started: + return + self.mem_watchdog.terminate() + self.mem_watchdog.wait() def bigmemtest(size, memuse, dry_run=True): @@ -1296,8 +1287,8 @@ def wrapper(self): if real_max_memuse and verbose: print() - print(" ... expected peak memory use: {peak:.1f}G" - .format(peak=size * memuse / (1024 ** 3))) + peak = (size * memuse) / (1024 ** 3) + print(f" ... expected peak memory use: {peak:.1f} GiB") watchdog = _MemoryWatchdog() watchdog.start() else: From 26ef459a162d46c083b0348331bd86cdacd74c6d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 25 May 2026 16:52:31 +0200 Subject: [PATCH 2/4] Fix Lint: remove now unused import (get_pagesize) --- Lib/test/memory_watchdog.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/memory_watchdog.py b/Lib/test/memory_watchdog.py index 3ef6af592c11a2..9a4f53c141303a 100644 --- a/Lib/test/memory_watchdog.py +++ b/Lib/test/memory_watchdog.py @@ -7,7 +7,6 @@ import sys import time -from test.support import get_pagesize from test.libregrtest.utils import get_process_memory_usage From f5234532a3042989f8f31b50f9e20eaed11972dc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 25 May 2026 22:03:00 +0200 Subject: [PATCH 3/4] Fix memory_watchdog.py usage Remove also outdated comment. --- Lib/test/memory_watchdog.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Lib/test/memory_watchdog.py b/Lib/test/memory_watchdog.py index 9a4f53c141303a..0ca7f8d3ce49ec 100644 --- a/Lib/test/memory_watchdog.py +++ b/Lib/test/memory_watchdog.py @@ -1,8 +1,5 @@ """Memory watchdog: periodically read the memory usage of the main test process and print it out, until terminated.""" -# stdin should refer to the process' /proc//statm: we don't pass the -# process' PID to avoid a race condition in case of - unlikely - PID recycling. -# If the process crashes, reading from the /proc entry will fail with ESRCH. import sys @@ -28,7 +25,7 @@ def watchdog(pid): def main(): if len(sys.argv) != 2: - print("usage: python {sys.argv[0]) pid") + print(f"usage: python {sys.argv[0]} pid") sys.exit(1) pid = int(sys.argv[1]) From 3a13b66b3eecc3b127309c9e10d635dfd125a9aa Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 25 May 2026 22:04:51 +0200 Subject: [PATCH 4/4] Add a comment to explain why sys.stdout.write() is used --- Lib/test/memory_watchdog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/memory_watchdog.py b/Lib/test/memory_watchdog.py index 0ca7f8d3ce49ec..4a3f66e1f822ba 100644 --- a/Lib/test/memory_watchdog.py +++ b/Lib/test/memory_watchdog.py @@ -19,6 +19,8 @@ def watchdog(pid): # fail the same way. return + # Prefer sys.stdout.write() to print() to use a single write() syscall. + # print(msg) calls write(msg.encode()) and then write(b"\n"). sys.stdout.write(f" ... process data size: {mem / ONE_GIB:.1f} GiB\n") sys.stdout.flush() time.sleep(1)