mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
75 lines
3.0 KiB
Python
Executable File
75 lines
3.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""Sample a launched Linux process without adding a sampler to Flectar's RAM."""
|
||
import argparse
|
||
import csv
|
||
import os
|
||
from pathlib import Path
|
||
import subprocess
|
||
import sys
|
||
import time
|
||
|
||
|
||
def sample(pid):
|
||
root = Path(f"/proc/{pid}")
|
||
status = {}
|
||
for line in (root / "status").read_text().splitlines():
|
||
key, _, value = line.partition(":")
|
||
if key in ("VmRSS", "VmHWM"):
|
||
status[key] = int(value.split()[0])
|
||
# The command name can contain spaces and parentheses.
|
||
fields = (root / "stat").read_text().rsplit(")", 1)[1].split()
|
||
ticks = int(fields[11]) + int(fields[12])
|
||
return status.get("VmRSS", 0), status.get("VmHWM", 0), ticks
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description=__doc__)
|
||
parser.add_argument("--renderer", choices=("cpu", "gpu"), default="cpu")
|
||
parser.add_argument("--output", type=Path, required=True)
|
||
parser.add_argument("--interval", type=float, default=0.1)
|
||
parser.add_argument("--disable-sync", action="store_true")
|
||
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 or not sys.platform.startswith("linux") or not 0.05 <= args.interval <= 10:
|
||
parser.error("Linux, a command after --, and an interval of 0.05–10 seconds are required")
|
||
env = os.environ.copy()
|
||
env["FLECTAR_RENDERER"] = args.renderer
|
||
env["FLECTAR_RENDER_TIMINGS"] = "1"
|
||
env.pop("FLECTAR_GPU_FALLBACK", None)
|
||
if args.disable_sync:
|
||
env["FLECTAR_BENCHMARK_DISABLE_SYNC"] = "1"
|
||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||
ticks_per_second = os.sysconf("SC_CLK_TCK")
|
||
started = time.monotonic()
|
||
previous = None
|
||
peak = 0
|
||
with args.output.open("w", newline="") as output:
|
||
writer = csv.writer(output)
|
||
writer.writerow(["elapsed_seconds", "requested_renderer", "rss_kib", "process_peak_rss_kib", "cpu_percent"])
|
||
process = subprocess.Popen(command, env=env)
|
||
print(f"Sampling PID {process.pid}; check FLECTAR_RENDERER logs for the actual active mode.", file=sys.stderr)
|
||
try:
|
||
while process.poll() is None:
|
||
now = time.monotonic()
|
||
try:
|
||
rss, high_water, ticks = sample(process.pid)
|
||
except (FileNotFoundError, ProcessLookupError):
|
||
break
|
||
cpu = 0 if previous is None else 100 * (ticks - previous[1]) / ticks_per_second / (now - previous[0])
|
||
previous = (now, ticks)
|
||
peak = max(peak, high_water)
|
||
writer.writerow([f"{now - started:.3f}", args.renderer, rss, high_water, f"{cpu:.2f}"])
|
||
output.flush()
|
||
time.sleep(args.interval)
|
||
except KeyboardInterrupt:
|
||
process.terminate()
|
||
finally:
|
||
code = process.wait()
|
||
print(f"Peak process RSS: {peak} KiB; samples: {args.output}", file=sys.stderr)
|
||
return code
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|