Skip to content

Cyberpunk 2077 (PPSA04029) on Linux: EngineWatchdog still trips on main d592d54e after #4321; the sceKernelBatchMap2 threads are now in the GuestAllocations registry walks #4634

Closed

Description

@snoozedDev

Title: Cyberpunk 2077 (PPSA04029 v02.120.000, EU disc, owned). Converted from the dump's eboot.bin with relinker --registry built from the same main, no --to-intel. Follows #3308: stops 1 and 2 are fixed on main (#3428, #3465, #3455, #3476); #4321 (merged 2026-10-12, fixes stop 3 of #3308) asked for a confirmation on this title: stop 3 still reproduces on main with #4321. This issue carries those runs and where the threads are now.
Host: Linux (kernel 7.2.5), GCC 16.2.1, RTX 3090, NVIDIA 610.57.04, selected with ANYPS5_GPU=3090. DualSense connected, no input given. vm.max_map_count is 1048576 on this host.
Build: main d592d54 (contains 5b7cb8d), Release, -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON (what build.yml's linux job uses). No probes. The runs "with #4216" below are the same tree plus #4216's head 327e3b8 cherry-picked, libs rebuilt; the five tests #4216 names pass.
Launch: APS5_TRACE_DISPATCH_IO=1 ANYPS5_GPU=3090 timeout 900 ./app.elf inside gamescope --backend headless -W 2304 -H 1296 -r 60, one launch at a time, cold shader cache unless stated. Times below are from launch to the title's exit, measured by the wrapper script (the log has no timestamps); no run reached the limit and none logs a timeout: sending signal line.
sce_module/: as in #3308. tools/import_audit.py on this conversion: 980 unique imports, 680 implemented, 28 stubs, 0 absent, 272 resolved by the title's own modules.

Stop 3 of #3308 on bare main d592d54: cold, cold, warm

!!! EngineWatchdog timeout!
timeout: the monitored command dumped core

139 s after launch in each of the three runs (after the eighth 10-second shader-cache tick), 0 dispatches, no earlier throw. The same tick as in the a11c8f7 retest on #3308; a bare-main cold run on d146ec1, just before #4321, also stopped at 139 s (that run was not posted). Reached before it: swapchain 1381x777 created, 767 / 758 shaders compiled in the cold runs, 765 hits / 359 misses in the warm one.

Three eu-stack samples of one cold run (the title started with a preload that calls prctl(PR_SET_PTRACER_ANY), nothing else changed; 44, 54 and 56 threads):

  • 60 s: two threads inside sceKernelBatchMap2 wait in pthread_mutex_lock from GuestAllocationsBegin_nid_postfix, one via DoMtypeprotect → DoMprotect, one via sceKernelMunmap → DoMunmap; ten title threads run title code.
  • 90 s: the holder is a sceKernelBatchMap2 thread in DoMtypeprotect → DoMprotect → GuestAllocationsProtect_nid_postfix, inside std::_Rb_tree<unsigned long, std::pair<const unsigned long, std::shared_ptr<GuestAllocations::Range const>>> operations; the title's main thread is in sched_yield.
  • 120 s: the main thread is in sceKernelBatchMap2 → DoMapDirect → GuestAllocationsRequireAvailable_nid_postfix.

WatchMapping is on no stack in any sample. On d592d54, GuestAllocationsProtect_nid_postfix (GuestAllocations.cpp:446) builds a copy of the whole range map on every call (replaceRange at line 418 copies registry().ranges at line 423, then swap), GuestAllocationsRequireAvailable_nid_postfix (line 348) and GuestAllocationsOverlaps_nid_postfix (line 361) walk the map from begin(), and GuestAllocationsAcquire_nid_postfix (line 488) copies every readable range into a lease on each call. With the title's ~35,000 live 64 KiB direct mappings (#3308), the per-call cost that was in WatchMapping is now in the allocation registry. #4321's description names the same functions (GuestAllocationsCovers, GuestAllocationsOverlaps, GuestAllocationsRequireAvailable) as where its benchmark's walks are once addresses are fixed. #2688 described the same registry cost and was closed in favour of #1324 (perf(memory): restrict registry work to affected ranges, open), whose description asks for a title profile of the remaining full-registry work; these samples are one.

With #4216 (327e3b8) on top of d592d54: cold 4 of 4 still trip, later; warm passes

Cold: !!! EngineWatchdog timeout! 227 s, 212 s, 222 s and 253 s after launch (after the 19th to 22nd tick; 1236 to 1309 shaders compiled; 0 dispatches); the fourth run was without the ptrace preload. Samples at 60, 120 and 200 s in the first three (56 to 57 threads): the sceKernelBatchMap2 threads are in std::_Rb_tree_increment inside GuestAllocationsAcquire_nid_postfix (reached via GuestAllocationsProtect_nid_postfix → GuestAllocationsRequireUnpinned_nid_postfix → WaitForLeasesAndImports, and via GuestAllocationsEnd_nid_postfix → Driver::Driver → ImportGuestMemory → ImportMappedRanges), inside GuestAllocationsRequireAvailable_nid_postfix and GuestAllocationsOverlaps_nid_postfix (via ReplaceFixedOverlap) from DoMapDirect, or waiting in pthread_mutex_lock from GuestAllocationsBegin_nid_postfix, GuestAllocationsAcquire_nid_postfix and GuestMemory::GpuMutexType::lock. #4216 starts GuestAllocationsCovers_nid_postfix and replaceRange at upper_bound and adds GuestAllocationsLookup_nid_postfix; RequireAvailable and Overlaps still start at begin(), and Acquire still copies the whole readable set per call.

Warm (cache of the fourth cold run; 1337 hits, 77 misses): the watchdog does not fire. 14,664 compute dispatches, then, 184 s after launch, the first presentation:

[videoout] presentation failed: clip distances, layer, viewport or auxiliary vertex exports is unsupported (register 0x207 = 0x00400001)
terminate called after throwing an instance of 'std::runtime_error'
  what():  clip distances, layer, viewport or auxiliary vertex exports is unsupported (register 0x207 = 0x00400001)

0x00400001 is CLIP_DIST_ENA_0 | VS_OUT_CCDIST0_VEC_ENA (State.cpp lines 701 and 1162 on d592d54), what #423 implements; tibzejoker reports GTA V (PPSA04264) on the same line there. Not yet a stop on bare main, so not reported as one; #3308 saw this value in four runs on a shutdown path.

Seen, not stops, in every run

79 s_swappc_b64 deferrals, 4 unsupported registered shader type 8, the background preparation failures GetImageResource dword 0 is not a valid runtime value at pc=0x00000474 and pc=0x000004d0 (plus pc=0x00000410 in the warm bare-main run and in every run with #4216), sceSysmoduleLoadModule unknown id 267, no not implemented line.

AI-assisted: yes (Claude Code, Claude Fable 5.1), operating under my supervision; I am accountable for this report and will respond to review.

Activity

  1. snoozedDev commented on Oct 14, 2026

    @snoozedDev
    ContributorAuthor

    Rerun on main 799982f, which contains #4216 (50578eb), #4698 (52edeed) and #423 (4bb2c7f). Same title, host, build configuration and launch as above, with timeout 520 instead of 900; the title was reconverted with this main's relinker. RTX 3090, NVIDIA 610.57.04. Times are from launch to the title's exit, measured by the wrapper script; no run reached the limit. Three cold runs and one warm run per tree.

    Bare main:

    run result time compute dispatches before it
    cold 1 !!! EngineWatchdog timeout! 431 s 11,036
    cold 2 !!! EngineWatchdog timeout! 446 s 12,216
    cold 3 first presentation refused (below) 491 s 10,330
    warm (cold 3's cache, 1440 hits, 0 misses) first presentation refused 205 s 12,954

    The watchdog now fires in 2 of 3 cold runs, about 300 s later than on d592d54 (139 s, 3 of 3) and after the title has started dispatching; the third cold run and the warm run pass it. eu-stack samples of the two cold runs that tripped (same ptrace preload as above; 57 to 61 threads): every sceKernelBatchMap2 thread that was not waiting on a lock was in one of three places, each inside std::_Rb_tree_increment or the range copy:

    • DoMapDirect → GuestAllocationsRequireAvailable_nid_postfix (60 s of cold 1, 250 s of cold 2);
    • GuestAllocationsOverlaps_nid_postfix, from DoMapDirect → ReplaceFixedOverlap (120 s of cold 1) and from DoMunmap (200 s of cold 1);
    • GuestAllocationsAcquire_nid_postfix, from DoMprotect → GuestAllocationsRequireUnpinned_nid_postfix → WaitForLeasesAndImports (90 s of cold 2) and from DoMapDirect → GuestAllocationsEnd_nid_postfix → ImportGuestMemory → ImportMappedRanges (400 s of cold 2).

    In two samples one further sceKernelBatchMap2 thread was in pthread_mutex_lock: from GuestAllocationsBegin_nid_postfix at 120 s of cold 1 and from GuestAllocationsRequireUnpinned_nid_postfix at 90 s of cold 2. WatchMapping, GuestAllocationsProtect_nid_postfix and replaceRange are on no stack any more. The first two places are the walks that (a) in #1324 bounds; the third is the lease copy, which #4698 shares between leases but rebuilds after every mutation.

    main + (a) (nikowolf123/AnyPS5 help/S3-registry-walks b3f7b2c cherry-picked onto 799982f, no conflict; libs rebuilt; ctest -R guest_memory passes on Linux, including its two new checks):

    run result time compute dispatches before it
    cold 1 !!! EngineWatchdog timeout! 161 s 0
    cold 2 first presentation refused 348 s 891
    cold 3 (no preload) first presentation refused 333 s 826
    warm (cold 3's cache, 1436 hits, 4 misses) same refusal (no [videoout] line in this log) 165 s 7,056

    With (a), RequireAvailable and Overlaps are on no stack in the six samples of the two preloaded cold runs. The sceKernelBatchMap2 threads sampled were in GuestAllocationsAcquire_nid_postfix, from ImportMappedRanges (60 s of cold 1, 120 s of cold 2) and from WaitForLeasesAndImports (200 s of cold 2): the two copies nikowolf123 listed as in neither (a) nor (b). At 60 s of cold 2 the thread was in ImportMappedRanges, releasing the lease's range vector. The one watchdog trip with (a) had no mapping or registry frame on any of its 58 threads at 150 s (the main thread was in title code, seven threads in SPIR-V validation and optimization), so the samples do not attribute it to the registry. Three runs per tree do not separate 2 of 3 from 1 of 3. The dispatch count before the first presentation grows with the time the title spends loading; it does not change what stops it.

    Next stop on bare main (cold 3 and warm, at the first presentation; the three runs with (a) that passed the watchdog end on the same shader and exception text, the warm one without a [videoout] line before it):

    [videoout] presentation failed: compute shader 0x1aa4f07500: AGC graphics: guest storage texture format 4 cannot be used as a storage image
    terminate called after throwing an instance of 'std::runtime_error'
      what():  compute shader 0x1aa4f07500: AGC graphics: guest storage texture format 4 cannot be used as a storage image
    

    The PA_CL_VS_OUT_CNTL refusal seen with #4216 above does not appear any more (#423). The new stop is reported in #6119.

    AI-assisted: yes (Claude Code, Claude Fable 5.1), operating under my supervision; I am accountable for this comment and will respond to review.

  2. mariuszkreft commented on Oct 15, 2026

    @mariuszkreft

    Opened a pull request from this issue: #6321

  3. snoozedDev commented on Nov 6, 2026

    @snoozedDev
    ContributorAuthor

    Retest on main 4c03784, which contains #6321 (merge 78ae9eb): the watchdog does not fire in any of five cold runs or in the warm run.

    Title: same dump as above (PPSA04029 v02.120.000), reconverted with this main's relinker.
    Build: GCC 16.2.1, Release, -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON, libs rebuilt, no local change.
    Host: Ryzen 7 7700X, RTX 3090, now on NVIDIA 615.78.08 and kernel 7.2.8 (the runs above were on 610.57.04), so the times are not comparable one to one with the earlier tables.
    Launch: APS5_TRACE_DISPATCH_IO=1 APS5_TRACE_SUMMARY_EVERY=30 APS5_TRACE_DIR=<dir> ANYPS5_GPU=3090 timeout -v 900 ./app.elf inside gamescope --backend headless -W 2304 -H 1296 -r 60, one launch at a time, log lines timestamped by the wrapper. No controller was connected (a DualSense was in the runs above) and no input was sent. Cold means AnyPS5's shader_cache/ was deleted before the launch; the driver's own cache was left alone. Run on 2026-11-06.

    run !!! EngineWatchdog timeout! ended by first present 60 fps from presents
    cold 1 (ptrace preload) no 900 s limit 81 s 473 s 27,902
    cold 2 (ptrace preload) no 900 s limit 78 s 409 s 31,321
    cold 3 no 900 s limit 78 s 379 s 32,870
    cold 4 no 900 s limit 78 s 379 s 32,376
    cold 5 no 900 s limit 78 s 439 s 29,368
    warm (cold 5's cache, 1491 hits, 0 misses; disturbed from about 340 s, see below) no 900 s limit 4 s 305 s 32,208

    Times are seconds from launch. First present is firstPresent in each run's anyps5-report.json; presents are summed over the aps5-summary.jsonl lines; "60 fps from" is the start of the first 30 s interval with 1,796 to 1,799 presents. Every run ends on the wrapper's timeout: sending signal TERM at the 900 s limit (exit 124). No log has a what():, terminate called, presentation failed or driver failure line.

    Cold runs that trip the watchdog: 0 of 5 here, 2 of 3 on 799982f, 2 of 2 on d592d54 (where the warm run tripped it too).

    In each cold run the first 30 s interval after the first present holds 7 to 56 presents, with one frame of 9 to 28 s (cold 1 has a second such interval, of 43 presents). The intervals after it hold 108 to 155 presents (median frame 193 to 257 ms). Then comes one interval of 602 to 1,468 presents in which the rate changes, and 60 fps from the next interval to the limit. A frame taken from gamescope at 700 s of cold 2 is the title screen with its PRESS … TO CONTINUE prompt. Other processes held 3,916 to 4,035 MiB of the card's 24,576 MiB before and after each cold run. The warm run was disturbed: other programs started using the GPU from about 340 s in, and from about 455 s its intervals hold 1,396 to 1,720 presents instead of 1,799.

    eu-stack samples at 60, 120, 200, 300, 400, 500 and 600 s of cold 1 and cold 2 (67 to 75 threads): no thread is in sceKernelBatchMap2 and no stack has a GuestAllocations frame in any of the 14 samples. On 799982f all six samples of the two tripping runs had one or two such threads.

    These runs do not isolate #6321. In October, with #6321 applied, two of four cold runs still tripped at 150 s, before the first dispatch (#6806 and its comments). On 4c03784 the title's first dispatch (the first [dispatch-io] queue= line; the device's own start-up probes log header-less [dispatch-io] lines at 2 to 3 s) comes at 77 to 78 s and the first present at 78 to 81 s in every cold run. No control run with #6321's change reverted was made on this tree.

    Closing as fixed on main. The title now waits at its title screen for input; a later stop gets its own issue. If the watchdog trips again on a later main, I will reopen this with the log.

    AI-assisted: yes (Claude Code, Claude Fable 5.1; the runs are the title's output), operating under my supervision; I am accountable for this comment and will respond to review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions