Skip to content

Commit cb60f48

Browse files
authored
gh-150114: Use get_process_memory_usage() in memory watchdog (#150402)
In practice, this change adds Windows, FreeBSD and macOS support to the memory watchdog.
1 parent 5cb54c9 commit cb60f48

2 files changed

Lines changed: 43 additions & 33 deletions

File tree

Lib/test/memory_watchdog.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
11
"""Memory watchdog: periodically read the memory usage of the main test process
22
and print it out, until terminated."""
3-
# stdin should refer to the process' /proc/<PID>/statm: we don't pass the
4-
# process' PID to avoid a race condition in case of - unlikely - PID recycling.
5-
# If the process crashes, reading from the /proc entry will fail with ESRCH.
63

74

85
import sys
96
import time
10-
from test.support import get_pagesize
11-
12-
13-
while True:
14-
page_size = get_pagesize()
15-
sys.stdin.seek(0)
16-
statm = sys.stdin.read()
17-
data = int(statm.split()[5])
18-
sys.stdout.write(" ... process data size: {data:.1f}G\n"
19-
.format(data=data * page_size / (1024 ** 3)))
20-
sys.stdout.flush()
21-
time.sleep(1)
7+
from test.libregrtest.utils import get_process_memory_usage
8+
9+
10+
ONE_GIB = (1024 ** 3)
11+
12+
13+
def watchdog(pid):
14+
while True:
15+
mem = get_process_memory_usage(pid)
16+
if mem is None:
17+
# get_process_memory_usage() is not supported on the platform,
18+
# or something went wrong. Exit since the next call is likely to
19+
# fail the same way.
20+
return
21+
22+
# Prefer sys.stdout.write() to print() to use a single write() syscall.
23+
# print(msg) calls write(msg.encode()) and then write(b"\n").
24+
sys.stdout.write(f" ... process data size: {mem / ONE_GIB:.1f} GiB\n")
25+
sys.stdout.flush()
26+
time.sleep(1)
27+
28+
def main():
29+
if len(sys.argv) != 2:
30+
print(f"usage: python {sys.argv[0]} pid")
31+
sys.exit(1)
32+
pid = int(sys.argv[1])
33+
34+
try:
35+
watchdog(pid)
36+
except KeyboardInterrupt:
37+
pass
38+
39+
if __name__ == "__main__":
40+
main()

Lib/test/support/__init__.py

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,29 +1241,20 @@ class _MemoryWatchdog:
12411241
"""
12421242

12431243
def __init__(self):
1244-
self.procfile = '/proc/{pid}/statm'.format(pid=os.getpid())
12451244
self.started = False
12461245

12471246
def start(self):
1248-
try:
1249-
f = open(self.procfile, 'r')
1250-
except OSError as e:
1251-
logging.getLogger(__name__).warning('/proc not available for stats: %s', e, exc_info=e)
1252-
sys.stderr.flush()
1253-
return
1254-
12551247
import subprocess
1256-
with f:
1257-
watchdog_script = findfile("memory_watchdog.py")
1258-
self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script],
1259-
stdin=f,
1260-
stderr=subprocess.DEVNULL)
1248+
watchdog_script = findfile("memory_watchdog.py")
1249+
cmd = [sys.executable, watchdog_script, str(os.getpid())]
1250+
self.mem_watchdog = subprocess.Popen(cmd)
12611251
self.started = True
12621252

12631253
def stop(self):
1264-
if self.started:
1265-
self.mem_watchdog.terminate()
1266-
self.mem_watchdog.wait()
1254+
if not self.started:
1255+
return
1256+
self.mem_watchdog.terminate()
1257+
self.mem_watchdog.wait()
12671258

12681259

12691260
def bigmemtest(size, memuse, dry_run=True):
@@ -1296,8 +1287,8 @@ def wrapper(self):
12961287

12971288
if real_max_memuse and verbose:
12981289
print()
1299-
print(" ... expected peak memory use: {peak:.1f}G"
1300-
.format(peak=size * memuse / (1024 ** 3)))
1290+
peak = (size * memuse) / (1024 ** 3)
1291+
print(f" ... expected peak memory use: {peak:.1f} GiB")
13011292
watchdog = _MemoryWatchdog()
13021293
watchdog.start()
13031294
else:

0 commit comments

Comments
 (0)