Repository navigation
fix(libkernel): keep sceKernelDebugOutText off a redirected guest stderr - #4536
Conversation
Guest descriptors are host descriptors, so a guest that moves standard error with dup2 also moves the stream sceKernelDebugOutText writes to. A title that forwards its standard error to the kernel log through a pipe then reads its own debug output back and logs it again without end. Before the guest's first dup2 onto descriptor 2, the host's standard error is kept, and sceKernelDebugOutText writes there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ard error to keep A Windows GUI build started without standard handles has no descriptor 2. _dup(2) then reached the invalid-parameter handler, which aborts, before the "cannot keep the host standard error" error could be thrown. It now goes through WithoutParameterHandler like the other descriptor calls in this file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
|
|
Windows test: head AI-assisted (Claude Code). |
|
Coordination note: I carried David’s offered #4631 fix into #4640, preserving his commit author. It separately preserves Win32 |
What
This carries the second commit of #1870 by @TheAggie, with its author kept, rebased onto current
main, plus one small commit of mine. #1870 was closed on Oct 9 because ccleste did not reachdup2onmainyet; #2478 then addeddupanddup2without this commit.Titles built on the PS5 Vulkan template call
ps5_klog_capture_stderr(mihawk-99/PS5_PayloadSDKplatform/src/klog.c). It creates a pipe, starts a thread that passes every line read from the pipe tosceKernelDebugOutText, and moves standard error onto the pipe withdup2(pipe, 2). Guest descriptors are host CRT descriptors, so thedup2also moves the host's descriptor 2, the onesceKernelDebugOutTextwrites to. The reader thread then reads its own output back, each round adds a prefix, and once the pipe is full the reader blocks writing into the pipe that only it reads. From then on every host thread that writes tostderrwaits for that stream's lock.On
main0ef9dc9, ccleste (PPSA59322) and RommPS (PPSA76677) print no error. Their last line isVulkan device ready, and no swapchain is created. The thread stacks on Windows, taken with gdb at 35 s, are the same for both titles:sceKernelDebugOutText→fprintf→fputc→_fflush_nolock→WriteFile, which does not return.stderrlock infprintf, called fromGraphics::PrepareImportWatch. That call sits betweenVulkan device readyandSurface capabilitiesin theVulkanDeviceconstructor.Changes:
dup2onto descriptor 2, the host's descriptor 2 is duplicated and kept (File::HostStandardError).sceKernelDebugOutTextwrites to that copy from then on. The guest's own writes to descriptor 2 still reach its pipe._dup(2)goes throughWithoutParameterHandler, like the other descriptor calls inStdio.cpp. A GUI build started without standard handles then gets the commit'sdup2: cannot keep the host standard errorerror instead of the invalid-parameter abort.guest_filesystem, from feat(libkernel): implement dup2 #1870). The host's descriptor 2 is on a temporary file and a guest pipe is moved onto descriptor 2.sceKernelDebugOutTextoutput must reach the file, and a guestwrite(2, ...)must reach the pipe.dup2was rewritten by feat(libkernel): implement dup and dup2 #2478. In both branches the call that keeps the descriptor now comes right before the host_dup2/dup2; on Windows it comes afterRejectDirectoryDuplicate. The test needed adup2_nid_postfixdeclaration inGuestFilesystem.cpp.Not changed: what
dup2onto descriptor 2 returns. The SDK says the console refuses it ("A title may not dup2 (the console refuses it with EPERM)") and then moves only thestderrstream.Tested
Windows 11 (26300), WinLibs GCC 15.2.0 r7, Release,
BUILD_TESTING=ON, RTX 4080 (driver 616.56), onmain0ef9dc9:guest_filesystemandguest_duppass 3 of 3. Revert-pin: withStdio.cpp,File.hppandNotification.cppas onmain,guest_filesystemfails at the pipe read (Filesystem check failed at line 245), because the pipe also receives[debug:1] kept.check_skipspasses.main, neither saves a frame in 90 s.Swapchain createdand save the 30 frames by present 870, 14.5 s after the first, at about 60 presents a second. ccleste shows its title screen. RommPS shows its own "RomM Sync isn't running" screen, because it needs a HEN payload loader on port 9021.--trap-sdk-syscall-stubs,main0ef9dc9 againstmain+ this PR.mainand 4 with this PR (the sweep saves at most 4).baseq3data. Onmainit ran 45 s with nothing on stderr; onmain3d8ffeb it had exited at once, so the hang depends on timing. With this PR its log reaches stderr and it exits with code 0 at once.mainitself (checked with a secondmainrun), and none of them importsdup2.STD_ERROR_HANDLE(CrashReport.cpp), which the CRT's_dup2onto descriptor 2 also replaces, as feat(libkernel): implement dup2 #1870 noted. This PR does not change that.The branch was then rebased onto
maind592d54, where these files are unchanged apart from unrelated symbolic-link checks inGuestFilesystem.cpp. On the pushed head, on Windows,guest_filesystemandguest_duppass 3 of 3, and ccleste creates its swapchain and saves 17 frames in 30 s.Linux, in an Ubuntu 24.04 container (GCC 13.3, lavapipe, X11):
main, ccleste stops at the sameVulkan device ready, writes nothing to stderr, and does not exit on SIGTERM. gdb shows the reader thread inwrite()fromsceKernelDebugOutText.AGC graphics: subgroup capability 61 is unsupported for shader stage 1, which is a lavapipe limit.tools/check_conventions.py --base origin/mainpasses.tools/pr_overlap.py --branchlists #3872, which removes theRejectDirectoryDuplicateline just above the new call. Neither PR depends on the other.@TheAggie, this is your commit. If you would rather resubmit it yourself, say so and I will close this one.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code