90 lines
2.0 KiB
Python
90 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Ukur CPU + output pty binary TUI tia (Phase 0).
|
|
|
|
Dipakai untuk spike T-0.4 (docs/spikes/T-0.4-animasi.md):
|
|
fase 1 animasi 30 FPS menyala, fase 2 setelah tombol 'a' (reduced-motion).
|
|
|
|
Pakai:
|
|
python3 scripts/measure-cpu.py [./tia-love] [detik-per-fase]
|
|
"""
|
|
import fcntl
|
|
import os
|
|
import pty
|
|
import signal
|
|
import struct
|
|
import sys
|
|
import termios
|
|
import threading
|
|
import time
|
|
|
|
BIN = sys.argv[1] if len(sys.argv) > 1 else "./tia-love"
|
|
SECS = float(sys.argv[2]) if len(sys.argv) > 2 else 8.0
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
pid, fd = pty.fork()
|
|
if pid == 0:
|
|
os.chdir(ROOT)
|
|
os.execvp("timeout", ["timeout", str(int(SECS * 2 + 10)), BIN])
|
|
fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", 40, 120, 0, 0))
|
|
|
|
nbytes = [0]
|
|
|
|
|
|
def drain():
|
|
while True:
|
|
try:
|
|
b = os.read(fd, 65536)
|
|
except OSError:
|
|
return
|
|
if not b:
|
|
return
|
|
nbytes[0] += len(b)
|
|
|
|
|
|
threading.Thread(target=drain, daemon=True).start()
|
|
|
|
|
|
def app_pid():
|
|
"""PID anak (binary tia), bukan PID `timeout`."""
|
|
for _ in range(50):
|
|
try:
|
|
kids = open(f"/proc/{pid}/task/{pid}/children").read().split()
|
|
if kids:
|
|
return int(kids[0])
|
|
except FileNotFoundError:
|
|
pass
|
|
time.sleep(0.1)
|
|
return pid
|
|
|
|
|
|
apid = app_pid()
|
|
print("monitoring PID:", apid)
|
|
|
|
HZ = os.sysconf("SC_CLK_TCK")
|
|
|
|
|
|
def cpu():
|
|
p = open(f"/proc/{apid}/stat").read().split()
|
|
return (int(p[13]) + int(p[14])) / HZ
|
|
|
|
|
|
def phase(label):
|
|
b0, c0, t0 = nbytes[0], cpu(), time.time()
|
|
time.sleep(SECS)
|
|
b1, c1, t1 = nbytes[0], cpu(), time.time()
|
|
print(f"{label:<38} CPU {100 * (c1 - c0) / (t1 - t0):5.2f}% · output pty {(b1 - b0) / (t1 - t0) / 1024:5.1f} KB/s")
|
|
|
|
|
|
time.sleep(3) # warmup
|
|
phase("FASE 1 (animasi 30fps)")
|
|
os.write(fd, b"t") # picu toast
|
|
time.sleep(3.5)
|
|
os.write(fd, b"a") # reduced-motion
|
|
time.sleep(1)
|
|
phase("FASE 2 (reduced-motion)")
|
|
try:
|
|
os.kill(pid, signal.SIGKILL)
|
|
except ProcessLookupError:
|
|
pass
|
|
print("selesai")
|