File size: 2,726 Bytes
e2e6c43 aae9a70 e2e6c43 aae9a70 e2e6c43 aae9a70 e2e6c43 aae9a70 e2e6c43 f1303ff aae9a70 e2e6c43 aae9a70 e2e6c43 aae9a70 e2e6c43 aae9a70 e2e6c43 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | #!/usr/bin/env python3
"""Sample process-tree RSS/swap while running a command."""
import argparse
import json
import signal
import subprocess
import sys
import time
def process_tree(root):
output = subprocess.check_output(["ps", "-e", "-o", "pid=,ppid="], text=True)
children = {}
for line in output.splitlines():
pid, ppid = map(int, line.split())
children.setdefault(ppid, []).append(pid)
result, todo = [], [root]
while todo:
pid = todo.pop()
result.append(pid)
todo.extend(children.get(pid, ()))
return result
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--interval", type=float, default=0.25)
parser.add_argument("--timeout", type=float, default=7200)
parser.add_argument("--rss-limit-gib", type=float)
parser.add_argument("command", nargs=argparse.REMAINDER)
args = parser.parse_args()
command = args.command[1:] if args.command[:1] == ["--"] else args.command
if not command:
parser.error("missing command")
started = time.monotonic()
process = subprocess.Popen(command)
peak_rss = peak_swap = 0
exceeded = False
while process.poll() is None:
rss = swap = 0
for pid in process_tree(process.pid):
try:
with open(f"/proc/{pid}/status", encoding="ascii") as status:
for line in status:
if line.startswith("VmRSS:"):
rss += int(line.split()[1])
elif line.startswith("VmSwap:"):
swap += int(line.split()[1])
except (FileNotFoundError, ProcessLookupError):
pass
peak_rss = max(peak_rss, rss)
peak_swap = max(peak_swap, swap)
# VmRSS 以 kB 為單位,門檻須換算成 kB(1 GiB = 1024 * 1024 kB)
if args.rss_limit_gib is not None and rss > args.rss_limit_gib * (1024 ** 2):
exceeded = True
process.send_signal(signal.SIGKILL)
break
if time.monotonic() - started > args.timeout:
process.kill()
break
time.sleep(args.interval)
result = process.wait()
print("RUNMEM " + json.dumps({
"peak_kb": peak_rss,
"peak_gib": round(peak_rss / 1048576, 3),
"limit_gib": args.rss_limit_gib,
"limit_exceeded": exceeded,
"swap_kb": peak_swap,
"wall_s": round(time.monotonic() - started, 2),
"exit": result,
"cmd": command,
"method": "procfs-rss-watchdog",
}))
return 0 if args.rss_limit_gib is None else (1 if exceeded else result)
if __name__ == "__main__":
raise SystemExit(main())
|