Calling sigaction() on Android does not make you the first handler for SIGSEGV.
libsigchain intercepts it and runs ART's FaultManager first, which reads the
faulting thread's registers as an ArtMethod* and dies on guest data -- so every
recoverable guest fault in JIT'd code killed the process before our handler ran,
with nothing in the app log to say why. Resolve sigaction from libc directly and
install through that, keeping the runtime's previous action so non-emulator faults
are forwarded on rather than swallowed. Registering through both paths recurses,
so this registers once and guards re-entry.
SIGBUS was only handled on Apple platforms; Android raises it for the same
unmapped-guest-page cases, so it needs the same treatment.
Also make the fault report survive a fault taken while reporting: emit an
allocation-free breadcrumb with the signal, address, PC and the GPRs before the
formatted dump, and chain to the previous handler first so debuggerd still records
a tombstone. The breadcrumb goes after the recovery attempts, not before -- emitting
it on entry logged over a thousand recovered faults per second and was itself a
stall.
Three faults that showed up in tester logs, all of which made the emulator
look broken in ways the log then hid.
Eternal Sonata flooded with SPU "Invalid code" errors: when the analyser
produced no data the recompiler had an empty branch with a TODO where the
fallback belonged, so the block was neither compiled nor marked, and the same
address was retried forever. It now marks the block failed and lets the
interpreter take it -- 6320 errors in one session down to none.
The unknown-instruction and halt messages are rate-limited, per opcode and per
address rather than globally, so a repeating fault reports once instead of
every execution. One tester's log went from 600 MB to 2.0 MB; the log volume
itself had been slowing the emulator, so this is not only a readability fix.
ARM64 fault classification in Thread.cpp preferred a heuristic comparing
si_addr against the PC, which misreads a genuine data fault as an instruction
fetch. It now decodes ESR first and only falls back to the heuristic, and an
SPU halt at the 0xffdead00 sentinel is reported as a guest assertion rather
than a host segfault. BLEACH crashed here, and the misclassification gated
every recovery path behind it.
SPU: the ARM64 block checksum folded two thirds of every block through
absolute difference, which is not injective, so adding the same value to
two words left the checksum unchanged and similar job binaries hashed
alike. Plain summation now. This is what Precise SPU Verification was
working around, and that setting is exposed properly instead of only being
reachable by hand editing the config.
SPU: a block is no longer marked permanently failed when the trampoline
rebuild fails. The compiled function was live, the state was not
recoverable for the rest of the session, and the claim could never be
retaken.
RSX: render pass churn cut in heavy scenes, roughly 113 to 85 passes per
frame. On a tile based GPU every pass boundary is a full tile store and
reload. Two Vulkan specification violations fixed, and a read/write hazard
on the render pass path.
RSX: the FIFO no longer burns a core on sched_yield while idle.
Android: ADPF is implemented rather than an inert setting, logcat no longer
allocates and makes an IPC call per line, and Silence All Logs is available
for playable titles.
Audio: Oboe backend, for the per device quirks database and stream recovery
on disconnect and route change.
Ported from ouroboros420/rpcsx: GPU Turbo, power and thermal handling, the
crash and freeze fixes, savestate and WSI surface lifetime, honest RAM VRAM
budgeting, the persistent SPU object cache design, occlusion query and RSX
fixes, frame pacing and tiler tuning.
Ported from rfandango/rpcsx: the Turnip ZCULL deadlock fix and ARM64 SPU
checksum handling.
Individual commits are credited in comments at each site.
get_cycles passes the pthread handle to pthread_getcpuclockid, which glibc
answers with an error for a thread that has already exited -- the else branch
below returns the last known value for exactly that case. bionic instead looks
the handle up in its list of live threads and aborts the process when it is not
there.
m_thread is never cleared when a thread ends and the performance overlay samples
every PPU, SPU and RSX thread on a timer, so one finished thread is enough to
take the emulator down with it. Latent here rather than absent: it needs the
overlay on and a thread to have gone.
Record the kernel tid at initialize and build the per-thread clock id from it
the way bionic does once its own lookup succeeds, so clock_gettime simply fails
for a dead thread, which is what the surrounding code already expects. Cleared
at finalize so a thread stops being sampled before it goes away. Other platforms
keep the original path.
Found and fixed by Zulux91 in PS3Native.
Arkham City dies of SIGSEGV about 140ms after the game writes PS3Progress_Frame_1,
on both the Qualcomm driver and Turnip, with the database on or off, Multithreaded
RSX on or off, and the RSX profiler on or off. A table of ten runs showed no setting
correlates with it.
The reason it took so long to even establish that it WAS a segfault: nothing records
it. No tombstone (/data/tombstones is root-only, so its emptiness proves nothing), no
logcat crash-buffer entry, and no line from our own handler. The single witness
anywhere is Zygote:
I Zygote : Process <pid> exited due to signal 11 (Segmentation fault)
That combination is what a stack overflow looks like here. The handler is installed
with SA_SIGINFO alone, so it runs on the faulting thread's own stack; if that stack is
what overflowed there is nowhere to run, it faults again immediately, and the kernel
applies the default action having written nothing.
So: an alternate signal stack per thread in thread_base::initialize, and SA_ONSTACK on
the handler. Thread-local rather than shared, because two threads can fault at once and
a shared stack would corrupt whichever report lost the race.
This does not fix the fault. It makes the fault reportable, which is the thing that has
been missing all along: the next occurrence should log where it came from instead of
vanishing. Android only.
RSX and every SPU thread shared one affinity mask, so on a 4+3+1 phone that is
six hot threads over five cores. RSX is the thread the frame waits on, and it
was measured spending about 10ms per frame inside its own loop without running:
not blocked on the GPU, not faulting, just waiting for a core.
Carves the single fastest core out of the SPU mask and leaves it in the RSX one.
RSX keeps the whole fast cluster and only loses the contention for the best
core; the SPUs lose one core out of several. Skipped when it would leave the
SPUs with a single core, which would be worse than the problem.
Only takes effect under the alternative scheduler. On Operating System mode
Android places threads itself and this code does not run.
set_native_priority used pthread_setschedparam with sched_priority. Android
threads run under SCHED_OTHER, where sched_priority must be zero and
sched_get_priority_max returns zero, so the call succeeded and changed nothing.
The RSX thread asks for a boost when it starts and was still measured at nice 0,
taking about 5300 involuntary preemptions a second, roughly 130 per frame, from
the PPU, SPU and audio threads sharing its cores. It is the thread everything
else waits on.
Under SCHED_OTHER the scheduler weights by nice, which setpriority does set, and
Android gives apps enough RLIMIT_NICE headroom to go negative for their own
threads. Applies -8 for a raise and +8 for a drop, and warns rather than fails
if the headroom is not there.
Modest on purpose: a hint to be scheduled ahead of the other emulator threads,
not a bid to starve them.
Adds an Android build of the RPCS3 core plus a Compose UI, and fixes several
things that stopped it working on ARM64.
Renderer:
- Emit concrete bounds for runtime sized arrays in uniform blocks when
VK_EXT_shader_uniform_buffer_unsized_array is missing. Adreno does not have
the extension, so every game pipeline failed with VK_ERROR_UNKNOWN and only
overlays drew.
- Probe and request that extension properly instead of chaining its feature
struct unconditionally.
- Hand VMA the Vulkan function pointers it needs under VK_NO_PROTOTYPES.
- Rebuild the surface and swapchain when the window is lost instead of killing
the RSX thread.
- Only create a GLES context when the GL renderer is actually selected.
SPU:
- Sum instead of taking an absolute difference in the ARM64 block verification
checksum. The difference collides on the near identical job binaries an SPU
job manager streams through one local store address, so a cached block could
run against another job's code.
Threading:
- Implement thread affinity on Android using sched_setaffinity.
- Add an ARM big.LITTLE core arrangement so SPU and RSX threads land on the
fast cores.
Misc:
- Detect the host CPU for the LLVM JIT instead of pinning cortex-a34.
- Fall back to the default audio device when cubeb cannot enumerate.
- Some SPU programs inexplicably fail to compile when TBL2/TBX2 are used.
- As an insane workaround, first try to compile with TBL2/TBX2, if LLVM crashes while compiling, try to compile the same program without TBL2/TBX2.