Keep #3987's q, D, O, U, width, '-' and trailing '%' handling for
unknown conversions and fold it into this branch's measured path:
'#' and precision are ignored as measured, '+', space, apostrophe
and '0' with a width still throw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
alwaysNoconv, maxLength, encoding, in, out, unshift, length matches the MSVC/Dinkumware xlocale declaration order at 0x20-0x50; the generated layout is unchanged because each swapped pair shares one function pointer.
The unknown-conversion branch printed %m, positional arguments, the grouping
flag and modifiers in an unusual order as literal characters. Only characters
FreeBSD's vfprintf prints as themselves are now printed.
(cherry picked from commit 0106858d3d39f83eb2234a5f581d5bf5260521a0)
The Windows printf family threw on the q length modifier, the D, O and U
conversions, an unknown conversion character and a format ending in a percent
sign, where FreeBSD's vfprintf formats the first three and prints the unknown
character itself or stops at the end of the format.
(cherry picked from commit 0e3b2896d228c601cefc992b2895723188ebf0fa)
Kept both sides of GuestLibcThreads.cpp: main's ThreadHelpers test and this PR's VersionedSync test, both called from main().
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured through libSceLibcInternal.sprx's snprintf in a payload process on a PS5 (firmware 13.60): any size is accepted, a NaN keeps its sign and the + and space flags, each UTF-16 unit of %ls and %lc is encoded alone, positions above 99 work, and an unknown conversion such as %m prints % and its letter.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A page marked to be compared when shared is refused by every collect once a
second mapping demotes it, so the writes made to it since its last collect
were never reported. Each view of such a page now carries an unreported mask:
set when the view is demoted, when the page is first marked while the view is
already shared, and when a new view of a marked page is mapped. ComparedPages
lists the compared shared pages of a range, or takes the unreported ones.
Every import lookup walked the lease out from the containing range to find
the run of continuing registry ranges, which is linear in the run length.
That walk depends only on the lease snapshot, the containing range and the
import window block, so its first and last index are now memoized per
thread under a weak reference to the snapshot, the range index, the window
and the window's low end. APS5_NO_RUN_MEMO walks every time.
Everything that depends on other state runs on each call as before: the
largest size, the budget, the failed blocks, the remembered refusals and
the halving block search. importRunBounds returns the chosen indices, so
sameRange, runCovers and the whole-run lookup in importAllocation compare
against the lease without copying the run's range pointers.
importRunFailureShrinkTests records a failed 8 MiB block between two
lookups on an unchanged registry and requires the run to shrink to its
4 MiB blocks. importRunGrowthTests and importRunReplacedTests check that a
run grows and shrinks when its ranges are registered or replaced.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A private guest commit on Windows (every anonymous kernel map commits
with a 16 KiB granule) ran reset() on each page before replacing it:
10 VirtualQuery calls, a placeholder split and the VirtualAlloc2, so
12 calls per page. At the arena's first-fit frontier the placeholder is
the arena's free tail, hundreds of GiB.
Commit now queries a placeholder once, cuts the range out of it (one
split where the range starts inside the placeholder, one where the
placeholder extends past the range), and carves the pages from that
bounded placeholder: one split and one VirtualAlloc2 per page, and no
split for the last page. Each page is still its own allocation with the
same protection, write watch and created ranges, so munmap, MAP_FIXED
remaps, views and collects see the same layout as before. A piece that
would cross the end of its placeholder (a whole-range granule over
several placeholders) still takes the coalescing path through reset(),
and committed memory in the range still only changes its protection. A
zero granule now throws std::invalid_argument before touching the
address space.
windows_write_watch counts the queries with a compile-time counter that
only that test defines (APS5_COUNT_MAPPING_QUERIES, copied from #9505)
and checks: 256 fresh pages one page into a 1 GiB placeholder take one
query (main's loop makes 2,561), each page is its own allocation and
the placeholders around them are intact; the release and recommit of
one page; a protection-only commit; the write watch of carved pages; a
whole-range granule inside one placeholder and across two; committed
pieces and adjacent placeholders inside the range; a partial release
and a fixed remap that keep their neighbours; a granule that would
cross the end of the placeholder being carved, which takes the
coalescing path; a short last piece; a commit that runs to the end of
its placeholder; an unwatched commit; and the zero-granule throw.
PPSA14363 boot on an RX 6700 XT, idle PC, 4 runs on main and 3 with
this change: the first 10 s after the first present go from 546-552
presents with a 578-704 ms frame to 588-594 presents with a 97-117 ms
frame.
Co-Authored-By: Claude <[email protected]>
An import over a range that is not all writable private memory maps a
read-write alias first, and MapAlias demotes every resident run in the
range to shared views before ImportWatched runs. ImportWatched then
marks only the views that are still resident, so the pages the alias
itself demoted stayed unmarked: the import's own walk and every later
clearing collect armed them, and a guest store into one faulted.
demote now records that MapAlias demoted a view, and the resident-only
mark ImportWatched applies to the spans it kept watched also marks those
views. A second guest mapping of the page clears the record, so memory
the guest shares keeps today's tracking. agc_import_alias_shared checks
both, with single pages at 64 KiB boundaries.
Grand Theft Auto V (PPSA04264) still faults in its HDD streamer with
#9181 and #9159; one of those faults is a resumed store into a 16 KiB
MEM_MAPPED view
(https://github.com/boykopovar/AnyPS5/issues/9136#issuecomment-6356302049).
This closes one path that arms such views; it does not claim to remove
those faults.
#9159 marks memory that a rewatch or an import kept watched while it was
resident with SharedPage::comparedWhenShared, so its shared views stay
compared instead of armed. The mark lived only on the SharedPage, which
a physical slot holds weakly: once every view of that physical page was
unmapped, Map's pageOf made a fresh, unmarked SharedPage for the next
mapping, and the next clearing collect armed its aliases again.
The mark is now also kept on the physical slot. CompareWhenShared sets
it through each view's slot, and pageOf copies it into a SharedPage it
creates. ForgetSection still drops the slot and the mark with it, so a
reused section handle starts unmarked, and memory shared before it was
ever marked keeps its tracking. MapAlias and its runs create no
SharedPage, so they need no change.
The test maps one page resident, marks it, unmaps it, maps it into two
views, and requires a clearing collect over either view to refuse and
neither to be made read-only; after ForgetSection the same mapping is
collected and armed again.
Grand Theft Auto V (PPSA04264): tibzejoker's remaining resumed write
faults in zlib's inflate_fast with #9181 and #9159
(https://github.com/boykopovar/AnyPS5/pull/9159#issuecomment-6355849767)
may come from memory remapped this way. This change does not claim to
remove them.
feof_unlocked, ferror_unlocked, getc_unlocked and putc_unlocked are macros
on macOS, so they are called without the global scope qualifier. The
remaining differences are the ones Darwin's headers and libSystem have:
- CLOCK_REALTIME_COARSE and st_mtim do not exist; the write mark uses
CLOCK_REALTIME and st_mtimespec.
- tm_zone is a non-const char*.
- __cxa_thread_atexit returns void, so the wrapper reports success.
- Darwin's stack protector reads __stack_chk_guard from libSystem; the
guard of the guest libc has the postfixed name, which receives the NID
of the original.
- APS5_EXPORT spells the symbols with the leading underscore of Mach-O.
- GuestArenaSetProtection and PosixProtection are built on macOS as on Linux.
- The fclose failure test fills /dev/full, which exists on Linux only.
exception_runtime relies on the Itanium unwinder of libc, which is not
registered on macOS (the host libc++abi is used), so its test is only
registered elsewhere.
Two threads writing to the same stream each ran FileStream::SyncStatus
after their stdio call, outside the FILE lock, with plain writes to the
read and write counters and a read-modify-write of the flag word, so the
flags of one thread could be lost. Publish the counters with atomic
stores and the flags with a compare-exchange loop; the values stay the
same.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Since #8172, a host import that stays in the write watch keeps each of
its watched runs in the watch instead of taking the whole import out
when part of it was unwatched. Grand Theft Auto V (PPSA04264) imports
resident direct memory that way, and later maps the same memory at a
second address, which turns it into shared views. From then on the write
watch arms them, and the game's own stores fault and are resumed, the
red zone hazard of #361 (#9136, eboot 0x162b9e4 and 0x28f413a).
ImportWatched now marks the resident pages of each run it keeps in the
watch with GuestArenaCompareResidentWhenShared. They stay write-watched
while they are private, as #8172 needs for resident DCC targets. Once
they are shared, their collects fail and they are compared, as before
#8172, instead of being armed. Memory that is already shared when it is
imported is not marked and stays tracked as before.
agc_rewatched_direct_shared gains the imported case: resident direct
memory imported with the watch kept, then mapped a second time, must
collect as 0 through either mapping, stay writable and show a store
through both, while unimported shared direct memory is still collected.
windows_write_watch gains CheckResidentOnlyComparedWhenShared: a
resident-only mark leaves a shared view armed and collected, and still
keeps a resident page compared once a second mapping makes it shared.