diff --git a/mpcore/src/main/cpp/emu/guest_engine.cpp b/mpcore/src/main/cpp/emu/guest_engine.cpp index 0cec9b8..2afe1e9 100644 --- a/mpcore/src/main/cpp/emu/guest_engine.cpp +++ b/mpcore/src/main/cpp/emu/guest_engine.cpp @@ -2982,117 +2982,53 @@ uc_engine* GuestEngine::CreateConfiguredEngine() { // prologue-load capture, each hit both breaking a translation block and // writing to logd. - // See Sub4BA588EntryProbeHookCb/Sub4BA588V20ProbeHookCb's own comments - - // tracing sub_4BA588 ("loadNodeUncached") across every call, to catch - // exactly when v20 first turns bad. - { - uc_hook sub4BA588EntryHook, sub4BA588V20Hook; - uc_hook_add(newUc, &sub4BA588EntryHook, UC_HOOK_CODE, - (void*)Sub4BA588EntryProbeHookCb, nullptr, - 0x4ba588u, 0x4ba588u); - uc_hook_add(newUc, &sub4BA588V20Hook, UC_HOOK_CODE, - (void*)Sub4BA588V20ProbeHookCb, nullptr, - 0x4ba6a8u, 0x4ba6a8u); - } - - // See Sub494EF8NotFoundProbeHookCb/Sub495698UnsupportedFormatProbeHookCb/ - // Sub494EF8Pre|PostCallSub495698ProbeHookCb's own comments - the three - // real EA code paths that can legitimately empty out the vector - // sub_4BA588 then crashes on, continuing the same chase. - { - uc_hook notFoundHook, unsupportedFormatHook, preCallHook, postCallHook; - uc_hook_add(newUc, ¬FoundHook, UC_HOOK_CODE, - (void*)Sub494EF8NotFoundProbeHookCb, nullptr, - 0x495398u, 0x495398u); - uc_hook_add(newUc, &unsupportedFormatHook, UC_HOOK_CODE, - (void*)Sub495698UnsupportedFormatProbeHookCb, nullptr, - 0x49599cu, 0x49599cu); - uc_hook_add(newUc, &preCallHook, UC_HOOK_CODE, - (void*)Sub494EF8PreCallSub495698ProbeHookCb, nullptr, - 0x4950d4u, 0x4950d4u); - uc_hook_add(newUc, &postCallHook, UC_HOOK_CODE, - (void*)Sub494EF8PostCallSub495698ProbeHookCb, nullptr, - 0x4950d8u, 0x4950d8u); - } - - // See Sub495698ReadCallProbeHookCb/Sub495698ReadResultProbeHookCb's own - // comment - bracketing sub_495698's own first "read 2 bytes" virtual - // call, to see which object/function it dispatches to and what (if - // anything) it actually returns. - { - uc_hook readCallHook, readResultHook; - uc_hook_add(newUc, &readCallHook, UC_HOOK_CODE, - (void*)Sub495698ReadCallProbeHookCb, nullptr, - 0x4956e4u, 0x4956e4u); - uc_hook_add(newUc, &readResultHook, UC_HOOK_CODE, - (void*)Sub495698ReadResultProbeHookCb, nullptr, - 0x4956e8u, 0x4956e8u); - } - - // See Sub494EF8CacheFastPathProbeHookCb's own comment - confirms - // whether the cache-slot object's own [begin,end) byte range is empty - // for the crashing call vs non-empty for working ones. - { - uc_hook cacheFastPathHook; - uc_hook_add(newUc, &cacheFastPathHook, UC_HOOK_CODE, - (void*)Sub494EF8CacheFastPathProbeHookCb, nullptr, - 0x495030u, 0x495030u); - } - - // See SbaLoadCallProbeHookCb/SbaLoadResultProbeHookCb's own comments - - // bracketing sub_494EF8's own ".sba load by name" virtual call. - { - uc_hook sbaLoadCallHook, sbaLoadResultHook; - uc_hook_add(newUc, &sbaLoadCallHook, UC_HOOK_CODE, - (void*)SbaLoadCallProbeHookCb, nullptr, - 0x4952c8u, 0x4952c8u); - uc_hook_add(newUc, &sbaLoadResultHook, UC_HOOK_CODE, - (void*)SbaLoadResultProbeHookCb, nullptr, - 0x4952ccu, 0x4952ccu); - } - - // PreMemcpyPltCallProbeHookCb's registration removed 2026-09-19 (the - // function itself stays defined in case this needs revisiting). Its own - // question - "is LR already 0 before this PLT call" - was answered long - // ago, and the cost of leaving it registered is severe rather than - // merely noisy: it puts a UC_HOOK_CODE on __aeabi_memcpy's PLT stub, - // i.e. on the single hottest routine in the whole program, so Unicorn - // must break its translation block and call out to C++ on EVERY memcpy. - // A single prologue-load capture logged 197,861 hits. Same opt-in - // discipline as the UC_HOOK_BLOCK diagnostics above. - - // See TextClipDispatchEntryProbeHookCb's own comment - task #39 - // continuation, dumping the render-batch object's +132/+136/+340 fields - // live at both AppendQuad-vs-clip dispatcher entries. - { - uc_hook textClipDispatch4010F0Hook, textClipDispatch4014ECHook; - uc_hook_add(newUc, &textClipDispatch4010F0Hook, UC_HOOK_CODE, - (void*)TextClipDispatchEntryProbeHookCb, nullptr, - 0x4010f0u, 0x4010f0u); - uc_hook_add(newUc, &textClipDispatch4014ECHook, UC_HOOK_CODE, - (void*)TextClipDispatchEntryProbeHookCb, nullptr, - 0x4014ecu, 0x4014ecu); - } - - // See QuadGeometryProbeHookCb's own comment - task #39, measuring the - // raw quad corner geometry one level upstream of every transform tested - // (and disproven) so far. - { - uc_hook appendQuadHook, clipConsumerHook, drawWrapperHook; - uc_hook_add(newUc, &appendQuadHook, UC_HOOK_CODE, - (void*)QuadGeometryProbeHookCb, nullptr, - 0x4015d4u, 0x4015d4u); - uc_hook_add(newUc, &clipConsumerHook, UC_HOOK_CODE, - (void*)QuadGeometryProbeHookCb, nullptr, - 0x3da24cu, 0x3da24cu); - uc_hook_add(newUc, &drawWrapperHook, UC_HOOK_CODE, - (void*)QuadGeometryProbeHookCb, nullptr, - 0x400678u, 0x400678u); - uc_hook glyphEmitterHook; - uc_hook_add(newUc, &glyphEmitterHook, UC_HOOK_CODE, - (void*)QuadGeometryProbeHookCb, nullptr, - 0x4042f0u, 0x4042f0u); - } + // 2026-09-19 (task #48): FIFTEEN spent diagnostic UC_HOOK_CODE probes + // de-registered here in one pass. Their functions stay defined, matching + // the convention the neighbouring comments already follow - only the + // registrations are gone. What was removed and which closed task it + // belonged to: + // task #35 (sub_4BA588 crash chase, closed): + // Sub4BA588Entry 0x4ba588, Sub4BA588V20 0x4ba6a8, + // Sub494EF8NotFound 0x495398, Sub495698UnsupportedFormat 0x49599c, + // Sub494EF8PreCall 0x4950d4, Sub494EF8PostCall 0x4950d8, + // Sub495698ReadCall 0x4956e4, Sub495698ReadResult 0x4956e8, + // Sub494EF8CacheFastPath 0x495030, + // SbaLoadCall 0x4952c8, SbaLoadResult 0x4952cc + // task #39 / #41 (UI projection + glyph chase, both closed): + // TextClipDispatchEntry 0x4010f0 and 0x4014ec, + // QuadGeometry 0x4015d4 (AppendQuad), 0x3da24c (clip consumer), + // 0x400678 (draw wrapper), 0x4042f0 (glyph emitter) + // + // Why this is a performance change and not just tidying, measured rather + // than assumed. The first in-race profile (simpleperf on the live + // process, 20,424 samples) put 63.03% of all CPU in helper_uc_tracecode + // and another 5.29% in helper_check_exit_request_arm - ~68% in Unicorn's + // hook machinery, against 1.30% in the GPU driver. Two properties of the + // vendored Unicorn source make a long hook list expensive: + // 1. helper_uc_tracecode (uc.c:2129) walks the ENTIRE UC_HOOK_CODE list + // on every hit and has NO early exit - it keeps iterating after + // finding and calling the matching hook. So each hit paid for all + // ~24 registered hooks regardless of how many matched. + // 2. A hooked instruction breaks the translation block: TCG cannot + // chain through it, and gen_set_pc_im + check_exit_request are + // emitted around it (target/arm/translate.c:11594-11604). That lost + // optimisation is why check_exit_request shows up second. + // The last four probes above sat on the TEXT rendering path, so they + // fired per HUD glyph per frame - during a race, continuously. + // + // Note the hook-list search itself is translate-time, not per-PC: + // HOOK_EXISTS_BOUNDED is evaluated in thumb_tr_translate_insn and the + // result is baked into the cached TB. Cold hooks are therefore nearly + // free; it is hooks on HOT addresses that cost, which is the real reason + // these four mattered. + // + // Deliberately NOT removed, because they are functional rather than + // diagnostic: MiscStubDispatch (the consolidated stub dispatcher), + // FnvHashAccelHookCb, the four zlib_accel hooks, mem_fault_hook_cb. + // + // PreMemcpyPltCallProbeHookCb was already de-registered earlier on the + // same reasoning - it sat on __aeabi_memcpy's PLT stub, the hottest + // routine in the program, and logged 197,861 hits in one prologue load. // Native zlib acceleration - see zlib_accel.h's own comment. Replaces // InflateProbeHookCb (which measured the problem: 66.2MB out of a single @@ -3131,18 +3067,12 @@ uc_engine* GuestEngine::CreateConfiguredEngine() { // per-byte. Kept unregistered until that is done - see // name_lookup_accel.h. - // See MeasureAdvanceProbeHookCb's own comment - task #39's root-cause - // chain, catching the advance value at the exact instruction that - // produces it. - { - uc_hook measureEntryHook, measureRetHook; - uc_hook_add(newUc, &measureEntryHook, UC_HOOK_CODE, - (void*)MeasureAdvanceProbeHookCb, nullptr, - 0x542e50u, 0x542e50u); - uc_hook_add(newUc, &measureRetHook, UC_HOOK_CODE, - (void*)MeasureAdvanceProbeHookCb, nullptr, - 0x542f14u, 0x542f14u); - } + // MeasureAdvanceProbeHookCb's two registrations (0x542e50, 0x542f14) + // removed 2026-09-19 with the rest of task #48's sweep; the function + // stays defined. It belonged to task #39's root-cause chain, which is + // closed, and it bracketed the glyph-advance computation - so like the + // QuadGeometry/TextClipDispatch probes it fired once per glyph per frame, + // on the hottest path there is for a HUD full of text. return newUc; }