#!/usr/bin/env python3
"""
proc_perf.py — Snapshot a live process's CPU time and peak memory from /proc.

Usage:  proc_perf.py <pid> <label> <out.json>

Reads /proc/<pid>/stat (utime+stime, in clock ticks) and /proc/<pid>/status
(VmHWM = peak resident set, VmRSS = current) *while the process is alive* — peak
RSS is only legible before the process exits, so the orchestrator calls this just
before tearing the stack down. Emits a small JSON record:

    {"label": ..., "avail": true, "cpu_s": float, "peak_rss_kb": int,
     "rss_kb": int, "threads": int}

If the process is already gone, writes {"label": ..., "avail": false}.
"""
import json
import os
import sys


def _clk_tck():
    try:
        return os.sysconf("SC_CLK_TCK") or 100
    except (ValueError, OSError):
        return 100


def snapshot(pid):
    stat_p = f"/proc/{pid}/stat"
    status_p = f"/proc/{pid}/status"
    if not os.path.exists(stat_p):
        return {"avail": False}
    try:
        with open(stat_p) as f:
            raw = f.read()
        # The comm field is parenthesised and may contain spaces/parens, so split
        # on the final ')': everything after it is space-separated, with state as
        # the first token. utime/stime are overall fields 14/15 → indices 11/12
        # of the post-')' tokens (pid + comm consumed before the split).
        after = raw[raw.rindex(")") + 1:].split()
        utime = int(after[11])
        stime = int(after[12])
        cpu_s = (utime + stime) / float(_clk_tck())
        rec: dict = {"avail": True, "cpu_s": cpu_s}
        with open(status_p) as f:
            for line in f:
                if line.startswith("VmHWM:"):
                    rec["peak_rss_kb"] = int(line.split()[1])
                elif line.startswith("VmRSS:"):
                    rec["rss_kb"] = int(line.split()[1])
                elif line.startswith("Threads:"):
                    rec["threads"] = int(line.split()[1])
        return rec
    except (OSError, ValueError, IndexError):
        return {"avail": False}


def main():
    if len(sys.argv) != 4:
        print("usage: proc_perf.py <pid> <label> <out.json>", file=sys.stderr)
        sys.exit(2)
    pid, label, out = sys.argv[1], sys.argv[2], sys.argv[3]
    rec = snapshot(pid)
    rec["label"] = label
    with open(out, "w") as f:
        json.dump(rec, f)
    if rec.get("avail"):
        print(f"perf {label}: cpu={rec.get('cpu_s', 0):.2f}s "
              f"peakRSS={rec.get('peak_rss_kb', 0)/1024:.1f}MB")
    else:
        print(f"perf {label}: unavailable (process gone)")


if __name__ == "__main__":
    main()
