#!/usr/bin/env python3 import os import signal import subprocess import sys import time if len(sys.argv) < 4 or sys.argv[2] != "--": raise SystemExit("usage: MeasureProcess.py LOG -- COMMAND [ARGS...]") log_path = sys.argv[1] command = sys.argv[3:] os.makedirs(os.path.dirname(log_path) or ".", exist_ok=True) start = time.monotonic() with open(log_path, "wb") as log_file: process = subprocess.Popen(command, stdout=log_file, stderr=subprocess.STDOUT, start_new_session=True) max_hwm_kib = 0 max_rss_kib = 0 max_pid = process.pid max_name = "" deadline = start + 1800.0 while process.poll() is None: if time.monotonic() >= deadline: os.killpg(process.pid, signal.SIGTERM) try: process.wait(timeout=10) except subprocess.TimeoutExpired: os.killpg(process.pid, signal.SIGKILL) process.wait() print("MONITOR_TIMEOUT_SECONDS=1800", file=log_file, flush=True) break stat_by_pid = {} for entry in os.scandir("/proc"): if not entry.name.isdigit(): continue pid = int(entry.name) try: with open(f"/proc/{pid}/stat", "r", encoding="ascii") as stat_file: fields = stat_file.read().rsplit(")", 1)[1].split() ppid = int(fields[1]) stat_by_pid[pid] = ppid except (OSError, ValueError, IndexError): continue descendants = {process.pid} changed = True while changed: changed = False for pid, ppid in stat_by_pid.items(): if ppid in descendants and pid not in descendants: descendants.add(pid) changed = True for pid in descendants: try: with open(f"/proc/{pid}/status", "r", encoding="ascii") as status_file: status = status_file.read().splitlines() values = {line.split(":", 1)[0]: line.split(":", 1)[1].strip() for line in status if ":" in line} hwm = int(values.get("VmHWM", "0 kB").split()[0]) rss = int(values.get("VmRSS", "0 kB").split()[0]) name = values.get("Name", "") except (OSError, ValueError, IndexError): continue if hwm > max_hwm_kib: max_hwm_kib, max_pid, max_name = hwm, pid, name max_rss_kib = max(max_rss_kib, rss) time.sleep(0.05) result = process.wait() elapsed = time.monotonic() - start for line in ( f"MONITOR_EXIT_CODE={result}", f"MONITOR_WALL_SECONDS={elapsed:.3f}", f"MONITOR_PEAK_PROCESS_HWM_KIB={max_hwm_kib}", f"MONITOR_PEAK_PROCESS_PID={max_pid}", f"MONITOR_PEAK_PROCESS_NAME={max_name}", f"MONITOR_MAX_SAMPLED_PROCESS_RSS_KIB={max_rss_kib}", ): log_file.write((line + "\n").encode("utf-8")) raise SystemExit(result)