Measuring instead of only judging
The suggestion was: rather than asserting how long a sleep should take, record how long it actually took, and compare that across emulators and eventually a console. That is a better idea than what was here, and it corrects a limitation in the earlier reasoning (D059).
The objection at the time - timing a sleep means picking a threshold, and picking one is inventing a specification - was aimed at the wrong thing. It argues against asserting on a measurement, not against recording one.
120-measure now emits OBS|measure records:
a quantity, a value, a unit, and no verdict. Assertions stay beside
them, deliberately loose and marked assumed, because a
stated guess is a better position than no expectation at all - and
because the number beside it turns correcting that guess into reading a
figure and editing a constant.
The clock experiment works
Three time sources are readable without a struct, and the platform does not say what any of them counts. Rather than assume: sleep a known interval, read them all, and see which advanced by roughly the sleep (D060).
Validated where the answer is known. The host stubs
were changed from constants to real clocks -
CLOCK_PROCESS_CPUTIME_ID and CLOCK_MONOTONIC -
so the experiment runs somewhere the correct classification exists.
Across 20ms it reported 42µs of process time and 20.13ms of monotonic,
which is right. A section whose logic has never run is not something to
hand an emulator, and constants in the stubs would have meant exactly
that.
Two findings on the first emulator run
sceKernelGetProcessTime is a wall clock under
shadPS4. All three sources advanced by the full 20ms; on the
host, process time moved 42µs. The name and the toolchain documentation
both say CPU time. Recorded as a divergence from the host reference
rather than as a proven bug - what the console does is precisely what
has not been measured, and this is the first check written to answer
that rather than assume it.
sceKernelReadTsc and
sceKernelGetProcessTimeCounter also differ by 36 ticks, so
they are the same underlying clock there.
sceKernelUsleep returns early.
| requested | host | shadPS4 |
|---|---|---|
| 1,000µs | 1,087 (over) | 1,372 (over) |
| 5,000µs | 5,120 (over) | 4,783 - 217 short |
| 20,000µs | 20,120 (over) | 19,288 - 712 short |
Same direction across two runs (the earlier one was 243µs short at 20ms), against a host that overshoots every time. A sleep is a lower bound; code that sleeps before reading a register expects the interval to have passed.
The first version of the check could not see it. Its
floor was half the request, so 19,288 against 20,000 passed comfortably.
The shortfall is now its own recorded quantity and a
partial verdict. That is the value of measuring showing up
immediately: the number was in the report before the check knew to care
about it.
The documented hazard bit again, somewhere it had not been fixed
sweep.ps1 died on a multipass exec stderr
warning - the exact failure recorded in D050 for
multipass transfer, guarded in
run-emulator.ps1 and not here. It is intermittent, which is
what made it read as a new fault rather than a known one: this script
had run cleanly a dozen times.
Now behind an Invoke-Vm helper that relaxes the
preference and checks the exit code, which is the thing that actually
reports failure. Writing a hazard down is not the same as fixing every
place it applies, and that is worth remembering the next time one gets
documented.
State
666 records, complete. 65 pass, 6 partial, 38 fail, 7 skip.
Provenance across 105 checks: 57 spec, 47
assumed, 1 documented, 0
hardware.
Thirty-six checks target the FreeBSD-derived sceKernel*
and scePthread* surface, and a good share of the assumed
ones have a FreeBSD man page that settles them. That is what
OBS_FROM_DOCUMENTED is for and it is currently used once.
Reading is the only cost, and it narrows what a console actually has to
answer.