| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- #!/usr/bin/env python3
- """Observe one owned benchmark child on Linux, without a wall-clock timeout.
- Usage: python3 observe-index.py NEW_RUN_DIR -- node measure-index.cjs ENGINE FIXTURE NEW_RUN_DIR native
- The child retains the caller's environment/affinity. No host settings are changed.
- """
- import json
- import os
- from pathlib import Path
- import subprocess
- import sys
- import time
- def read(path):
- try:
- return Path(path).read_text()
- except (FileNotFoundError, PermissionError, ProcessLookupError):
- return None
- def sample(pid):
- tasks = {}
- try:
- for task in Path(f'/proc/{pid}/task').iterdir():
- tasks[task.name] = {name: read(task / name) for name in ('stat', 'schedstat', 'wchan')}
- except (FileNotFoundError, ProcessLookupError):
- pass
- # Both common cgroup layouts; unavailable counters are recorded as null.
- paths = [
- '/proc/stat', '/proc/meminfo', '/proc/loadavg', '/proc/diskstats',
- '/proc/pressure/cpu', '/proc/pressure/io', '/proc/pressure/memory',
- '/sys/fs/cgroup/cpu.stat', '/sys/fs/cgroup/cpu.max',
- '/sys/fs/cgroup/memory.current', '/sys/fs/cgroup/memory.max',
- '/sys/fs/cgroup/cpu,cpuacct/cpu.stat',
- '/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us',
- '/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us',
- '/sys/fs/cgroup/memory/memory.usage_in_bytes',
- '/sys/fs/cgroup/memory/memory.limit_in_bytes',
- ]
- return {'epochMs': time.time() * 1000,
- 'process': {name: read(f'/proc/{pid}/{name}') for name in ('stat', 'status', 'io')},
- 'threads': tasks, 'host': {path: read(path) for path in paths}}
- def main():
- if sys.platform != 'linux' or len(sys.argv) < 4 or sys.argv[2] != '--':
- raise SystemExit(__doc__)
- out = Path(sys.argv[1]).resolve()
- out.mkdir(parents=True, exist_ok=False) # Preserve completed/interrupted runs.
- argv = sys.argv[3:]
- command = {'argv': argv, 'startedEpochMs': time.time() * 1000,
- 'affinity': sorted(os.sched_getaffinity(0)), 'wallTimeout': None}
- command_path = out / 'command.json'
- command_path.write_text(json.dumps(command, indent=2))
- start = time.monotonic()
- with (out / 'console.log').open('w') as log, (out / 'resources.ndjson').open('w') as resources:
- # Inherit the foreground process group: Ctrl-C reaches this owned child
- # too. Never signal a PID discovered outside this invocation.
- child = subprocess.Popen(argv, stdout=log, stderr=subprocess.STDOUT)
- command['pid'] = child.pid
- command_path.write_text(json.dumps(command, indent=2))
- next_notice = start
- try:
- while child.poll() is None:
- resources.write(json.dumps(sample(child.pid)) + '\n')
- resources.flush()
- now = time.monotonic()
- if now >= next_notice:
- print(f'Benchmark PID {child.pid}: {now-start:.0f}s elapsed; progress in {out}', flush=True)
- next_notice = now + 30
- time.sleep(2)
- except BaseException:
- # Record an explicit interrupted outcome, even when no final result
- # exists. Give only our child a chance to stop, then reap it.
- child.terminate()
- try:
- child.wait(timeout=10)
- except subprocess.TimeoutExpired:
- child.kill()
- child.wait()
- command['interrupted'] = True
- raise
- finally:
- command.update(exit=child.poll(), wallSec=time.monotonic()-start)
- command_path.write_text(json.dumps(command, indent=2))
- return child.returncode
- if __name__ == '__main__':
- sys.exit(main())
|