- mlock on macOS wires only the pages libkernel has not wired yet. XNU
counts each wire of a page, and the locked ranges merge overlapping
locks, so a page locked twice stayed wired after munlock and
munlockall. The mlock test checks the wire count on macOS.
- mprotect changes a watched page through GuestWriteWatchProtect, which
applies the host protection and records it under the write watch's
lock, so a collection on another thread cannot run between the two.
The write watch test covers it.
- sceKernelVirtualQuery takes a watched page's write permission from the
write watch for memory outside the guest allocation registry.
- GuestAllocationsGuestPages counts an image segment up to the end of
its last host page, as the image page collection does.
- The macOS includes stay in main's include block, so the host code
other pull requests add there still sees them; the unused Mach-O
headers and the end trim, which never changed the protected spans,
are gone, and the guest page size is passed in.
- The libc and libkernel debt entries describe what is wired now.
- mprotect: the guest rounds a range to its 16 KiB pages. A Mach-O image
is loaded at a multiple of the host page size only (4 KiB under
Rosetta), so the pages are counted from where the ELF address 0 of the
image holding the range was loaded, and a rounded edge smaller than a
guest page that lies in the gap between two segments, where no host page
exists, is left out. The write watch is told the new protection.
- sceKernelVirtualQuery reads the region with mach_vm_region.
- sceKernelMunlockall unlocks the ranges libkernel locked, one by one:
macOS has no munlockall, which fails there with ENOSYS and would
overwrite errno.
- The stack protect test reads page protections with mach_vm_region.
This is mugurc's f66a4df741 from his macOS series, without the mlock
check that #10889 adds.
Co-authored-by: mugurc <[email protected]>
A resident direct memory mapping created a SharedPage, a Physical, a
physical map node and an aliases buffer for every 16 KiB page, about
18 MiB of heap per GiB mapped, although a page mapped once holds no
state there. Resident pages now keep only their View, with pins and the
compare-when-shared mark in it; an index from (section, offset) to the
resident run finds a live resident mapping of a physical page, and the
shared state is created when a run is demoted, or when it is evicted
with data or a compare mark. ForgetSection drops the index entries of
the section's runs.
guest_memory on Windows 11 26200 (Ryzen 5 5600X): private memory per
GiB mapped at map time drops from 30.9 MiB to 13.4 MiB.
Co-Authored-By: Claude <[email protected]>
Add the BSD "quad" `strtoq` export for the libc PRX as a thin wrapper
around `strtoll`, as FreeBSD 11.4 lib/libc/stdlib/strtoq.c does. The
unsigned sibling `strtouq` is already here as the same alias of
`strtoull`. Extend the guest wide strings test to probe the signed
path on an under/overflow run and a short positive run with a trailing
non-digit. Document the export in docs/dev/debt/libc.md with the
registry NID `yUUOxgk8Dmc`; console behavior is unverified beyond the
existing `strtoll` contract.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Page 2 now samples its sources once a second, in NotePresent, while it
shows; nothing of it runs while the overlay is hidden or on page 1.
- Busy share per queue worker, the top 5 phases in ms per frame and queue
0's waits (memory, lock, GPU) come from the trace's per-thread totals.
Showing page 2 turns recording on with buffering off, so a timed scope
only adds to its thread's totals; leaving it turns recording off again.
Toggle switches it under a mutex, and ANYPS5_OVERLAY=2 switches it on at
start. An APS5_TRACE_* run keeps its own recording untouched, and page 2
then shows these lines only while that trace records. A driver thread
names itself when it starts (BindThread without APS5_TRACE_* stores the
name only) and binds on its first timed scope while page 2 shows;
unnamed threads are not bound.
- Render passes per frame: the draw path counts a begun pass in the trace
totals while page 2's totals are on (PerformanceTrace::Count).
- Flips/s and the draw cache's hit rate come from the driver through a
source it registers at start (the draw cache's lock is only tried); the
pipeline store's hit rate from PipelineStoreTotals; the compile backlog
from the shader preparation and library prebuild pools (tried locks).
- Dropped frames per second are the presents that found no free swapchain
image; repeated ones are presents of the display buffer shown just before.
The device passes the buffer's address, its present mode and the drops.
- Commit charge against the commit limit: GetPerformanceInfo on Windows,
Committed_AS and CommitLimit from /proc/meminfo on Linux; page faults per
second from PageFaultCount or getrusage.
- Host imports: their count and bytes against the budget, on devices that
import host memory.
- CPU copies: address-based builds count the bytes they copy in a
per-thread counter (a relaxed load and store, folded once a second) with
the largest build of each second against the address-copy cap.
- Unimplemented-function calls: libc counts NotImplemented_nid_no_patch
calls; refused shaders are the APS5_SKIP_REFUSED_DRAWS set's size.
- Title ID (when the title has a param.json) and present mode.
The tests cover the trace deltas, hit rates, present-mode names, the
/proc/meminfo parser and the OS reads, the per-thread copy counter's fold,
the counted unimplemented call, the trace switch with page 2 and lazy
binding without buffering, and on lavapipe the sampler end to end with a
fake driver source, its reset when page 2 is left, and the cost of a
present hidden, on page 1 and on page 2.