mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
The review of c05e6528 found a window in perf.c ft_frame: it loads walk_t0 and then walk_t1, while perf_census_walk(1) stores walk_t1 = 0 and then the new start. A walk that starts between the two loads leaves ft_frame with the previous walk's start and an end of 0, so perf_census.h census_charge took that walk as running since then, the whole frame as overlapped, and moved all its replay and unattributed time to "census". The window is a few nanoseconds, but the error is a whole frame. A walk takes 100-260 ms, so census_charge now returns 0 for a walk seen running for longer than CENSUS_WALK_MAX_NS (2 s). tests/census_charge_check.c gets a torn read (a start 10 s back with an end of 0), a walk running for 1.9 s, which still counts, and one running for 2.001 s, which does not; its random frames now reach walks more than 2 s old and check the moved time exactly (the overlap capped by the frame's room and its replay and unattributed parts) instead of only its bound. Built against the header before this change, the check fails the torn read, the 2.001 s walk and the first such random frame; with it, it passes. docs/building.md names the new case. Diagnostics only: the census part exists only with NX2NAVI_MEM_CENSUS = 1. Not reproduced in a run (the window is a few nanoseconds). perf.c compiles with the dev flags and adds no -Wall -Wextra warnings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32 lines
2.0 KiB
C
32 lines
2.0 KiB
C
// The part of a frame that an address-space walk of NX2NAVI_MEM_CENSUS overlapped, charged to the census (perf.c
|
|
// ft_frame). vk_memory.c census_main walks on a thread of its own, but the walk holds the process's address-space lock,
|
|
// which the replaying thread's write-watch queries (vk_memory.c vk_mem_mirror_begin) and every page fault wait for: in
|
|
// a session of 2026-11-13, 63 of the 66 hitches the stutter map charged to the replay sat beside such a walk, with a
|
|
// 20-34 ms write-watch query in them. So the time the frame overlapped the walk is taken out of the frame's replay part
|
|
// first, then out of its unattributed part, never more than the overlap and never so much that the census part grows
|
|
// past the frame. Apart from perf.c so that tests/census_charge_check.py builds it alone.
|
|
#pragma once
|
|
#include <stdint.h>
|
|
|
|
// A walk takes 100-260 ms, so one seen running for longer than this is a torn read: perf.c ft_frame loaded the last
|
|
// walk's start, then the 0 that perf.c perf_census_walk(1) stores as the end before it stores the next start. Taken as
|
|
// running, that walk would cover the whole frame.
|
|
#define CENSUS_WALK_MAX_NS 2000000000ull
|
|
|
|
// The frame is [now - d, now]; the walk [w0, w1]: w0 0 when no walk was seen, w1 0 (or older than w0, the end of the
|
|
// walk before) while it runs, then reaching now. Returns the time moved into *census.
|
|
static inline uint64_t census_charge(uint64_t now, uint64_t d, uint64_t w0, uint64_t w1, uint64_t *replay,
|
|
uint64_t *other, uint64_t *census) {
|
|
if (!w0 || !d || d > now) return 0;
|
|
if (w1 && w1 < w0) w1 = 0;
|
|
if (!w1 && now - w0 > CENSUS_WALK_MAX_NS) return 0;
|
|
uint64_t lo = now - d > w0 ? now - d : w0, hi = w1 && w1 < now ? w1 : now;
|
|
if (hi <= lo) return 0;
|
|
uint64_t ov = hi - lo, room = d > *census ? d - *census : 0;
|
|
if (ov > room) ov = room;
|
|
uint64_t r = ov < *replay ? ov : *replay;
|
|
uint64_t o = ov - r < *other ? ov - r : *other;
|
|
*replay -= r, *other -= o, *census += r + o;
|
|
return r + o;
|
|
}
|