Files
NX2navi/tests/census_charge_check.py
chrg6 af8e2a323b Charge the frames a memory-census walk overlapped to census, the memory report's time to a report part, and walk once at frame 3600N.
With mem_census = 1 and trace = perf, vk_memory.c census_main walks the address space for 100-240 ms on its own
thread, holding the process's address-space lock. The replaying thread's write-watch queries wait for that lock
("mem: slow write-watch query: 20-34 ms ... 0 pages written"), and ft_frame charged the frame to "replay": in the
0.10.0 fresh session (nx2navi.1.log, 7,974,927 bytes) 63 of the stutter map's 66 replay hitches sat beside such a
walk, 49 of them at frames 600N+1..3. The presenting thread's own 600-frame memory report (10-17 ms, timed in
vk_mem_census since b81a9561) went to "unattributed". And at frame 3600N, census_check walked on the presenting thread
and the same frame's heap_log started census_main's second walk.

- perf.c perf_census_walk brackets census_main's walk (atomics); ft_frame moves the part of a frame over 25 ms (or a
  stutter-map candidate) that overlapped the walk out of the replay part first, then out of the unattributed part,
  into "census". The rule is runtime/perf_census.h census_charge, capped by the overlap and the frame.
- A new cause "report": vk_mem_census feeds the report's time (perf_report_part), and so do perf.c's memory
  snapshots and its 600-frame sizes lines; the next frame takes it, so the overlap rule no longer has to infer it.
  The cause list changes together: perf.c FT_C_* and ft_cause_name, stutter_map.c SM_CAUSES and cause_short,
  tests/stutter_hitch_check.c names[].
- census_check records the frame it walked (perf_census_walked_frame); heap_log does not start census_main for that
  frame, so frame 3600N walks once. A frame stamp, not a time window, since prof_commit_census can fail and leave
  perf_note_wc uncalled.
- The "mem: process" line gives the walk's start and end on the run's clock and its length, so a play-test with
  mem_census = 0 can tell the walk from the presenting thread's report as the cause of the 600N+1 hitches.

New tests/census_charge_check.py builds runtime/perf_census.h alone: no overlap, partial, contained and still-running
walks, the charge from replay first, capped by the overlap and the parts, and 100,000 random frames in which the census
part never passes the frame; docs/building.md describes it. Diagnostics only: NX2NAVI_MEM_CENSUS defaults to 0, and
nothing in gameplay, pacing or the save changes. Not reproduced before the change (it needs a game run). The pass's
known-issues table, which should say that hitch counts are taken with mem_census = 0 and trace off, is not in the
repository. census_charge_check, stutter_hitch_check and stutter_map_check pass; every runtime file compiles with the
dev flags, and perf.c, stutter_map.c and vk_memory.c add no -Wall -Wextra warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 18:41:39 -05:00

42 lines
1.5 KiB
Python

"""Build and run tests/census_charge_check.c: runtime/perf_census.h, which charges the part of a frame that an
address-space walk of NX2NAVI_MEM_CENSUS overlapped to the frame's census part (perf.c ft_frame): from the replay part
first, then from the unattributed part, never more than the overlap and never past the frame.
No GPU, no game files, any Python.
Usage: census_charge_check.py
"""
import os
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MSYS_BIN = Path(r'C:\msys64\ucrt64\bin')
def compiler():
"""$CC, else MSYS2 UCRT64's clang, else clang on PATH (as tests/diff/common.py picks it, without its Unicorn)."""
if os.environ.get('CC'):
return os.environ['CC']
if os.name == 'nt' and (MSYS_BIN / 'clang.exe').exists():
return str(MSYS_BIN / 'clang.exe')
return shutil.which('clang') or 'clang'
def main():
work = ROOT / 'build' / 'test' / 'census_charge'
work.mkdir(parents=True, exist_ok=True)
exe = work / ('census_charge_check.exe' if os.name == 'nt' else 'census_charge_check')
env = dict(os.environ)
if os.name == 'nt' and MSYS_BIN.exists():
env['PATH'] = str(MSYS_BIN) + os.pathsep + env.get('PATH', '')
subprocess.check_call([compiler(), '-std=gnu11', '-O1', '-Wall', '-Werror', '-I', str(ROOT / 'runtime'),
str(ROOT / 'tests' / 'census_charge_check.c'), '-o', str(exe)], env=env)
return subprocess.run([str(exe)], env=env).returncode
if __name__ == '__main__':
sys.exit(main())