diff options
| author | godosa <godosa@godosa.eu> | 2026-10-06 23:39:36 +0200 |
|---|---|---|
| committer | godosa <godosa@godosa.eu> | 2026-10-06 23:39:36 +0200 |
| commit | 39066900773e7857faf2d02e7ed51b71d219d97d (patch) | |
| tree | ffab8e4ddd971626776c3b1f4ce1da2c186c2187 /scripts/thread_cpu.py | |
| download | godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.tar.gz godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.zip | |
godosa-engine: initial public history
Diffstat (limited to 'scripts/thread_cpu.py')
| -rwxr-xr-x | scripts/thread_cpu.py | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/scripts/thread_cpu.py b/scripts/thread_cpu.py new file mode 100755 index 0000000..f22ad51 --- /dev/null +++ b/scripts/thread_cpu.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Per-thread CPU of a process over an interval, grouped by thread name (Linux /proc). +Shows where a smoke run's CPU goes: UI/render thread, audio thread, OpenAL mixer, +compositor, GC... (smoke_run.sh step `threads:<s>`). + +Usage: scripts/thread_cpu.py PID SECONDS [--top N] +""" +import argparse +import os +import time +from collections import defaultdict + + +def sample(pid): + """{tid: (name, cpu_ticks)} for every live thread of pid.""" + out = {} + base = f"/proc/{pid}/task" + for tid in os.listdir(base): + try: + with open(f"{base}/{tid}/stat") as f: + stat = f.read() + except OSError: + continue # thread ended + name = stat[stat.index("(") + 1: stat.rindex(")")] + fields = stat[stat.rindex(")") + 2:].split() + out[tid] = (name, int(fields[11]) + int(fields[12])) # utime + stime + return out + + +def per_name(before, after, seconds, hz): + """[(name, percent of one core, thread count)] busiest first; threads that started + during the interval count from zero.""" + total = defaultdict(float) + count = defaultdict(int) + for tid, (name, ticks) in after.items(): + start = before.get(tid, (name, 0))[1] + total[name] += (ticks - start) * 100.0 / hz / seconds + count[name] += 1 + return sorted(((n, total[n], count[n]) for n in total), key=lambda r: -r[1]) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("pid", type=int) + ap.add_argument("seconds", type=float) + ap.add_argument("--top", type=int, default=10) + args = ap.parse_args() + hz = os.sysconf("SC_CLK_TCK") + a = sample(args.pid) + time.sleep(args.seconds) + b = sample(args.pid) + rows = per_name(a, b, args.seconds, hz) + print(f"threads: {sum(r[1] for r in rows):.1f}% total over {args.seconds:g}s") + for name, pct, n in rows[: args.top]: + if pct >= 0.05: + print(f" {pct:5.1f}% {name}" + (f" ×{n}" if n > 1 else "")) + + +if __name__ == "__main__": + main() |
