Skip to content

feat(libSceNKWebKitRequirements): implement the libjpeg, libxml2 and libpsl exports - #14421

Open
PrivacyOnion wants to merge 14 commits into
boykopovar:mainfrom
PrivacyOnion:feat/nkwebkit-requirements
Open

PrivacyOnion wants to merge 14 commits into
boykopovar:mainfrom
PrivacyOnion:feat/nkwebkit-requirements

Conversation

@PrivacyOnion

@PrivacyOnion PrivacyOnion commented Nov 1, 2026 •

Copy link
Copy Markdown

What

Implements the 60 libjpeg, libxml2 and libpsl exports of libSceNKWebKitRequirements, stacked on #14374 (zlib). Only the last five commits are new here; the three before them are #14374.

  • libjpeg (15 exports, Jpeg.cpp): the decompression API (jpeg_CreateDecompress, jpeg_std_error, jpeg_read_header, jpeg_start_decompress, jpeg_read_scanlines, jpeg_finish_decompress, jpeg_save_markers, jpeg_resync_to_restart, …) over the libjpeg-turbo already in 3rdparty/libjpeg-turbo. Guest jpeg_decompress_struct, jpeg_error_mgr, jpeg_source_mgr and saved markers are LP64 mirrors (include/GuestJpeg.hpp, sizes fixed by static_assert) copied to and from the host objects around each call. Guest source-manager, marker and error callbacks run through APS5_VABI trampolines. A null object, or one not created by jpeg_CreateDecompress, throws std::invalid_argument.
  • libxml2 (23 exports, Xml.cpp): parser contexts, push and memory parsing, SAX handlers, documents, entities, error access and output buffers over libxml2 2.9.14, a new submodule at 3rdparty/libxml2 built from source by cmake/LibXml2.cmake without iconv, ICU, HTTP, FTP or catalogs. Guest-visible structures are LP64 mirrors (include/GuestXml.hpp); SAX, input and output callbacks go through trampolines.
  • libpsl (22 exports, Psl.cpp): psl_builtin, psl_load_file, psl_is_public_suffix, psl_registrable_domain, psl_unregistrable_domain, psl_is_cookie_domain_acceptable, psl_str_to_utf8lower, the builtin metadata getters and the rest of the 0.21.5 API, over libpsl 0.21.5, a new submodule at 3rdparty/libpsl (with its pinned list submodule) built by cmake/LibPsl.cmake. The builtin table is generated at build time from the pinned list.

The 60 stubs are removed from Export.cpp and their parameterless declarations in Identified.hpp are replaced by the real prototypes. 95 entries stay stubs (63 pending inventory exports, mostly SQLite, plus the 32 additional callable exports). Technical debt: pending counts updated and one Functional entry per library (no console verification, error_exit handling, libxml2 build options, libpsl snapshot).

Tested

Windows 11, host harness built with the Qt MinGW 13.1 toolchain:

  • tests/Jpeg.cpp: decodes a JPEG encoded by libjpeg-turbo through a guest source manager (init_source, fill_input_buffer, skip_input_data, resync_to_restart, term_source as guest callbacks), checks dimensions, components, a saved APP1 marker and every pixel against host libjpeg output, then decodes a progressive image in buffered-image mode (jpeg_has_multiple_scans, jpeg_consume_input, jpeg_input_complete, jpeg_start_output, jpeg_finish_output), and drives a guest error_exit through JERR_NO_SOI;
  • tests/Xml.cpp: strings and dictionary, chunked push parsing with guest SAX2 callbacks, error retrieval through guest error/serror and xmlCtxtGetLastError, memory parsing with predefined and document entities, external entities loaded through two distinct guest input handlers (including a registration made while one stream is open and registrations rejected by a full table), and output registration and rejection;
  • tests/Psl.cpp: builtin list queries (public suffix, registrable and unregistrable domain, cookie domain, counts, metadata), psl_str_to_utf8lower, and psl_load_file, psl_load_fp and psl_latest on a list the test writes, including wildcard and exception rules and their counts;
  • the zlib test of feat(libSceNKWebKitRequirements): implement the zlib exports #14374 still passes.

Not covered by a test: guest output callbacks being called (no export of this library opens an output buffer).

Export.cpp, Zlib.cpp, Jpeg.cpp, Xml.cpp and Psl.cpp compile with the real APS5_VABI. tools/check_conventions.py reports 0 errors (the one warning is the zlib entry from #14374) and test_public_headers.py --audit-only passes. A full CMake configure is not possible here (missing SDL2/ffmpeg submodules), so the guest build and CTest run are left to CI.

pr_overlap.py: .gitmodules is shared with #1835, #5021, #9855 and #13578, which add other submodules; #14212 only reformats this debt file. None implements these functions.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: feat(libSceNKWebKitRequirements): implement the zlib exports #14374
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Please preserve each libxml2 I/O registration's guest callbacks. Xml.cpp:695-708 overwrites one global callback set, while libxml2 stores a separate host table entry for each successful registration. Since every entry uses the same trampolines, registering B changes the callbacks reached through A's entry, including reads or closes for an already opened A context. A registration that returns -1 also changes those globals before libxml2 rejects it.

Keep the callback set associated with its registration and opened context, and leave existing registrations unchanged when a new registration fails. Please add regressions that actually parse through two distinct input handlers, continue using an earlier open context after another registration, and check failed-registration behavior. Cover the corresponding output-handler ownership as well. The current test only registers one dummy input and output set; it never calls them through I/O.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — changes requested

The mirror layouts, submodule pins, CMake files and public-header audit check out, but the libxml2 I/O registrations share one set of guest callbacks, so a later registration changes the callbacks of earlier ones, and the debt document was rewritten with CRLF line endings, so git diff --check fails at ec1fa3c51e.

What I ran

Exact head ec1fa3c51ec88d663f13c3b97fc92b4fde802adb; PR base ba627802e2210187d417deb64c8f2fed8e33011f (current main), unchanged at completion. Stacking: the PR declares Depends on: #14374; the first three commits are #14374's (head 3979b705f3, an ancestor of this head), and the two new commits are 408a262b5e and ec1fa3c51e. No build and no test run (no-build review). Commands and tools:

python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421
python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head ec1fa3c51ec88d663f13c3b97fc92b4fde802adb --json
python3 core/libs/tests/test_public_headers.py --audit-only   # run in an extracted copy of the head tree
git diff --check ba627802e2210187d417deb64c8f2fed8e33011f ec1fa3c51ec88d663f13c3b97fc92b4fde802adb

Hand checks: the LP64 layout of every sizeof/offsetof assertion in GuestJpeg.hpp, GuestXml.hpp and GuestPsl.hpp (a Python layout calculator over the header members, not a compile); the submodule pins against the upstream repositories; the new CMake files against the upstream CMake options; the export move between Export.cpp, Jpeg.cpp, Xml.cpp and Psl.cpp.

Results

Check Result
Diff check (precheck) Failed: trailing whitespace on all 55 lines of docs/dev/debt/libSceNKWebKitRequirements.md (CRLF)
Conventions and PR template (precheck) Passed; hosted conventions queued at this head
Live dependency and mergeability (precheck) Passed
Submodules .gitmodules and pins 3rdparty/libxml2 is https://github.com/GNOME/libxml2.git at 7846b0a677f8 (tag v2.9.14, 2022-05-02). 3rdparty/libpsl is https://github.com/rockdaboot/libpsl.git at ba4c49a20540 (v0.21.5, 2024-01-13). Both commits exist upstream
LP64 mirror asserts, GuestJpeg.hpp Computed layout matches all 5 sizeof and 120 offsetof assertions
LP64 mirror asserts, GuestXml.hpp Computed layout matches all 8 sizeof and 172 offsetof assertions. The xmlDoc prefix offsets match libxml2 2.9.14 tree.h
GuestPsl.hpp Opaque struct psl_ctx_st forward declaration; no layout to check
CMake (cmake/LibXml2.cmake, cmake/LibPsl.cmake) All 38 LIBXML2_WITH_* options exist in upstream 2.9.14 CMakeLists.txt; the libpsl template variables and the src/psl-make-dafsa, src/psl.c, src/lookup_string_in_fixed_set.c sources exist at the pin
Public-header audit 399 libraries, 26181 exports: unique declarations and umbrella coverage verified, exit 0
ABI audit by hand passed_with_warnings, 27 findings, all public-declaration-not-found: 5 static DefaultGuest* callbacks in Jpeg.cpp, 8 Dummy* callbacks, 5 Guest* callbacks and 9 Sax* callbacks, all static in tests/Jpeg.cpp and tests/Xml.cpp. None is an export
Export move Export.cpp declarations go from 155 (3979b705f3) to 95 (head); exactly 60 names leave it and are defined in Jpeg.cpp (15), Xml.cpp (23) and Psl.cpp (22). The 95 left are 63 pending exports and 32 Unknown rows
Test registration guest_nk_req_{jpeg,xml,psl} via foreach in CMakeLists.txt; guest_webkit_zlib from #14374
Xml I/O registrations Shared globals overwritten per registration (finding 1); the test registers one handler only
Hosted checks Queued at this head
Unedited diff check output (precheck)
docs/dev/debt/libSceNKWebKitRequirements.md:1: trailing whitespace.
+# libSceNKWebKitRequirements technical debt
docs/dev/debt/libSceNKWebKitRequirements.md:2: trailing whitespace.
+
docs/dev/debt/libSceNKWebKitRequirements.md:3: trailing whitespace.

Findings

Blocking:

  1. The libxml2 I/O registrations share one callback set. xmlRegisterInputCallbacks_nid_postfix and xmlRegisterOutputCallbacks_nid_postfix overwrite the globals s_guestInputMatch, s_guestInputOpen, s_guestInputRead, s_guestInputClose (and the output set) before calling libxml2 (Xml.cpp L695-L708 at head). Every host entry libxml2 stores for a registration uses the same trampoline, and the trampolines call the current globals (Xml.cpp L517-L521 at head). So registering a second handler changes the callbacks reached through the first handler's entry, including reads and closes for a context already opened through it. A registration that libxml2 rejects with -1 has already overwritten the globals. The only test registers one dummy handler, so none of this is pinned. I confirmed the same point independently of the comment at 12:25 by oneandonlydean. Fix: keep each callback set with its registration and its opened contexts, leave existing registrations unchanged when a new one fails, and add regressions that parse through two distinct input handlers, keep using an earlier open context after another registration, and check a rejected registration; cover the output handler the same way.
  2. The debt document uses CRLF line endings. Commit 408a262b5e (the first new commit) rewrote docs/dev/debt/libSceNKWebKitRequirements.md with 55 CRLF lines; the stacked base 3979b705f3 has LF. git diff --check therefore fails on every line, and the diff of that file is a whole-file rewrite that hides the real debt change. Fix: convert the file back to LF (for example sed -i 's/\r$//') and push. Nothing else in the PR carries CR characters.

Non-blocking:

  1. cmake/LibXml2.cmake sets BUILD_SHARED_LIBS with CACHE BOOL "" FORCE (line 6). The other bundled-library files use a plain set (for example core/libs/prx/libSceAgcDriver/CMakeLists.txt line 16). The forced cache write changes the value for every subdirectory configured after this one. Suggest the plain set.
  2. The debt entry says the export names were taken from "libxml2 2.13.4" sources, but the bundled tree is libxml2 2.9.14. This is about provenance, not behaviour, but the two versions should be named the same way in the doc.
  3. HostErrorExit (Jpeg.cpp lines 39-41) throws a C++ exception through libjpeg's C frames. The debt entry discloses that errors become exceptions, but correctness relies on the unwind tables of the C code. Worth stating which toolchains this was checked on.
  4. Several Psl.cpp assertions are weak: wildCount >= 0, excCount >= 0, a non-null check on distFn, and int outdated is discarded ((void)outdated). The version-number assertions are precise and match upstream psl_check_version_number (returns PSL_VERSION_NUMBER for 0x001505 and 0 for 0x001000, src/psl.c lines 1574-1590 at ba4c49a2).

The debt counts check out against the code: 63 pending in Export.cpp, 32 Unknown table rows, 52 pending exports declared in Identified.hpp and 11 others, and 15 + 23 + 22 = 60 moved exports. The zlib claim (vendored 1.3.2 under libjpeg-turbo/src/spng/zlib with NO_GZIP defined in zconf.h) is accurate.

Sources

Not verified

  • Nothing was compiled or run; the layout result comes from a Python calculator over the header members and from reading the upstream structs, not from a build.
  • The console's libjpeg, libxml2, libpsl and zlib versions and layouts are not verified (the debt says so).
  • The "63 pending exports are present in PS5-3.20_Libs" claim was not checked against that inventory.
  • Hosted checks at this head were queued; the Linux and Windows test results for the three new test binaries are not available.

To get this to a good state

Make the libxml2 I/O registrations keep their callbacks per registration, add the regressions described in finding 1, and convert docs/dev/debt/libSceNKWebKitRequirements.md to LF so that git diff --check ba627802e2210187d417deb64c8f2fed8e33011f HEAD is empty. The remaining points are optional. The diff check, the public-header audit, the layout checks and the Xml tests will be rerun on the new head.

@PrivacyOnion

Copy link
Copy Markdown
Author

@oneandonlydean thanks, fixed in fbc2ded.

Ownership. Xml.cpp no longer has one global callback set. libxml2 2.9.14 holds at most 15 input and 15 output entries (MAX_INPUT_CALLBACK/MAX_OUTPUT_CALLBACK in xmlIO.c), so the adapter has 15 slots of each, each with its own instantiated trampolines (HostInputMatch<Slot> … HostOutputClose<Slot>).

  • A registration fills the next free slot with its guest callbacks and registers that slot's trampolines.
  • Once libxml2 accepts it, the slot is never written again. An entry, and every context it opened, therefore keeps reaching its own guest callbacks whatever is registered later.
  • If libxml2 returns -1, the slot is cleared and returned, and no earlier registration is touched.
  • A null guest callback is registered as null, as libxml2 would store it.
  • The adapter never calls the libxml2 functions that empty these tables, so they only grow. Running out of slots therefore means libxml2's table is full, and the call returns -1 as libxml2 does.

Regressions (tests/Xml.cpp), all through the exports:

  • Two distinct input handlers. alpha: and beta: are registered. A push parse with XML_PARSE_NOENT loads two external entities, one through each handler. Each read returns one byte per call and checks that every context it receives was opened by its own open. The test checks that each handler opened, read and closed exactly its own stream.
  • An earlier open context. During alpha:'s first read, a third handler is registered. The rest of alpha's reads and its close must still reach alpha. The late handler is consulted for beta:two but never receives a stream.
  • Failed registrations. Input handlers are registered until libxml2 returns -1, and then one more distinct handler, which must also get -1. A second parse must still be served by alpha: and beta:, and the rejected handler must never be called, not even its match.
  • Output. Registrations must return consecutive entries, then -1 once the table is full, followed by one more rejected handler.

No export of this library opens an output buffer (there is no save or xmlOutputBuffer* export). Guest output callbacks are stored per slot exactly like the input ones, through the same code path, but no export can call them, so the output part of the test can only check registration and rejection.

Against the previous Xml.cpp, the new test fails: failed to load external entity "alpha:one", then the late-registration assertion. That run also showed a second bug, fixed in the same commit: xmlCreatePushParserCtxt with a null SAX handler passed the adapter's binding to libxml2 as user_data, so libxml2's default SAX2 handlers received it in place of the parser context. It now passes the caller's user_data, as libxml2 expects.

Host harness (MinGW 13.1): the zlib, jpeg, xml and psl tests pass. Xml.cpp and tests/Xml.cpp compile with the real APS5_VABI, and conventions report no errors.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked — new head, I/O callbacks fixed; line endings still blocking

The libxml2 I/O registrations now keep one callback slot per registration, which fixes the shared-callback defect from my review at ec1fa3c51e, but the debt document still uses CRLF line endings, so git diff --check still fails at fbc2ded785.

What I ran

Exact head fbc2ded78536e77667752a3c29c0201457a53fc4; PR base ba627802e2210187d417deb64c8f2fed8e33011f (current main), unchanged at completion. The new commit touches only Xml.cpp and tests/Xml.cpp. No build and no test run. Commands:

python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421
python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head fbc2ded78536e77667752a3c29c0201457a53fc4 --json
python3 tools/pr_test_quality.py --root <git archive of fbc2ded785: core/libs/tests, core/libs/prx/libSceNKWebKitRequirements, core/libs/CMakeLists.txt> --json core/libs/tests/Xml.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Xml.cpp

I also read the slot table and the templated trampolines in Xml.cpp (L45-L46 and the Host*<Slot> functions), the registration functions (L745-L781), and the new regressions in tests/Xml.cpp (L292-L326 and L376-L414). The libxml2 callback table capacity at the pinned commit 7846b0a677f8 is MAX_INPUT_CALLBACK and MAX_OUTPUT_CALLBACK = 15 (xmlIO.c L100 and L117), which matches XmlInputSlotCount and XmlOutputSlotCount = 15.

Results

Check Result
Diff check (precheck) Failed: 55 CRLF lines in docs/dev/debt/libSceNKWebKitRequirements.md (unchanged by this commit)
Conventions and PR template (precheck) Passed
Live dependency and mergeability (precheck) Passed
ABI audit (by hand) passed_with_warnings, 27 findings, the same static test-helper set as before; none is an export
Test-quality heuristic (by hand) Two possible-unregistered-cpp-test findings; false positives, because the Xml test is registered via foreach in libSceNKWebKitRequirements/CMakeLists.txt
I/O fix Per-registration slots, rollback on a failed registration, and a full-table rejection; the tests cover two handlers, use of an earlier context after a later registration, a rejected registration and the output side
Build and tests Not run (no-build review)
Hosted checks Conventions queued at this head

Findings

Blocking:

  1. The debt document still uses CRLF line endings: 55 lines from commit 408a262b5e, unchanged by fbc2ded785. git diff --check fails on every line, and the file diff is a whole-file rewrite. Fix: convert the file to LF.

Non-blocking:

  1. A registration slot is never released, because libxml2 has no per-handler unregister. That is fine for the current use, but the debt entry should say so.
  2. The non-blocking points from my review at ec1fa3c51e (the global BUILD_SHARED_LIBS force in cmake/LibXml2.cmake, the libxml2 version wording in the debt entry, the C++ exceptions thrown through libjpeg's C frames, and the weak Psl.cpp assertions) still apply. This commit does not touch those files, and I did not re-check them at this head.

Not verified

  • Nothing was compiled or run; the test changes were read, not executed.
  • Hosted checks are queued at this head.
  • Console behaviour of the I/O callbacks is not measured.

To get this to a good state

Convert docs/dev/debt/libSceNKWebKitRequirements.md to LF and confirm git diff --check ba627802e2210187d417deb64c8f2fed8e33011f HEAD is empty. The diff check and the I/O regressions will be rerun on the new head.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head fbc2ded merged onto main 291e02d · tested 2026-11-01 12:53 UTC · 7 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,093 passed · 9 failed (0 new, 9 also fail on main) · 57 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1240 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 103.8 s, ⚠️ agc_driver_graphics_maps_fallback 38.2 s, ⚠️ agc_driver_graphics 37.9 s, ⚠️ public_headers 30.7 s
Failing on main too (9, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_idle_publish_wait
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (5 IDs in 717 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (717 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 709 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (57)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not 
Reproduce
git fetch origin fbc2ded78536e77667752a3c29c0201457a53fc4 291e02d4877784d03d346962af51713b9df63b91
git checkout 291e02d4877784d03d346962af51713b9df63b91 && git merge --no-ff fbc2ded78536e77667752a3c29c0201457a53fc4
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@PrivacyOnion

Copy link
Copy Markdown
Author

@MarcZX10 thanks, all addressed in 967de9b.

Line endings (blocking). docs/dev/debt/libSceNKWebKitRequirements.md is stored with LF again (git ls-files --eol shows i/lf). git diff --check ba627802e2210187d417deb64c8f2fed8e33011f 967de9b is now empty. The file's diff against #14374 is back to the real changes only.

Non-blocking points:

  • BUILD_SHARED_LIBS. cmake/LibXml2.cmake now uses a plain set(BUILD_SHARED_LIBS OFF) inside add_bundled_libxml2(). libxml2 2.9.14 requires CMake 3.15, so CMP0077 is NEW and its option(BUILD_SHARED_LIBS … ON) takes the function-scoped value without touching the cache.

  • Slot lifetime. The libxml2 debt entry now says that each successful registration keeps one of the 15 slots for the life of the process, as libxml2 keeps its table entry, and that no export removes a registration.

  • libxml2 version wording. The name-provenance sentence now says the names were searched for in the libxml2 2.13.4 identifiers, and that the implemented libxml2 exports run the bundled 2.9.14.

  • Exceptions through libjpeg's C frames. jpeg_turbo is built by core/Decoder/Jpeg as an ExternalProject with its own default flags. The libraries' -fno-asynchronous-unwind-tables does not apply to it, so on x86-64 GCC its C frames keep their unwind tables. tests/Jpeg.cpp now exercises this path, which no test did before: a stream without SOI makes libjpeg raise JERR_NO_SOI inside jpeg_read_header. The test checks that the exception crosses libjpeg back to the adapter, that the guest error_exit is called once with msg_code == JERR_NO_SOI, that jpeg_read_header returns 0, and that the object can still be destroyed. It passes in the host harness, MinGW 13.1 x86-64 (SEH). The Linux and Windows CI jobs will run the same test.

  • Psl assertions. The builtin checks are now exact:

    • psl_builtin_sha1sum is 40 lowercase hex digits;
    • psl_dist_filename equals the configured PSL_DISTFILE (public_suffix_list.dat);
    • psl_builtin_outdated is 0 or 1;
    • the wildcard and exception counts are strictly positive (the list has *.ck and !www.ck).

    The loaded-list counts were already exact: 4 suffixes, 1 exception and 1 wildcard.

Host harness: the zlib, jpeg, xml and psl tests pass. The changed tests compile with the real APS5_VABI, and conventions report no errors (the one warning is the zlib entry from #14374).

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 967de9b merged onto main 291e02d · tested 2026-11-01 13:09 UTC · 7 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,093 passed · 9 failed (0 new, 9 also fail on main) · 57 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1240 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 119.6 s, ⚠️ agc_driver_graphics_maps_fallback 39.3 s, ⚠️ agc_driver_graphics 36.4 s, ⚠️ public_headers 30.5 s
Failing on main too (9, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_idle_publish_wait
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (5 IDs in 717 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (717 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 709 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (57)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not 
Reproduce
git fetch origin 967de9b8893ca8dae0c6dc1da04728233073f943 291e02d4877784d03d346962af51713b9df63b91
git checkout 291e02d4877784d03d346962af51713b9df63b91 && git merge --no-ff 967de9b8893ca8dae0c6dc1da04728233073f943
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

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.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 14435 .gitmodules L78

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1835 .gitmodules
PR 5021 .gitmodules
PR 9855 .gitmodules
PR 13578 .gitmodules
PR 14212 docs/dev/debt/libSceNKWebKitRequirements.md

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blocking findings

The CRLF line endings that blocked the previous head are fixed at head 967de9b, and the three changes in this delta (a CMake cache change, a JPEG error-exit test and tighter PSL assertions) read correctly; hosted checks are still pending, so I am not approving.

What I ran

  • Exact head 967de9b8893ca8dae0c6dc1da04728233073f943 (one commit after fbc2ded785, the head of my earlier request-changes review 5728122842), base ba627802e2210187d417deb64c8f2fed8e33011f.
  • Environment: Linux, /usr/bin/python3 3.11.2, no build.
  • Precheck at the new head: python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421 --output-dir /dev/shm/reviewer-run/precheck/14421b
  • Delta read with the compare API (fbc2ded7...967de9b8): four files, cmake/LibXml2.cmake, Jpeg.cpp, Psl.cpp, and the debt doc.
  • Debt doc line endings checked with gh api contents at both heads: 0 CR bytes at 967de9b8, 57 at fbc2ded7.
  • ABI audit by hand against the local repository, at both heads, to compare findings (the helper is absent from the head tree):
    python3 tools/pr_abi_audit.py --repo /marc/AnyPS5 --base ba627802e2210187d417deb64c8f2fed8e33011f --head 967de9b8893ca8dae0c6dc1da04728233073f943 --json
  • Test-quality helper on Jpeg.cpp and Psl.cpp from a head extract: each reports one literal-path registration heuristic; both are already registered in the library's test target.

Results

Precheck report, checks table, unedited (/dev/shm/reviewer-run/precheck/14421b/report.md):

Check Status Command / reason
build_and_compile not_applicable The precheck deliberately does not configure, build, or compile AnyPS5.
diff_check passed git diff --check ba627802e2210187d417deb64c8f2fed8e33011f...967de9b8893ca8dae0c6dc1da04728233073f943
conventions_and_pr_template passed /usr/bin/python3 tools/check_conventions.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head 967de9b8893ca8dae0c6dc1da04728233073f943 --title feat(libSceNKWebKitRequirements): implement the libjpeg, libxml2 and libpsl exports --body /dev/shm/reviewer-run/precheck/14421b/pr-body.md
changed_export_abi_audit failed /usr/bin/python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head 967de9b8893ca8dae0c6dc1da04728233073f943 --json
python_syntax not_applicable No changed Python implementation files.
related_python_unit_tests not_applicable No changed tools/ module has an existing sibling tools/tests/test_<module>.py suite.
python_test_registration not_applicable No changed test_*.py files.
live_dependency_and_mergeability_check passed /usr/bin/python3 /marc/AnyPS5/tools/pr_stack_check.py 14421 --repo boykopovar/AnyPS5 --output-dir /dev/shm/reviewer-run/precheck/14421b/stack
build_dependent_cpp_tests not_applicable Not run: C++ tests need exact-head binaries, which require a build; the precheck does not build.
test_quality_heuristics failed /usr/bin/python3 /dev/shm/reviewer-run/precheck/14421b/head-967de9b8893c-7d3f2d4f/tools/pr_test_quality.py --root /dev/shm/reviewer-run/precheck/14421b/head-967de9b8893c-7d3f2d4f --json .gitmodules 3rdparty/libpsl 3rdparty/libxml2 cmake/LibPsl.cmake cmake/LibXml2.cmake core/libs/prx/libSceNKWebKitRequirements/CMakeLists.txt core/libs/prx/libSceNKWebKitRequirements/Export.cpp core/libs/prx/libSceNKWebKitRequirements/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/Xml.cpp core/libs/prx/libSceNKWebKitRequirements/Zlib.cpp core/libs/prx/libSceNKWebKitRequirements/include/GuestJpeg.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestPsl.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestXml.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestZStream.hpp core/libs/prx/libSceNKWebKitRequirements/include/Identified.hpp core/libs/prx/libSceNKWebKitRequirements/tests/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Xml.cpp core/libs/tests/GuestWebKitZlib.cpp docs/dev/debt/libSceNKWebKitRequirements.md
negative_controls not_applicable No changed Python test module in tools/ can be run as a no-build base-source control.

Manual results: the ABI audit at 967de9b8 reports passed_with_warnings with 28 findings and 195 symbols; at fbc2ded7 it reported 27. The one new finding is RecordingErrorExit, the test callback described below, flagged lexically as a definition with no header declaration.

Hosted at review time: conventions was cancelled and re-queued, and eligibility, library-portability and report were queued. No approval until these pass.

Negative control

Not run. The PR does not claim a negative control for the new JPEG test.

Findings

Blocking: none.

Non-blocking:

  • TestGuestErrorExit expects jpeg_read_header to return 0 after the guest error_exit returns. libjpeg's contract is that error_exit does not return, so this asserts behaviour of the bundled libjpeg-turbo when a callback returns. That is worth a comment in the test, and the assertion should be confirmed on a build.
  • RecordingErrorExit is marked APS5_VABI inside the anonymous namespace; the ABI audit's new finding comes from that marker. Dropping the marker, or stating in the debt doc that it is test-local, would clear it.
  • The PSL assertions wildCount > 0 and excCount > 0 assume the bundled list contains wildcard and exception rules such as *.ck and !www.ck. The bundled list is in the libpsl submodule, which is not in this checkout, so I did not check it.
  • cmake/LibXml2.cmake changes set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) to a plain set. That is a function-scope variable, so it no longer writes the cache. Confirm on a configure that the bundled libxml2 add_subdirectory runs inside that function.

Sources

  • Debt doc at head, LF: https://github.com/boykopovar/AnyPS5/blob/967de9b8893ca8dae0c6dc1da04728233073f943/docs/dev/debt/libSceNKWebKitRequirements.md
  • JPEG test at head:
    int g_errorExitCalls = 0;
    int g_errorExitCode = -1;
    void APS5_VABI RecordingErrorExit(NKWebKit::GuestCommonPtr cinfo) {
    ++g_errorExitCalls;
    g_errorExitCode = cinfo->err->msg_code;
    }
    void TestGuestErrorExit() {
    const uint8_t notJpeg[] = {'N', 'O', 'T', ' ', 'J', 'P', 'E', 'G', 0, 0, 0, 0};
    NKWebKit::GuestJpegErrorMgr jerr{};
    jpeg_std_error_nid_postfix(&jerr);
    jerr.error_exit = RecordingErrorExit;
    NKWebKit::GuestJpegDecompress guest_cinfo{};
    guest_cinfo.err = &jerr;
    jpeg_CreateDecompress_nid_postfix(&guest_cinfo, 62, sizeof(NKWebKit::GuestJpegDecompress));
    NKWebKit::GuestJpegSourceMgr srcMgr{};
    srcMgr.next_input_byte = notJpeg;
    srcMgr.bytes_in_buffer = sizeof(notJpeg);
    srcMgr.init_source = GuestInitSource;
    srcMgr.fill_input_buffer = GuestFillInputBuffer;
    srcMgr.skip_input_data = GuestSkipInputData;
    srcMgr.resync_to_restart = GuestResyncToRestart;
    srcMgr.term_source = GuestTermSource;
    guest_cinfo.src = &srcMgr;
    AssertTrue(jpeg_read_header_nid_postfix(&guest_cinfo, 1) == 0, "jpeg_read_header should end early after error_exit returns");
    AssertTrue(g_errorExitCalls == 1, "the guest error_exit should be called once");
    AssertTrue(g_errorExitCode == JERR_NO_SOI, "the guest error_exit should see JERR_NO_SOI");
    jpeg_destroy_decompress_nid_postfix(&guest_cinfo);
  • CMake change at head:
    set(BUILD_SHARED_LIBS OFF)

Not verified

  • No build, so the JPEG test and the PSL test were not compiled or run; their results are unknown on this head.
  • libjpeg-turbo's behaviour when error_exit returns is not verified.
  • The bundled PSL rules were not checked against the libpsl submodule.
  • Hosted: the CI jobs listed above were not complete at review time.

@PrivacyOnion

Copy link
Copy Markdown
Author

@MarcZX10 thanks. On the four non-blocking points, at 967de9b:

  • error_exit that returns. The test pins the adapter's documented behaviour, not libjpeg's: libjpeg raises the error, the adapter's host error_exit throws, the export catches it, calls the guest error_exit and, if that returns, ends the export with 0. The debt entry says so ("an error_exit that returns ends the export early"). libjpeg-turbo never sees the guest callback return, because its own error_exit is the throwing host one. The test was built and run: tests/Jpeg.cpp passes in the host harness (MinGW 13.1), including this case, and JERR_NO_SOI is what libjpeg raises for these bytes in first_marker. The repository has no comments in code, so the explanation stays in the debt entry.
  • RecordingErrorExit and APS5_VABI. It is installed as the guest jpeg_error_mgr::error_exit, so it must have the guest calling convention, like the other test callbacks in the same file (GuestInitSource, GuestFillInputBuffer, …), which the audit flagged in the earlier rounds as well. Dropping the marker would make the adapter call it with the wrong convention on Windows. It is a static-linkage test helper, not an export.
  • PSL wildcard and exception rules. The pinned list (3rdparty/libpsl/list at 5db9b65) has *.ck at line 752 and !www.ck at line 753, so the builtin table has at least one wildcard and one exception. The test also checks foo.ck (public suffix) and www.ck (not one) on the list it writes itself.
  • BUILD_SHARED_LIBS. The bundled add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/libxml2 …) is at line 46 of cmake/LibXml2.cmake, inside add_bundled_libxml2() (lines 1–60), so it sees the function-scope OFF. libxml2 2.9.14 requires CMake 3.15, so CMP0077 is NEW and its option(BUILD_SHARED_LIBS … ON) keeps that value. A full configure is not possible on this machine (SDL2/ffmpeg submodules missing), so the Windows and Linux CI jobs will confirm it.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Progress report (d1ae628 → b75abdf)

📈 System libraries: 26.66% (+0.19%, +70 implemented functions)

✅ 70 implemented
Library Name
libSceNKWebKitRequirements deflateBound_nid_postfix
libSceNKWebKitRequirements deflateEnd_nid_postfix
libSceNKWebKitRequirements deflateInit2__nid_postfix
libSceNKWebKitRequirements deflateReset_nid_postfix
libSceNKWebKitRequirements deflate_nid_postfix
libSceNKWebKitRequirements inflateEnd_nid_postfix
libSceNKWebKitRequirements inflateInit2__nid_postfix
libSceNKWebKitRequirements inflateReset_nid_postfix
libSceNKWebKitRequirements inflate_nid_postfix
libSceNKWebKitRequirements jpeg_CreateDecompress_nid_postfix
libSceNKWebKitRequirements jpeg_calc_output_dimensions_nid_postfix
libSceNKWebKitRequirements jpeg_consume_input_nid_postfix
libSceNKWebKitRequirements jpeg_destroy_decompress_nid_postfix
libSceNKWebKitRequirements jpeg_finish_decompress_nid_postfix
libSceNKWebKitRequirements jpeg_finish_output_nid_postfix
libSceNKWebKitRequirements jpeg_has_multiple_scans_nid_postfix
libSceNKWebKitRequirements jpeg_input_complete_nid_postfix
libSceNKWebKitRequirements jpeg_read_header_nid_postfix
libSceNKWebKitRequirements jpeg_read_scanlines_nid_postfix
libSceNKWebKitRequirements jpeg_resync_to_restart_nid_postfix
libSceNKWebKitRequirements jpeg_save_markers_nid_postfix
libSceNKWebKitRequirements jpeg_start_decompress_nid_postfix
libSceNKWebKitRequirements jpeg_start_output_nid_postfix
libSceNKWebKitRequirements jpeg_std_error_nid_postfix
libSceNKWebKitRequirements psl_builtin_file_time_nid_postfix
libSceNKWebKitRequirements psl_builtin_filename_nid_postfix
libSceNKWebKitRequirements psl_builtin_nid_postfix
libSceNKWebKitRequirements psl_builtin_outdated_nid_postfix
libSceNKWebKitRequirements psl_builtin_sha1sum_nid_postfix
libSceNKWebKitRequirements psl_check_version_number_nid_postfix
libSceNKWebKitRequirements psl_dist_filename_nid_postfix
libSceNKWebKitRequirements psl_free_nid_postfix
libSceNKWebKitRequirements psl_free_string_nid_postfix
libSceNKWebKitRequirements psl_get_version_nid_postfix
libSceNKWebKitRequirements psl_is_cookie_domain_acceptable_nid_postfix
libSceNKWebKitRequirements psl_is_public_suffix2_nid_postfix
libSceNKWebKitRequirements psl_is_public_suffix_nid_postfix
libSceNKWebKitRequirements psl_latest_nid_postfix
libSceNKWebKitRequirements psl_load_file_nid_postfix
libSceNKWebKitRequirements psl_load_fp_nid_postfix
libSceNKWebKitRequirements psl_registrable_domain_nid_postfix
libSceNKWebKitRequirements psl_str_to_utf8lower_nid_postfix
libSceNKWebKitRequirements psl_suffix_count_nid_postfix
libSceNKWebKitRequirements psl_suffix_exception_count_nid_postfix
libSceNKWebKitRequirements psl_suffix_wildcard_count_nid_postfix
libSceNKWebKitRequirements psl_unregistrable_domain_nid_postfix
libSceNKWebKitRequirements uncompress_nid_postfix
libSceNKWebKitRequirements xmlByteConsumed_nid_postfix
libSceNKWebKitRequirements xmlCreateMemoryParserCtxt_nid_postfix
libSceNKWebKitRequirements xmlCreatePushParserCtxt_nid_postfix
libSceNKWebKitRequirements xmlCtxtGetLastError_nid_postfix
libSceNKWebKitRequirements xmlCtxtUseOptions_nid_postfix
libSceNKWebKitRequirements xmlDictLookup_nid_postfix
libSceNKWebKitRequirements xmlFreeDoc_nid_postfix
libSceNKWebKitRequirements xmlFreeParserCtxt_nid_postfix
libSceNKWebKitRequirements xmlGetDocEntity_nid_postfix
libSceNKWebKitRequirements xmlGetPredefinedEntity_nid_postfix
libSceNKWebKitRequirements xmlInitParser_nid_postfix
libSceNKWebKitRequirements xmlParseChunk_nid_postfix
libSceNKWebKitRequirements xmlParseContent_nid_postfix
libSceNKWebKitRequirements xmlRegisterInputCallbacks_nid_postfix
libSceNKWebKitRequirements xmlRegisterOutputCallbacks_nid_postfix
libSceNKWebKitRequirements xmlSAX2EndDocument_nid_postfix
libSceNKWebKitRequirements xmlSAX2EntityDecl_nid_postfix
libSceNKWebKitRequirements xmlSAX2InternalSubset_nid_postfix
libSceNKWebKitRequirements xmlSAX2StartDocument_nid_postfix
libSceNKWebKitRequirements xmlStopParser_nid_postfix
libSceNKWebKitRequirements xmlStrdup_nid_postfix
libSceNKWebKitRequirements xmlStrndup_nid_postfix
libSceNKWebKitRequirements xmlSwitchEncoding_nid_postfix

Changed libraries (implemented exports)

Library Before After Change
libSceNKWebKitRequirements 0/165 (0.00%) 70/165 (42.42%) +42.42%

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked — new head, no blocking findings

The CRLF line endings and the libxml2 callback-slot problems that my earlier request-changes reviews raised are both resolved at head 8cabcc0; the only new change is a rename of five static default callbacks, and hosted checks are still pending, so this is a COMMENTED result, not an approval.

What I ran

  • Exact head 8cabcc04effd5bff71a887b1a70e2753de68924b, base ba627802e2 (the precheck base). The head moved from 967de9b889 (my last posted result, review 5728363081). The one new commit, 8cabcc04ef, is a refactor in core/libs/prx/libSceNKWebKitRequirements/Jpeg.cpp.
  • Environment: Linux, /usr/bin/python3 3.11.2, no build.
  • Precheck: python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421 --output-dir /dev/shm/reviewer-run/precheck/14421c
  • Delta read with the compare API (967de9b8...8cabcc04): one file, Jpeg.cpp, 10 lines changed.
  • CRLF check: the debt file docs/dev/debt/libSceNKWebKitRequirements.md at this head has 0 CR bytes (fetched with gh api); at fbc2ded785 it had 57.
  • libxml2 callbacks read at head: core/libs/prx/libSceNKWebKitRequirements/Xml.cpp lines 745-785. xmlRegisterInputCallbacks_nid_postfix and xmlRegisterOutputCallbacks_nid_postfix store each guest callback set in its own slot (g_inputSlots[slot], g_outputSlots[slot]), bind it to a per-slot host trampoline (InputTrampolines[slot], OutputTrampolines[slot]), refuse when the slot count is reached, and clear the slot if libxml2 rejects the registration. Both slot counts are 15 (XmlInputSlotCount, XmlOutputSlotCount, lines 45-46).
  • ABI audit by hand (helper absent from the head tree): python3 tools/pr_abi_audit.py --repo /marc/AnyPS5 --base ba627802e2 --head 8cabcc04effd5bff71a887b1a70e2753de68924b --json (exit 0; passed_with_warnings; 23 public-declaration-not-found findings, all lexical; 195 symbols).

Results

Precheck report, checks table, unedited (/dev/shm/reviewer-run/precheck/14421c/report.md):

Check Status Command / reason
build_and_compile not_applicable The precheck deliberately does not configure, build, or compile AnyPS5.
diff_check passed git diff --check ba627802e2210187d417deb64c8f2fed8e33011f...8cabcc04effd5bff71a887b1a70e2753de68924b
conventions_and_pr_template passed /usr/bin/python3 tools/check_conventions.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head 8cabcc04effd5bff71a887b1a70e2753de68924b --title feat(libSceNKWebKitRequirements): implement the libjpeg, libxml2 and libpsl exports --body /dev/shm/reviewer-run/precheck/14421c/pr-body.md
changed_export_abi_audit failed /usr/bin/python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head 8cabcc04effd5bff71a887b1a70e2753de68924b --json
python_syntax not_applicable No changed Python implementation files.
related_python_unit_tests not_applicable No changed tools/ module has an existing sibling tools/tests/test_<module>.py suite.
python_test_registration not_applicable No changed test_*.py files.
live_dependency_and_mergeability_check passed /usr/bin/python3 /marc/AnyPS5/tools/pr_stack_check.py 14421 --repo boykopovar/AnyPS5 --output-dir /dev/shm/reviewer-run/precheck/14421c/stack
build_dependent_cpp_tests not_applicable Not run: C++ tests need exact-head binaries, which require a build; the precheck does not build.
test_quality_heuristics failed /usr/bin/python3 /dev/shm/reviewer-run/precheck/14421c/head-8cabcc04effd-d0337903/tools/pr_test_quality.py --root /dev/shm/reviewer-run/precheck/14421c/head-8cabcc04effd-d0337903 --json .gitmodules 3rdparty/libpsl 3rdparty/libxml2 cmake/LibPsl.cmake cmake/LibXml2.cmake core/libs/prx/libSceNKWebKitRequirements/CMakeLists.txt core/libs/prx/libSceNKWebKitRequirements/Export.cpp core/libs/prx/libSceNKWebKitRequirements/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/Xml.cpp core/libs/prx/libSceNKWebKitRequirements/Zlib.cpp core/libs/prx/libSceNKWebKitRequirements/include/GuestJpeg.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestPsl.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestXml.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestZStream.hpp core/libs/prx/libSceNKWebKitRequirements/include/Identified.hpp core/libs/prx/libSceNKWebKitRequirements/tests/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Xml.cpp core/libs/tests/GuestWebKitZlib.cpp docs/dev/debt/libSceNKWebKitRequirements.md
negative_controls not_applicable No changed Python test module in tools/ can be run as a no-build base-source control.

Negative control

Not applicable to this head: the change is a rename.

Findings

Blocking: none.

Non-blocking:

  • The five default callbacks (DefaultGuestErrorExit, DefaultGuestEmitMessage, DefaultGuestOutputMessage, DefaultGuestFormatMessage, DefaultGuestResetErrorMgr) are static, so the rename changes no linkage or behaviour; it only changes what tools/progress.py counts. A rename to satisfy a counting tool is a workaround. The tool should skip static functions, and the rename can then be dropped.

Stale reviews: my earlier CHANGES_REQUESTED reviews 5728052613 (at ec1fa3c51e) and 5728122842 (at fbc2ded785) no longer describe this head. 5728052613 raised two things at ec1fa3c51e: libxml2 registrations sharing one set of guest callbacks, and CRLF debt-file line endings. The shared-callback defect is fixed by the per-registration slots at fbc2ded785 (read above); the CRLF item is fixed at 967de9b889 (0 CR bytes). 5728122842 raised the CRLF item at fbc2ded785, fixed at 967de9b889. Both should be dismissed or superseded by the author or coordinator.

Sources

  • Registration code at head:
    int APS5_VABI xmlRegisterInputCallbacks_nid_postfix(NKWebKit::GuestInputMatchCallback matchFunc, NKWebKit::GuestInputOpenCallback openFunc, NKWebKit::GuestInputReadCallback readFunc, NKWebKit::GuestInputCloseCallback closeFunc) {
    std::lock_guard<std::mutex> lock(g_ioMutex);
    if (g_inputSlotsUsed == XmlInputSlotCount) {
    return -1;
    }
    const std::size_t slot = g_inputSlotsUsed;
    g_inputSlots[slot] = {matchFunc, openFunc, readFunc, closeFunc};
    const auto& host = InputTrampolines[slot];
    const int result = xmlRegisterInputCallbacks(matchFunc ? host.match : nullptr, openFunc ? host.open : nullptr,
    readFunc ? host.read : nullptr, closeFunc ? host.close : nullptr);
    if (result < 0) {
    g_inputSlots[slot] = {};
    return result;
    }
    ++g_inputSlotsUsed;
    return result;
    }
    int APS5_VABI xmlRegisterOutputCallbacks_nid_postfix(NKWebKit::GuestOutputMatchCallback matchFunc, NKWebKit::GuestOutputOpenCallback openFunc, NKWebKit::GuestOutputWriteCallback writeFunc, NKWebKit::GuestOutputCloseCallback closeFunc) {
    std::lock_guard<std::mutex> lock(g_ioMutex);
    if (g_outputSlotsUsed == XmlOutputSlotCount) {
    return -1;
    }
    const std::size_t slot = g_outputSlotsUsed;
    g_outputSlots[slot] = {matchFunc, openFunc, writeFunc, closeFunc};
    const auto& host = OutputTrampolines[slot];
    const int result = xmlRegisterOutputCallbacks(matchFunc ? host.match : nullptr, openFunc ? host.open : nullptr,
    writeFunc ? host.write : nullptr, closeFunc ? host.close : nullptr);
    if (result < 0) {
    g_outputSlots[slot] = {};
    return result;
    }
    ++g_outputSlotsUsed;
    return result;
    }
    void APS5_VABI xmlSAX2EndDocument_nid_postfix(void* ctx) {
    auto* b = FindBinding(reinterpret_cast<NKWebKit::GuestParserCtxt*>(ctx));
    xmlSAX2EndDocument(b ? b->host_ctxt : ctx);
    }
  • Slot counts at head:
    constexpr std::size_t XmlInputSlotCount = 15;
    constexpr std::size_t XmlOutputSlotCount = 15;
  • The refactor at head: https://github.com/boykopovar/AnyPS5/blob/8cabcc04effd5bff71a887b1a70e2753de68924b/core/libs/prx/libSceNKWebKitRequirements/Jpeg.cpp

Not verified

  • The pinned libxml2 callback limit (15) was verified in an earlier review of the same pin; the header was not re-fetched for this result.
  • The C++ tests (guest_nkwebkit_requirements and the Jpeg, Psl and Xml tests) were not built or run here.
  • The PSL assertions against the bundled list were not checked (from my earlier review).
  • Hosted: at this head conventions (one run cancelled, one queued), eligibility, labels, library-portability and report were queued at review time.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 8cabcc0 merged onto main 291e02d · tested 2026-11-01 14:47 UTC · 13 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,093 passed · 9 failed (0 new, 9 also fail on main) · 57 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1240 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 112.4 s, ⚠️ public_headers 42.1 s, ⚠️ agc_driver_graphics_maps_fallback 38.0 s, ⚠️ agc_driver_graphics 36.9 s
Failing on main too (9, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_idle_publish_wait
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (5 IDs in 717 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (717 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 709 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (57)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not 
Reproduce
git fetch origin 8cabcc04effd5bff71a887b1a70e2753de68924b 291e02d4877784d03d346962af51713b9df63b91
git checkout 291e02d4877784d03d346962af51713b9df63b91 && git merge --no-ff 8cabcc04effd5bff71a887b1a70e2753de68924b
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@THEMANnt THEMANnt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 8cabcc04effd5bff71a887b1a70e2753de68924b.

[P1] Unwrap guest FILE streams before passing them to libpsl — Psl.cpp:76–77. psl_load_fp_nid_postfix forwards its argument directly to host psl_load_fp. Guest stdio uses FileStream*, whose host FILE* is stored beyond the guest FILE prefix; the two pointers are not interchangeable. Reproduced on Windows with the pinned libpsl: loading the same two-rule suffix list through a host FILE passes, while passing a real FileStream wrapper segfaults. A separate control that unwraps the stream with GetStreamHandle passes. Please expose the guest stream type, unwrap it for libpsl, and make the regression use a guest stream rather than std::fopen's host pointer.

Validation: built the submitted shared library against the pinned JPEG/XML/PSL dependencies with MSYS2 GCC 16.2 Release; all four supplied suites pass. The supplied PSL test also passes standalone, but its FILE test uses a host FILE and misses this defect. Conventions and whitespace pass. The diagnostic control changes only the review harness; this PR's files are unchanged. Depends on #14374.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — changes requested

Three blocking defects at this head: a guest FILE* reaches a host FILE API, a guest document pointer dangles after its parser context is freed, and the default JPEG error callback returns after a fatal error.

What I ran

  • Head 8cabcc04effd5bff71a887b1a70e2753de68924b; PR base ba627802e2; merge base ba8dcc6ae8; 21 changed files, +4012/-351 (PR files API).
  • Read: all 21 changed files, split across five read-only subagents (Xml.cpp; Jpeg.cpp, GuestJpeg.hpp, tests/Jpeg.cpp; GuestXml.hpp, tests/Xml.cpp, Identified.hpp, GuestPsl.hpp; Zlib.cpp, GuestWebKitZlib.cpp, GuestZStream.hpp, Psl.cpp, tests/Psl.cpp; Export.cpp removals, CMake files, debt doc, submodule gitlinks). I re-read the three blocking sites and the CONTRIBUTING rule myself. The non-blocking items are the subagents' findings; I re-checked only the lines cited under Findings.
  • Precheck report at this exact head (head and base unchanged at completion). Two precheck rows (ABI audit, test quality) failed; I reran both helpers by hand on an archive of the head. Details below.

Results

Check Result Note
Diff check passed
Conventions and PR template passed
Mergeability and dependencies passed live check
Export ABI audit (by hand) passed 195 symbols, 0 alias mismatches; 23 warnings are test-file callbacks
Test quality (by hand) passed 4 flags are false positives: the tests are registered in CMake
Guest FILE handling failed host FILE API on a guest stream (finding 1)
Document lifetime failed pointer outlives its context (finding 2)
Error callbacks failed returns after a fatal error (finding 3)
Build and runtime tests not run no build
Negative controls not run needs a build
Hosted CI not run at review time Windows was in progress and macOS relinker jobs were queued; completed runs show no failure
Commands and unedited precheck output

Commands:

  • Precheck: as listed in the precheck table below (report at /dev/shm/reviewer-run/precheck/14421c/report.md).
  • ABI audit, by hand, from the main checkout: python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head 8cabcc04effd5bff71a887b1a70e2753de68924b --json. Result: status passed_with_warnings; 195 symbols; 0 alias mismatches; 23 findings, all of kind public-declaration-not-found. The 23 are defined only in tests/Jpeg.cpp (GuestFillInputBuffer, GuestInitSource, GuestResyncToRestart, GuestSkipInputData, GuestTermSource, RecordingErrorExit) and tests/Xml.cpp (IoClose, IoMatch, IoOpen, IoRead, OutClose, OutMatch, OutOpen, OutWrite, SaxCdataBlock, SaxCharacters, SaxComment, SaxEndDoc, SaxEndElementNs, SaxError, SaxSerror, SaxStartDoc, SaxStartElementNs).
  • Test quality, by hand, on an archive of the head: python3 tools/pr_test_quality.py --root <archive> --json tests/Jpeg.cpp tests/Psl.cpp tests/Xml.cpp core/libs/tests/GuestWebKitZlib.cpp. Result: 4 possible-unregistered-cpp-test flags. Registration exists: CMakeLists.txt line 21 (guest_webkit_zlib_tests) and line 31 (foreach over Jpeg, Xml, Psl).
  • Hosted checks: gh api repos/boykopovar/AnyPS5/commits/8cabcc04effd5bff71a887b1a70e2753de68924b/check-runs.

Precheck table (unedited):

Check Status Command / reason
build_and_compile not_applicable The precheck deliberately does not configure, build, or compile AnyPS5.
diff_check passed git diff --check ba627802e2210187d417deb64c8f2fed8e33011f...8cabcc04effd5bff71a887b1a70e2753de68924b
conventions_and_pr_template passed /usr/bin/python3 tools/check_conventions.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head 8cabcc04effd5bff71a887b1a70e2753de68924b --title feat(libSceNKWebKitRequirements): implement the libjpeg, libxml2 and libpsl exports --body /dev/shm/reviewer-run/precheck/14421c/pr-body.md
changed_export_abi_audit failed /usr/bin/python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head 8cabcc04effd5bff71a887b1a70e2753de68924b --json
python_syntax not_applicable No changed Python implementation files.
related_python_unit_tests not_applicable No changed tools/ module has an existing sibling tools/tests/test_<module>.py suite.
python_test_registration not_applicable No changed test_*.py files.
live_dependency_and_mergeability_check passed /usr/bin/python3 /marc/AnyPS5/tools/pr_stack_check.py 14421 --repo boykopovar/AnyPS5 --output-dir /dev/shm/reviewer-run/precheck/14421c/stack
build_dependent_cpp_tests not_applicable Not run: C++ tests need exact-head binaries, which require a build; the precheck does not build.
test_quality_heuristics failed /usr/bin/python3 /dev/shm/reviewer-run/precheck/14421c/head-8cabcc04effd-d0337903/tools/pr_test_quality.py --root /dev/shm/reviewer-run/precheck/14421c/head-8cabcc04effd-d0337903 --json .gitmodules 3rdparty/libpsl 3rdparty/libxml2 cmake/LibPsl.cmake cmake/LibXml2.cmake core/libs/prx/libSceNKWebKitRequirements/CMakeLists.txt core/libs/prx/libSceNKWebKitRequirements/Export.cpp core/libs/prx/libSceNKWebKitRequirements/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/Xml.cpp core/libs/prx/libSceNKWebKitRequirements/Zlib.cpp core/libs/prx/libSceNKWebKitRequirements/include/GuestJpeg.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestPsl.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestXml.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestZStream.hpp core/libs/prx/libSceNKWebKitRequirements/include/Identified.hpp core/libs/prx/libSceNKWebKitRequirements/tests/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Xml.cpp core/libs/tests/GuestWebKitZlib.cpp docs/dev/debt/libSceNKWebKitRequirements.md
negative_controls not_applicable No changed Python test module in tools/ can be run as a no-build base-source control.

Negative control

Not run. The new tests are C++ and need a build.

Findings

Blocking

  1. A guest FILE is passed to a host FILE API.* Identified.hpp line 52 declares psl_load_fp_nid_postfix(FILE* fp) with the host FILE, and Psl.cpp lines 76-78 pass it unchanged to libpsl's psl_load_fp. In this library fopen returns a FileStream*, and the host std::FILE* sits behind the guest prefix (FileStream.hpp lines 64-68), so libpsl would run stdio on a FileStream object. tests/Psl.cpp lines 103-105 use the host std::fopen, so the test cannot catch this. Smallest fix: take the guest stream type and pass its native handle; add a test that goes through the libc fopen path.

  2. A document outlives its parser context. Xml.cpp line 190 sets g->myDoc = &b->guest_doc, and guest_doc is a member of the binding (line 37). xmlFreeParserCtxt_nid_postfix (lines 694-703) moves the binding out of the map (line 696), frees host_ctxt (line 701), and destroys the binding, including guest_doc, when it returns. A guest that takes ctxt->myDoc, frees the context and keeps using the document reads freed memory. Smallest fix: give the guest document storage its own owner, released with the document, not with the binding; add a test that frees the context before the document.

  3. The default JPEG error callback returns after a fatal error. Jpeg.cpp lines 152-156 (DefaultGuestErrorExit_nid_no_patch) only call output_message and return. HandleException (lines 314-329) calls error_exit (line 326) and returns; the wrapper jpeg_calc_output_dimensions (lines 470-480) catches JpegException (line 477), calls HandleException (line 478) and returns normally, so a fatal decode error becomes a normal return. CONTRIBUTING.md line 7 requires each function to do what it is supposed to or throw. Smallest fix: make the default error_exit throw. tests/Jpeg.cpp replaces error_exit (line 242), so the default path is not tested; add a test for it.

Non-blocking (subagent findings; line numbers as reported except where cited above)

  • JPEG: the jpeg_CreateDecompress catch does not copy msg_code (374-378). s_std_inited is set without synchronisation (338-341). Source-manager callbacks return success when the guest callback is NULL (93-122). The exception object leaks if a guest error_exit longjmps out of the catch (423).
  • XML: the Host callbacks call guest code without SyncHostToGuest (781-799). A reentrant SyncGuestToHost can restore a stale spaceTab (663, 730, 739, 819). h->charset = h->charset should read g->charset (226). Six SAX slots are never forwarded and host_sax starts zeroed (502-528). guest_ctxt.sax is never set (608-625). FindBinding releases the mutex before returning a raw pointer (68-76). InitHostSax sets initialized unconditionally (504).
  • Zlib (stacked on #14374): the licence blocks and attribution comment (Zlib.cpp 1-35) and the adapted uncompress (231-269) are maintainer questions under the third-party code rules; CONVENTIONS allows only debt comments, but the same kind of header exists in libc sources. The branch at 151 leaks on a path the pinned zlib does not reach; a re-initialisation leaks the old stream (148). The totals are checked only on zero-initialised streams (GuestWebKitZlib.cpp 102-108 and 122-128).
  • PSL: the outdated check accepts 0 or 1 and so proves nothing (tests/Psl.cpp 40-41). The builtin list name returned at Psl.cpp 20-22 comes from the build configuration; whether it embeds a build-host path was not confirmed. IDN support is disabled, so the version string reports "no IDNA support", and the debt doc does not record it.
  • Build: cmake/LibPsl.cmake requires Python 3 at every configure. cmake/LibXml2.cmake keeps the libxml2 install rules and turns off XInclude, XPath, Reader, Writer, zlib and gzip input; the debt doc lists none of this.
  • Tests: TestErrorHandling never asserts the error callback (tests/Xml.cpp 174); the xmlSwitchEncoding result is discarded (200-201); the external-entity text is not checked (344-358); the progressive JPEG test does no pixel comparison and has unbounded loops (182-226).

Sources

Not verified

  • Guest call patterns (not in the repository): whether a guest takes myDoc and frees the context, and whether a guest's error_exit longjmps.
  • libxml2 2.9.14 ownership of myDoc on context free was not checked against the pinned source. The submodule is not checked out; the pin itself was checked.
  • The libpsl and libxml2 sources are not checked out locally; the NID values were not recomputed here (the names are unchanged between base and head).
  • A subagent report said warning counts and trace level are not propagated to the guest; the code at Jpeg.cpp lines 323-324 does copy them, so that claim is not carried forward.
  • Build, tests and negative controls were not run. Hosted CI was still running at review time.

To get this to a good state

  • Fix the three blocking items, each with a test: psl_load_fp through the libc stream path; a document read after its context is freed; the default JPEG error_exit throws.
  • Rerun the precheck and the Jpeg, Xml and Psl test executables on the new head.

@PrivacyOnion

Copy link
Copy Markdown
Author

@MarcZX10 @THEMANnt thanks, the three blocking items are fixed in a320213, each with a regression.

1. Guest FILE* to libpsl. psl_load_fp_nid_postfix now takes the guest FileStream* (declared through prx/libc/include/FileStream.hpp in Identified.hpp). It passes GetNativeStream(fp) to libpsl and then calls SyncStatus(), so the guest EOF and error flags reflect libpsl's reads. A null stream returns null, as libpsl does. tests/Psl.cpp now opens the list with the guest fopen_nid_postfix, checks the loaded rules, checks that feof_nid_postfix is set afterwards, and closes with fclose_nid_postfix. I also ran a host harness that wraps a real FileStream around a file, with libpsl 0.21.5 built from the pinned sources: org, foo.ck (wildcard) and www.ck (exception) resolve correctly.

2. Document outliving its context. The binding no longer holds the guest document. g_guestDocs owns one GuestDoc per host document, and the guest myDoc points to it. xmlFreeDoc releases it together with the host document, and xmlFreeParserCtxt no longer touches it. This matches libxml2, where the caller owns myDoc. The new TestDocumentOutlivesContext:

  • parses a document with an internal entity, detaches myDoc and frees the context;
  • parses a second document in a new context and frees that one too;
  • then checks that the two documents are distinct, that the first one keeps its type, its self-pointer, its version and its own entity, and that it doesn't see the second document's entity.

Against the previous Xml.cpp, this test fails on "a later context must not reuse a document that is still alive": the second binding reuses the freed one's storage. With the fix it passes.

3. Default error_exit. The error_exit that jpeg_std_error installs now calls output_message and format_message, then throws std::runtime_error("libjpeg: <message>"), where libjpeg would call exit. A fatal error therefore no longer turns into a normal return. A guest error_exit keeps the documented adapter behaviour. The new TestDefaultErrorExitThrows keeps the default handler, feeds a stream without SOI, and checks that jpeg_read_header throws with the formatted message and msg_code == JERR_NO_SOI. It fails against the previous Jpeg.cpp and passes now. The debt entry describes the default handler.

Verification:

  • The Xml and Jpeg tests and the Psl harness pass in a host build with WinLibs GCC 16.1. libxml2 2.9.14 was built from the pinned submodule; libjpeg-turbo comes from the repository build.
  • Every changed source and test compiles with the real APS5_VABI and -Wall -Wextra, with no warnings.
  • test_public_headers.py --audit-only (399 libraries, 26181 exports), check_conventions.py (0 errors; the one warning is still the zlib entry from feat(libSceNKWebKitRequirements): implement the zlib exports #14374) and git diff --check pass.
  • The guest test executables, tests/Psl.cpp with the real libc fopen included, are left to CI.

The non-blocking items are not part of this commit.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head a320213 merged onto main 291e02d · tested 2026-11-01 18:05 UTC · 10 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,092 passed · 10 failed (0 new, 9 also fail on main, 1 unstable on main) · 57 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1240 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.0 s, ⚠️ agc_driver_graphics_maps_fallback 42.1 s, ⚠️ agc_driver_graphics 40.4 s, ⚠️ unwrap_self 34.5 s, ⚠️ public_headers 31.5 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 3 of the last 4 main runs
Failing on main too (9, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_idle_publish_wait
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (5 IDs in 717 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (717 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 709 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (57)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not 
Reproduce
git fetch origin a32021388e2d09040667e76c72f3fa56d8d38672 291e02d4877784d03d346962af51713b9df63b91
git checkout 291e02d4877784d03d346962af51713b9df63b91 && git merge --no-ff a32021388e2d09040667e76c72f3fa56d8d38672
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@lost9409

lost9409 commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Independent Windows integrated-build result for a320213: build succeeds, but the JPEG test aborts.

Environment:

  • Windows 11 Pro, build 26200
  • WinLibs MinGW-w64 GCC 15.2.0, UCRT/POSIX/SEH
  • CMake 4.3.1, Ninja, Release, BUILD_TESTING=ON
  • CMake's ANYPS5_MINGW_DWARF_EXCEPTIONS check succeeds.

Built successfully:
guest_webkit_zlib_tests
guest_nk_req_jpeg_tests
guest_nk_req_xml_tests
guest_nk_req_psl_tests

CTest results:

  • guest_webkit_zlib: PASS
  • guest_nk_req_xml: PASS
  • guest_nk_req_psl: PASS
  • guest_nk_req_jpeg: abort, exit code 0xc0000409

The JPEG log reports:
[libc] unhandled exception 'N12_GLOBAL__N_113JpegExceptionE'
terminate called after throwing an instance of '(anonymous namespace)::JpegException'
FATAL: abort() (SIGABRT)

This is the internal exception thrown by HostErrorExit, not the std::runtime_error expected from the default guest error handler. The source has catch handlers for JpegException, so exception propagation in the integrated Windows build seems worth investigating; the exact cause is not established.

Reproduction after configuring the project:

cmake --build build --parallel 8 --target guest_webkit_zlib_tests guest_nk_req_jpeg_tests guest_nk_req_xml_tests guest_nk_req_psl_tests
ctest --test-dir build -R "^(guest_webkit_zlib|guest_nk_req_(jpeg|xml|psl))$" --parallel 1 -V

No source changes were applied. This validates only the focused build/tests, not title execution.

AI-assisted: yes (ChatGPT), for commands, log analysis and this report. Tests executed locally on my Windows machine.

AnyPS5-14421-CTest.log

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — changes requested

The three blockers from my earlier review are fixed at this head, but the Windows job still aborts in guest_nk_req_jpeg (a libjpeg error path throws through C frames), as it did at 8cabcc0. This review at a320213 supersedes 5728873006 (8cabcc0) for the current head.

What I ran

  • Head a32021388e2d09040667e76c72f3fa56d8d38672; base ba627802e2; merge base ba8dcc6ae8; 21 changed files, +4101/-351, 9 commits (PR files API).
  • Change since my last review head 8cabcc04ef: one commit (a32021388e), 8 files, +101/-12. I read the whole delta with context myself and checked each fix against the libc FileStream API and the libjpeg error path. The other 13 PR files are byte-identical to 8cabcc04ef (verified with git diff --stat between the two heads); the five read-only subagent reports from that review still cover them. I did not re-read those 13 files.
  • Precheck at this exact head (head and base unchanged at completion). Two precheck rows (ABI audit, test quality) failed because their helpers are not in the head tree; I reran both by hand on an archive of the head. Details below.
  • Hosted Windows job logs at this head and at 8cabcc04ef, to compare the abort.

Results

Check Result Note
Diff check passed
Conventions and PR template passed
Mergeability and dependencies passed stacked on #14374 (still open)
Export ABI audit (by hand) passed 195 symbols, 0 alias mismatches; 23 warnings are test-file callbacks
Test quality (by hand) passed 4 flags are false positives; the tests are registered in CMake
Blocker 1: guest FILE* in libpsl passed guest stream is unwrapped; null-stream and end-of-file tests added
Blocker 2: document past its context passed documents are owned until xmlFreeDoc; test added
Blocker 3: default libjpeg error_exit passed now throws; test added
Hosted Windows job failed guest_nk_req_jpeg terminates on a libjpeg error; same at 8cabcc0
Other hosted jobs passed conventions, eligibility, labels, library portability, relinker (ubuntu, windows, asan), report; macOS relinker queued
Build and runtime tests not run the Windows log is the only runtime evidence
Negative controls not run needs a build
Commands, logs and unedited precheck output

Commands:

  • Precheck: python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421 --output-dir <scratch>/precheck/14421d (report.md in that directory).
  • ABI audit, by hand, from the main checkout: python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head a32021388e2d09040667e76c72f3fa56d8d38672 --json. Result: passed_with_warnings; 195 symbols; 0 alias mismatches; 23 public-declaration-not-found warnings, all in test files.
  • Test quality, by hand, on an archive of the head, on tests/Jpeg.cpp, tests/Psl.cpp, tests/Xml.cpp and core/libs/tests/GuestWebKitZlib.cpp. Result: 4 possible-unregistered-cpp-test flags; the tests are registered in CMakeLists.txt.
  • Windows log: run 41333345534, job 124934749616 (head a320213). Lines: guest_nk_req_jpeg ... Exit code 0xc0000409, unhandled exception 'JpegException' thrown, terminate called after throwing an instance of '(anonymous namespace)::JpegException'. The same test, with the same messages, failed at 8cabcc0: run 41316484095, job 124885806921.

Precheck table (unedited):

Check Status Command / reason
build_and_compile not_applicable The precheck deliberately does not configure, build, or compile AnyPS5.
diff_check passed git diff --check ba627802e2210187d417deb64c8f2fed8e33011f...a32021388e2d09040667e76c72f3fa56d8d38672
conventions_and_pr_template passed /marc/Conlyse-VIEJO/.venv/bin/python3 tools/check_conventions.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head a32021388e2d09040667e76c72f3fa56d8d38672 --title feat(libSceNKWebKitRequirements): implement the libjpeg, libxml2 and libpsl exports --body /dev/shm/reviewer-run/precheck/14421d/pr-body.md
changed_export_abi_audit failed /marc/Conlyse-VIEJO/.venv/bin/python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head a32021388e2d09040667e76c72f3fa56d8d38672 --json
python_syntax not_applicable No changed Python implementation files.
related_python_unit_tests not_applicable No changed tools/ module has an existing sibling tools/tests/test_<module>.py suite.
python_test_registration not_applicable No changed test_*.py files.
live_dependency_and_mergeability_check passed /marc/Conlyse-VIEJO/.venv/bin/python3 /marc/AnyPS5/tools/pr_stack_check.py 14421 --repo boykopovar/AnyPS5 --output-dir /dev/shm/reviewer-run/precheck/14421d/stack
build_dependent_cpp_tests not_applicable Not run: C++ tests need exact-head binaries, which require a build; the precheck does not build.
test_quality_heuristics failed /marc/Conlyse-VIEJO/.venv/bin/python3 /dev/shm/reviewer-run/precheck/14421d/head-a32021388e2d-f482c71c/tools/pr_test_quality.py --root /dev/shm/reviewer-run/precheck/14421d/head-a32021388e2d-f482c71c --json .gitmodules 3rdparty/libpsl 3rdparty/libxml2 cmake/LibPsl.cmake cmake/LibXml2.cmake core/libs/prx/libSceNKWebKitRequirements/CMakeLists.txt core/libs/prx/libSceNKWebKitRequirements/Export.cpp core/libs/prx/libSceNKWebKitRequirements/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/Xml.cpp core/libs/prx/libSceNKWebKitRequirements/Zlib.cpp core/libs/prx/libSceNKWebKitRequirements/include/GuestJpeg.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestPsl.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestXml.hpp core/libs/prx/libSceNKWebKitRequirements/include/GuestZStream.hpp core/libs/prx/libSceNKWebKitRequirements/include/Identified.hpp core/libs/prx/libSceNKWebKitRequirements/tests/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Xml.cpp core/libs/tests/GuestWebKitZlib.cpp docs/dev/debt/libSceNKWebKitRequirements.md
negative_controls not_applicable No changed Python test module in tools/ can be run as a no-build base-source control.

Negative control

Not run. The new tests are C++ and need a build.

Findings

Blocking

  1. The Windows job aborts in guest_nk_req_jpeg. The log shows a JpegException leaving the test and calling terminate. The throw comes from the host error callback (Jpeg.cpp line 41, throw JpegException{cinfo}), which runs inside libjpeg's C code; the C++ catch in the wrappers is above those frames. On Windows, C frames built without unwind information cannot be unwound through, which is consistent with the abort. The same abort is in the job at 8cabcc0, so this predates this commit; the commit does not change this path. I did not check the libjpeg-turbo build flags for Windows. Direction, not verified: keep libjpeg's error path unwind-safe on Windows (build flags for the C sources), or leave the error through libjpeg's own setjmp path instead of throwing through it. Hosted checks must pass before approval.

Blockers 1 to 3 from the earlier review: fixed at this head

  • Psl: psl_load_fp_nid_postfix now takes the guest FileStream* (Psl.cpp line 76, Identified.hpp line 52), returns null for a null stream, and passes the native handle from GetNativeStream (FileStream.hpp line 401). SyncStatus (line 252) publishes end-of-file and error state, as other libc functions do when they read the native handle. Tests at tests/Psl.cpp lines 105-110 go through the guest fopen path and check feof, fclose and a null stream.
  • Xml: the guest document is now owned by g_guestDocs, keyed by the host document (Xml.cpp lines 43, 140-143, 196-198), and released in xmlFreeDoc (lines 681-696), not with the parser binding. tests/Xml.cpp lines 417-443 free the context and then use the document.
  • Jpeg: the default error_exit now throws std::runtime_error with the formatted message (Jpeg.cpp lines 153-159). tests/Jpeg.cpp lines 265-291 check the throw and JERR_NO_SOI. The debt file (line 12) records this.

Non-blocking

  • Jpeg, jpeg_CreateDecompress (lines 377-384): the catch calls the guest error_exit without copying msg_code from the host error, unlike the other wrappers (lines 323-328). The default error_exit formats from the guest msg_code, so the message on this path can be stale.
  • Jpeg, lines 342-345: s_std_inited is set without synchronisation.
  • Jpeg: an exception object leaks if a guest error_exit longjmps out of a catch block (reported; not re-run).
  • Xml, line 234: h->charset = h->charset; should read g->charset.
  • Xml, lines 510-512: InitHostSax sets initialized unconditionally. Host SAX slots are forwarded only when the guest slot is set (lines 513-525).
  • Xml, line 232: SyncGuestToHost writes h->spaceTab from the guest; a reentrant sync can restore a stale value (reported; not re-run).
  • Xml, lines 68-76: FindBinding releases the mutex before its raw pointer is used.
  • Xml: no assignment to guest_ctxt.sax exists in the file, so the guest SAX table is not set on the guest context (grep).
  • Xml tests: TestErrorHandling (tests/Xml.cpp line 171) never asserts the error callback; the xmlSwitchEncoding result at line 200 is discarded (line 201).
  • Psl test, lines 42-43: outdated == 0 || outdated == 1 proves nothing.
  • Jpeg tests, lines 210 and 220: the progressive loops have no bound (reported).
  • Psl, lines 20-22: the builtin list name comes from the build configuration; whether it embeds a build-host path was not confirmed.
  • Unchanged files, carried from the earlier review: Zlib.cpp licence and attribution headers and the adapted uncompress (maintainer questions); the leak at Zlib.cpp line 151 and the re-initialisation leak at line 148; the zero-initialised checks at GuestWebKitZlib.cpp lines 102-108 and 122-128; CMake requires Python 3 on every configure; libxml2 install rules and the disabled features are not all in the debt file.

Sources

Not verified

  • No build or test run. The Windows job log is the only runtime evidence; the linux job is skipped for this PR, so the Linux runs of these tests were not observed.
  • How a C++ exception from the guest-called default error_exit reaches the guest boundary was not traced. Guest code can also call that function through the pointer.
  • The libjpeg-turbo build flags on Windows were not checked.
  • The libxml2 ownership of myDoc on context free was not checked against the pinned source.
  • The 13 unchanged PR files were not re-read at this head (see What I ran).

To get this to a good state

  • Make the libjpeg error path safe on Windows (or leave it without a C++ throw through libjpeg frames), so guest_nk_req_jpeg passes in the Windows job.
  • Rerun the precheck and the Jpeg, Xml and Psl test executables on the new head.
  • Address the non-blocking items as the author sees fit.

@PrivacyOnion

Copy link
Copy Markdown
Author

@MarcZX10 thanks, fixed in 1946ca0.

Windows abort in guest_nk_req_jpeg. On MinGW the libraries unwind with DWARF tables (configure_windows_unwind renames .eh_frame after the build), but libjpeg-turbo is an ExternalProject built with its own flags and does not carry those tables, so the JpegException thrown from the host error_exit could not get through libjpeg's C frames and terminate was called. No C++ exception crosses libjpeg any more:

  • The host error_exit jumps back to the export with __builtin_longjmp, as libkernel's UnwindRecovery does. libjpeg's own documented recovery is this kind of non-local return.
  • Each call into libjpeg goes through RunGuarded, which sets up the jump buffer for that call and restores the previous one afterwards. A guest callback that calls another export on the same object (for example resync_to_restart = jpeg_resync_to_restart) therefore keeps its own target, and the outer call still lands in a live frame.
  • All libjpeg calls are guarded, including jpeg_CreateDecompress, jpeg_destroy_decompress, jpeg_save_markers, and jpeg_has_multiple_scans and jpeg_input_complete, which can raise JERR_BAD_STATE.
  • After the jump the export does what it did before: it copies the state to the guest and calls the guest error_exit. The default one still throws std::runtime_error, but from the adapter's own C++ frame, outside libjpeg.

Non-blocking item, also fixed: jpeg_CreateDecompress now copies msg_code and msg_parm to the guest before calling its error_exit, like the other wrappers.

The debt entry now describes the jump instead of exceptions.

Host harness (WinLibs GCC): the jpeg, xml and psl tests pass, including TestGuestErrorExit and TestDefaultErrorExitThrows. Jpeg.cpp compiles with the real APS5_VABI and -Wall -Wextra with no new warning. git diff --check is clean, and conventions against the merge base report 0 errors (the one warning is the zlib entry from #14374). The Windows job on this head will confirm the abort is gone.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — changes requested

Four blocking defects at head 1946ca063c: the Xml use-after-free, two Xml pointer forwards, and the JPEG stale-position bug are verified by reading the exact lines, and Zlib.cpp carries block comments that the repository's rule forbids.

What I ran

  • Exact head 1946ca063c119301a8a1212063c0e81bd6ec7b2f, PR base ba627802e2210187d417deb64c8f2fed8e33011f.
  • Read-only: git -C /marc/AnyPS5 show 1946ca063c119301a8a1212063c0e81bd6ec7b2f:<path> for the changed files, split across read-only file-range reads. I re-read the lines cited below myself.
  • Precheck: python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421 --output-dir <scratch>.
  • Not built, not run.

Results

Check Result Note
Diff check passed
Conventions passed comment findings dropped because the PR touches a debt file
ABI audit failed helper absent from the head tree
Test quality failed helper absent from the head tree
Build and ctest not run needs a build
Windows hosted job not verified see Not verified
Precheck report (unedited header and failing rows)
# PR precheck report — checks failed
changed_export_abi_audit | failed | pr_abi_audit.py (helper absent from the head tree)
test_quality_heuristics | failed | pr_test_quality.py (helper absent from the head tree)

Findings

Blocking

  1. core/libs/prx/libSceNKWebKitRequirements/Xml.cpp 698-699: xmlFreeDoc releases the host document, but nothing clears the host parser context's myDoc. The next sync (195-198) reads the freed document. Fix: clear myDoc on every binding that points at the freed document, under the same lock.
  2. Xml.cpp 812: xmlSAX2StartDocument(b ? b->host_ctxt : ctx) passes the guest pointer to libxml2 when no binding is found. The same pattern appears at 657, 797, 802 and 807 (reported). Fix: return without calling libxml2 when b is null.
  3. core/libs/prx/libSceNKWebKitRequirements/Jpeg.cpp 104-137: the guest source callbacks see the guest's input position from before the libjpeg call. libjpeg advances the host copy, and the write-back at 137 overwrites it with the stale value. Syncs happen only at entry and exit. Fix: copy the host state into the guest source before each guest callback. Add a test with an unsaved APPn marker that checks pixels.
  4. core/libs/prx/libSceNKWebKitRequirements/Zlib.cpp 1-4 (the comment block runs to line 35): block comments with the zlib attribution and copyright. CONVENTIONS.md line 13 allows comments only for debt. Fix: remove them and put the attribution in the PR description, or ask maintainers for an exception.

Non-blocking

  • The Windows abort in guest_nk_req_jpeg appears fixed: Jpeg.cpp 36-54 replaces the C++ throw with longjmp, and the only throw left runs after libjpeg has returned. Not confirmed by a Windows run.
  • tests/Psl.cpp 28 expects psl_check_version_number_nid_postfix(0x001000) == 0. A minimum-version check should not return 0 for a library that is newer. Reported, not run. It needs a run before merge.
  • Jpeg.cpp 380 does not check structsize (reported).
  • The debt file overstates what the wrappers guarantee (reported, not confirmed line by line).

Sources

  • Xml frees:
    if (h->myDoc) {
    auto* doc = GuestDocFor(h->myDoc);
    SyncHostDocToGuest(h->myDoc, doc);
    g->myDoc = doc;
    and #L698-L699
  • Xml forward:
    xmlSAX2StartDocument(b ? b->host_ctxt : ctx);
  • JPEG callbacks:
    static void HostInitSource(j_decompress_ptr cinfo) {
    auto* b = static_cast<JpegBinding*>(cinfo->client_data);
    if (!b || !b->guest_cinfo || !b->guest_cinfo->src) return;
    auto* gsrc = b->guest_cinfo->src;
    if (gsrc->init_source) {
    gsrc->init_source(b->guest_cinfo);
    }
    b->host_src.next_input_byte = gsrc->next_input_byte;
    b->host_src.bytes_in_buffer = static_cast<size_t>(gsrc->bytes_in_buffer);
    }
    static boolean HostFillInputBuffer(j_decompress_ptr cinfo) {
    auto* b = static_cast<JpegBinding*>(cinfo->client_data);
    if (!b || !b->guest_cinfo || !b->guest_cinfo->src) return FALSE;
    auto* gsrc = b->guest_cinfo->src;
    int32_t ret = 0;
    if (gsrc->fill_input_buffer) {
    ret = gsrc->fill_input_buffer(b->guest_cinfo);
    }
    b->host_src.next_input_byte = gsrc->next_input_byte;
    b->host_src.bytes_in_buffer = static_cast<size_t>(gsrc->bytes_in_buffer);
    return ret ? TRUE : FALSE;
    }
    static void HostSkipInputData(j_decompress_ptr cinfo, long num_bytes) {
    auto* b = static_cast<JpegBinding*>(cinfo->client_data);
    if (!b || !b->guest_cinfo || !b->guest_cinfo->src) return;
    auto* gsrc = b->guest_cinfo->src;
    if (gsrc->skip_input_data) {
    gsrc->skip_input_data(b->guest_cinfo, static_cast<int64_t>(num_bytes));
    }
    b->host_src.next_input_byte = gsrc->next_input_byte;
    b->host_src.bytes_in_buffer = static_cast<size_t>(gsrc->bytes_in_buffer);
    }
  • Zlib comments:
    /* uncompress_nid_postfix is adapted from uncompress2() in uncompr.c of zlib 1.3.1
    * (https://github.com/madler/zlib/blob/51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf/uncompr.c).
    * Altered for AnyPS5: it takes 64-bit guest lengths, counts the produced bytes itself
    * instead of reading total_out, and does not report the consumed source length.

Not verified

  • Build, ctest and every new test: not run.
  • The Windows hosted job at this head: not confirmed.
  • libxml2 and libpsl sources are submodules that are not in this checkout, so their behaviour is unverified.
  • libjpeg behaviour for the stale-position case: reasoned from the code, not run.

To get this to a good state

Fix blocking 1-4, rebuild, and run guest_nk_req_xml, guest_nk_req_jpeg and guest_nk_req_psl on the new head. Confirm the Windows job passes.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 1946ca0 merged onto main f2d874c · tested 2026-11-01 21:43 UTC · 16 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,099 passed · 15 failed (0 new, 9 also fail on main, 6 unstable on main) · 58 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1246 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s, ⚠️ agc_driver_graphics_maps_fallback 88.9 s, ⚠️ agc_driver_graphics 85.7 s, ⚠️ agc_driver_bda_device 51.6 s, ⚠️ unwrap_self 47.4 s
Unstable on main (6, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_array_layer_target_cpu: failed in 1 of the last 5 main runs
agc_driver_job_manager_compute_capacity: failed in 4 of the last 5 main runs
agc_driver_pipeline_journal: failed in 3 of the last 5 main runs
agc_driver_submit_progress: failed in 3 of the last 5 main runs
agc_submit_progress: failed in 4 of the last 5 main runs
agc_submit_progress_no_timeline: failed in 4 of the last 5 main runs
Failing on main too (9, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_idle_publish_wait
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (5 IDs in 717 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (717 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 709 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (58)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_pipeline_binary_vulkan: skipped, the device lacks VK_KHR_pipeline_binary
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipp
Reproduce
git fetch origin 1946ca063c119301a8a1212063c0e81bd6ec7b2f f2d874c1fdccf781c0fa5d86619bb5dd8894b958
git checkout f2d874c1fdccf781c0fa5d86619bb5dd8894b958 && git merge --no-ff 1946ca063c119301a8a1212063c0e81bd6ec7b2f
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@MarcZX10

MarcZX10 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Correction — the Psl version-check item is wrong

The item on tests/Psl.cpp line 28 in my review at 1946ca063c was wrong. libpsl 0.21.5 psl_check_version_number returns the library version only when the requested version is at least the library's, and returns 0 otherwise (src/psl.c lines 1562-1582 at tag 0.21.5). The test expects 0 for 0x001000, which is older, so the assertion is correct. The other findings in that review are unchanged.

@PrivacyOnion

Copy link
Copy Markdown
Author

@MarcZX10 thanks. Blocking items 1 to 3 are fixed in d2316b7, and the structsize point in bb9960f. Item 4 is a license question, see below.

1. myDoc after xmlFreeDoc. Under g_xmlMutex, xmlFreeDoc now walks every binding and clears myDoc on the host and guest contexts that still point at the freed document, so the next sync cannot read it. libxml2 itself leaves myDoc dangling; the debt entry now records the difference. Regression TestFreedDocumentLeavesContext:

  • a push parse is left open after its first chunk;
  • its document is freed while the context is alive;
  • the test checks that ctxt->myDoc is null, that xmlCtxtUseOptions still works, and that the following sync leaves myDoc null.

Against the previous Xml.cpp it fails with freeing the document should clear the context's document.

2. Guest pointers forwarded to libxml2. xmlCtxtGetLastError, xmlSAX2StartDocument, xmlSAX2EndDocument, xmlSAX2InternalSubset and xmlSAX2EntityDecl now resolve the context through RequireBinding:

  • A null context returns as libxml2 does: NULL from xmlCtxtGetLastError, nothing from the SAX2 helpers.
  • Any other pointer that is not a context this library created throws std::invalid_argument, like the libjpeg exports. Nothing reaches libxml2 in that case.
  • The thread_local fallback error that served unknown contexts is gone.

TestUnknownContexts covers both cases for all five exports.

3. Stale input position in guest source callbacks. SyncSourceToGuest copies the host next_input_byte and bytes_in_buffer into the guest jpeg_source_mgr before each guest init_source, fill_input_buffer, skip_input_data, resync_to_restart and term_source. The existing write-back after each callback is unchanged.

The new test decodes a baseline JPEG whose APP1 marker is not saved, so libjpeg skips it through the guest skip_input_data. The test then checks every pixel against host libjpeg, and TestDecode now also requires num_warnings == 0.

A plain unsaved marker was not enough: with the stale position, libjpeg resynchronised on the next real marker and only warned about extraneous bytes. So the skipped payload carries an FF D9 12 bytes before its end, which a stale skip lands on. Against the previous Jpeg.cpp the test fails with JPEG datastream contains no image; with the fix it passes.

4. The comment block in Zlib.cpp. I have kept it. uncompress_nid_postfix is an altered copy of uncompress2() from zlib's uncompr.c. The zlib license requires altered sources to be "plainly marked as such" (clause 2) and says "this notice may not be removed or altered from any source distribution" (clause 3). Moving the notice into the PR description would break clause 3 for the copy in the repository.

main already keeps upstream notices on adapted code in the same way, for example core/libs/prx/libc/src/Math.cpp (Sun Microsystems and FreeBSD notices) and core/libs/prx/libc/src/FileAndHeap.cpp (Otto Moerbeek's ISC notice). The block also comes from #14374 (commit 41d0039, "attribute adapted uncompress"), which this PR stacks on.

@boykopovar, could you confirm that a third-party license notice on adapted code is accepted as an exception to the no-comments rule, as on main? If not, I can replace uncompress_nid_postfix with an implementation written against the documented uncompress2 semantics only, without the block.

Non-blocking.

  • structsize, fixed in bb9960f. The adapter passed the host size to libjpeg and ignored the guest's. A structsize other than sizeof(GuestJpegDecompress) now raises JERR_BAD_STRUCT_SIZE through the guest error_exit, with the expected and passed sizes in msg_parm.i[0] and [1] as libjpeg does. This happens after libjpeg's own version check, so the error order is unchanged, and the object is left unusable. TestBadStructSize covers it and fails against the previous Jpeg.cpp.
  • The Psl version item: thanks for the correction.
  • The debt file: the libjpeg and libxml2 entries are rewritten to describe what the wrappers do now and stay under the 1000-character limit.

Run. Host harness (WinLibs MinGW, x86-64), with negative controls against the previous Xml.cpp and Jpeg.cpp:

Test This head Previous code
xml pass fails as above
jpeg pass fails as above, and on the wrong structsize
psl pass —
  • The four changed sources also pass -fsyntax-only -Wall -Wextra with the real APS5_VABI.
  • git diff --check ba627802e2 HEAD is empty.
  • tools/check_conventions.py reports 0 errors. Its one warning is the zlib entry from feat(libSceNKWebKitRequirements): implement the zlib exports #14374.
  • The guest build and CTest on Linux and Windows are left to CI.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — changes requested

At head bb9960ffa0d11520159f16adb43c283c67e4f8e1, the new CMake dependency breaks the documented Python-optional configure path, and guest updates to the XML parser context’s charset are discarded.

What I ran

Reviewed all 21 changed file entries at head bb9960ffa0d11520159f16adb43c283c67e4f8e1 against PR base ba627802e2210187d417deb64c8f2fed8e33011f. The dependency-aware merge preview against current main 88e804b0a7df1f1ac373a9e2f72fb378d925fdd4 was clean; #14374 is included in this PR’s stack. Environment: Debian Linux, Python 3.11.2, GCC 12.2.0; no build or compilation was run.

python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421 --output-dir /tmp/review-14421-precheck
python3 tools/pr_abi_audit.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head bb9960ffa0d11520159f16adb43c283c67e4f8e1 --json
python3 tools/pr_test_quality.py --root /tmp/review-14421-precheck/head-bb9960ffa0d1-9847ce5d --json core/libs/prx/libSceNKWebKitRequirements/tests/Jpeg.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Psl.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Xml.cpp core/libs/tests/GuestWebKitZlib.cpp
git diff --check ba627802e2210187d417deb64c8f2fed8e33011f..bb9960ffa0d11520159f16adb43c283c67e4f8e1
python3 tools/pr_stack_check.py 14421 --repo boykopovar/AnyPS5 --output-dir /tmp/review-14421-stack-final
gh pr checks 14421 --repo boykopovar/AnyPS5

Results

Check Result Note
Diff and conventions passed One debt-length warning
ABI audit passed 23 declaration warnings need manual interpretation
Test registration passed Heuristic warnings are registered through a CMake loop
Merge preview passed Clean against current main; #14374 included
Windows and relinker CI passed Current-head jobs passed
Linux CI not run Hosted job was skipped
Build and CTest not run No build in this initial review
Precheck results (unedited)
Check Status Command / reason
build_and_compile not_applicable The precheck deliberately does not configure, build, or compile AnyPS5.
diff_check passed git diff --check ba627802e2210187d417deb64c8f2fed8e33011f...bb9960ffa0d11520159f16adb43c283c67e4f8e1
conventions_and_pr_template passed tools/check_conventions.py --base ba627802e2210187d417deb64c8f2fed8e33011f --head bb9960ffa0d11520159f16adb43c283c67e4f8e1
changed_export_abi_audit failed The isolated PR snapshot does not contain tools/pr_abi_audit.py; the workspace copy was run separately.
python_syntax not_applicable No changed Python implementation files.
related_python_unit_tests not_applicable No changed tools module has a sibling suite.
python_test_registration not_applicable No changed Python tests.
live_dependency_and_mergeability_check passed tools/pr_stack_check.py 14421 --repo boykopovar/AnyPS5
build_dependent_cpp_tests not_applicable Exact-head binaries require a build; the precheck does not build.
test_quality_heuristics failed The isolated PR snapshot does not contain tools/pr_test_quality.py; the workspace copy was run separately.
negative_controls not_applicable No changed Python test module supports a no-build control.

Negative control

Not run locally. The author reports regressions against prior code for the XML lifetime and JPEG callback fixes; I did not reproduce them. The current Windows CI is hosted evidence, not a local run.

Findings

Blocking

  1. Python is no longer optional at configure time. core/libs/CMakeLists.txt adds every PRX directory, which includes this library. Its CMakeLists.txt unconditionally includes cmake/LibPsl.cmake, where find_package(Python3 COMPONENTS Interpreter REQUIRED) runs even though the library target is EXCLUDE_FROM_ALL. This makes a default CMake configure fail on systems without Python, contrary to the repository’s documented optional-Python path. Scope the Python requirement to building the PSL target or avoid requiring Python during configure.
  2. The XML charset field is not synchronized from guest to host. SyncGuestToHost assigns h->charset = h->charset at Xml.cpp:241, so a guest update to the exposed parser-context field is discarded before the next libxml2 call. Copy the guest field, or explicitly document and enforce that this exposed field is read-only.
  3. The Zlib source notice still conflicts with the repository comment rule. Zlib.cpp:1-35 adds source comments, while AGENTS.md and docs/dev/CONVENTIONS.md permit code comments only for technical debt and the specified closing markers. The PR author identifies the notice as required for the adapted zlib source; I am not asking that it be removed. Resolve this through a maintainer-approved exception or other license-compliant repository policy before merge.

Non-blocking

  • The manual ABI audit reported 23 public-declaration-not-found warnings. These are lexical findings for internal callback/helper definitions; the audit does not establish an ABI defect.
  • The test-quality heuristic flagged four C++ tests as possibly unregistered. CMakeLists.txt registers them through foreach(topic IN ITEMS Jpeg Xml Psl), so these are heuristic false positives.
  • SyncGuestToHost also leaves dict and sax2 untouched; I did not establish whether guest writes to those opaque pointers are supported by this wrapper.

Sources

Not verified

  • Linux build and tests were skipped by hosted CI; no local build, CTest, GPU, or console run was performed.
  • The audit-only public-header command did not finish before I stopped it; it has no result in this review.
  • The libjpeg-turbo, libxml2, and libpsl submodule contents were not checked out locally. The reviewed PR pins their gitlink revisions.
  • Whether guest software mutates charset, dict, or sax2 in practice was not confirmed from a runtime caller.

To get this to a good state

Make the PSL generator optional at configure time, correct or formally restrict the XML charset sync, and resolve the Zlib notice policy with maintainers while preserving required license terms. Then run the applicable build and tests on the new head; I will rerun the same no-build checks and re-review the fixes.

@PrivacyOnion

Copy link
Copy Markdown
Author

@MarcZX10 thanks. 203d118 fixes items 1 and 2. Item 3 still needs a maintainer decision.

1. Python at configure time. cmake/LibPsl.cmake now looks for Python with find_package(Python3 COMPONENTS Interpreter QUIET), so a configure without Python goes through. Python is only needed to build the libpsl suffix table. If there's no interpreter, that build step prints libpsl: generating the built-in suffix table needs a Python 3 interpreter (set Python3_EXECUTABLE) and fails. Since the library is EXCLUDE_FROM_ALL, a default build never reaches that step. I checked both branches in a small CMake project with the same command, once with -DCMAKE_DISABLE_FIND_PACKAGE_Python3=ON (configure passes, the generation step fails with that message) and once with Python (the header is generated).

2. charset from guest to host. SyncGuestToHost now copies g->charset. I added TestGuestCharsetReachesHost: it sets a different charset on the guest context, calls xmlCtxtUseOptions, which syncs both ways, and checks the value is still there. It passes on the host harness (g++ 15 against libxml2 from the submodule) and fails against the previous Xml.cpp with a guest charset should reach the host context and survive the next sync.

3. zlib notice. Still waiting on a maintainer exception, which I asked for earlier in this thread. I'll do whatever the maintainers decide.

I also shortened the zlib debt entry below the 1000-character limit, so check_conventions.py no longer prints the debt-length warning for this library.

justdoston and others added 13 commits November 2, 2026 17:47
Implement deflateInit2_, deflate, deflateReset, deflateEnd, deflateBound,
inflateInit2_, inflate, inflateReset, inflateEnd and uncompress on the
vendored zlib, translating the guest LP64 z_stream and calling the guest
allocators through the guest ABI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p gzip throw tests

Keep the zlib 1.3.1 uncompr.c and zlib.h notices at the top of Zlib.cpp
and mark how uncompress_nid_postfix alters uncompress2. Remove the test
assertions that pinned the unimplemented gzip paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…idth

On a host with a 32-bit uLong, a checksum zlib resets to the truncated
value of the guest's 64-bit field was not written back, which left stale
upper bits. Compare in guest width and cover initialization over a
stale checksum.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…libpsl exports

The 60 exports are adapters over libjpeg-turbo, libxml2 2.9.14 and libpsl 0.21.5, built from source (libxml2 and libpsl as new submodules). Guest-visible structures use LP64 mirrors, guest callbacks go through host trampolines, and invalid libjpeg objects throw instead of returning.
… guest callbacks

Every successful xmlRegisterInputCallbacks or xmlRegisterOutputCallbacks now binds its own host trampolines to its own guest callbacks, so a later or failed registration no longer redirects the entries or the open contexts of earlier ones. A null guest callback is registered as null, as libxml2 stores it. A push parser created without a SAX handler now passes the caller's user data to libxml2 instead of the adapter's binding.
…ten the jpeg and psl tests

The debt file goes back to LF line endings and says that I/O callback slots are kept for the process lifetime. LibXml2.cmake no longer forces BUILD_SHARED_LIBS into the cache. The jpeg test drives a guest error_exit through JERR_NO_SOI, and the psl test checks exact builtin metadata.
…callbacks as internal

The five default guest error-manager callbacks take the _nid_no_patch suffix, so tools/progress.py no longer counts them as exports.
…p documents past their parser context and make the default libjpeg error_exit throw

psl_load_fp takes the guest FileStream and passes its native stream to libpsl, then publishes the stream status. The guest document of a parser context is owned by the document and released by xmlFreeDoc, not by the context binding, so it stays valid after xmlFreeParserCtxt. The error_exit installed by jpeg_std_error throws std::runtime_error with the formatted message where libjpeg would exit, so a fatal error no longer becomes a normal return. Each fix has a regression: psl_load_fp through the libc fopen path, a document used after its context and a second context are freed, and the default error_exit on a stream without SOI.
…instead of a C++ exception

On Windows the libraries unwind with DWARF tables that the libjpeg-turbo build does not emit, so a JpegException thrown from the host error_exit could not cross libjpeg's C frames and terminated guest_nk_req_jpeg. The host error_exit now jumps back to the export with __builtin_longjmp to a buffer each call sets up and restores, so nested exports keep their own target. jpeg_CreateDecompress also copies the error code to the guest before calling its error_exit, and jpeg_has_multiple_scans and jpeg_input_complete report libjpeg errors the same way.
… reject unknown contexts and keep the guest input position current

xmlFreeDoc left the host parser context's myDoc pointing at the freed
document, and the next sync of that context read it. It now clears myDoc in
every context that still points at the document.

xmlCtxtGetLastError and the xmlSAX2 exports passed a guest pointer to libxml2
when it was not a context this library created. A null context now returns as
libxml2 does, and an unknown one throws std::invalid_argument.

The guest source callbacks saw the input position from before the libjpeg
call, so a guest skip_input_data advanced from a stale position. The host
position is now copied into the guest source manager before each callback.
…ng jpeg_CreateDecompress structsize

jpeg_CreateDecompress passed the host structure size to libjpeg and ignored the guest's, so a caller built against a different jpeg_decompress_struct was accepted. A structsize other than the guest mirror's size now raises JERR_BAD_STRUCT_SIZE through the guest error_exit with the expected and passed sizes, as libjpeg does, and leaves the object unusable.
…arser and need Python only to generate the suffix table

The guest-to-host context sync assigned the host charset to itself, so a charset the guest set was lost on the next call. Configuring no longer requires a Python interpreter; building the libpsl suffix table without one fails with a message naming Python3_EXECUTABLE.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — changes requested

The entity-returning XML exports reuse one mutable mirror and clear the owning-document link, so valid entity lookups can return incorrect guest data.

What I ran

Reviewed all 21 changed files at head 31dabab2080fe1d6e94db59aaa477f1cf4479b5c against PR base b28bb772177768f99874ca38f6252cd0139acf2a. The merge preview against current main (7feb37ee3e30e10139b483fb4df689d5b5b69ab8) was clean. Environment: Debian Linux 6.1, Python 3.11.2, GCC 12.2.0. No local build or compilation was run.

python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421 --output-dir /tmp/anyps5-pr-precheck/14421-20261103
git diff --check b28bb772177768f99874ca38f6252cd0139acf2a 31dabab2080fe1d6e94db59aaa477f1cf4479b5c
gh pr checks 14421 --repo boykopovar/AnyPS5

Results

Check Result Note
Diff and conventions passed No whitespace or convention errors
Main merge preview passed Clean against current main
Export audit passed Warnings are test callback declarations, not missing exports
Test registration passed CMake registers the added tests
Hosted checks passed Windows and relinker jobs passed; several specialized jobs skipped
Local build and CTest not run No local build
Negative control not run Requires exact-head C++ test binaries
Raw check output

Precheck output:

Head: `31dabab2080fe1d6e94db59aaa477f1cf4479b5c`
Base: `b28bb772177768f99874ca38f6252cd0139acf2a`
Head unchanged at completion: **True**
Base unchanged at completion: **True**
| diff_check | passed | `git diff --check b28bb772177768f99874ca38f6252cd0139acf2a...31dabab2080fe1d6e94db59aaa477f1cf4479b5c` |
| conventions_and_pr_template | passed | `All conventions checks passed.` |
| live_dependency_and_mergeability_check | passed | `PR #14421: clean against current base` |

The ABI helper returned passed_with_warnings; all 23 warnings were for callback definitions used by the added tests. The test-quality heuristic warned that four C++ test sources may be unregistered; direct inspection of the CMake registration confirmed they are included.

Hosted checks:

conventions	pass	28s	https://github.com/boykopovar/AnyPS5/actions/runs/41486557045/job/125418359440
eligibility	pass	5s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125418371035
labels	pass	13s	https://github.com/boykopovar/AnyPS5/actions/runs/41486557134/job/125418358229
library-portability	pass	1m53s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561315/job/125418368677
relinker (macos-latest, clang++)	pass	6m5s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424852988
relinker (macos-latest, clang++, x86_64)	pass	6m47s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424853334
relinker (ubuntu-latest, g++)	pass	4m3s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424852986
relinker (windows-latest, g++)	pass	8m6s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424853191
edge-watch	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424855834
linux	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424854870
register-context	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424855792
write-watch	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424855623
relinker-asan	pass	2m23s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424852850
report	pass	1m27s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561258/job/125418368226
windows	pass	12m23s	https://github.com/boykopovar/AnyPS5/actions/runs/41486561494/job/125424852634

Negative control

Assertion Change to code under test Did the new test fail?
Returned entity mirrors retain independent contents and the correct owner document No source mutation was made Not run locally; no test currently covers these assertions

Findings

Blocking

  • Entity results alias one temporary object. SyncHostEntityToGuest rewrites the same thread_local GuestEntity for every call. A caller that saves the result of xmlGetPredefinedEntity("amp") and then looks up "lt" receives a pointer whose fields now describe "lt"; a document entity result is overwritten the same way. Keep a stable guest mirror for each host entity for the relevant entity lifetime and add a regression that retains two lookup results and checks both after the second lookup.
  • Document entities lose their owner. The mirror unconditionally sets GuestEntity::doc to null. libxml2 associates document entities with their owning document, and consumers reading this field through the guest ABI will see a different relationship. Map the host xmlDocPtr to its GuestDoc and preserve that pointer in the stable mirror; test a document entity's doc field.

Non-blocking

  • The author reports that a maintainer decision is still pending on retaining zlib's required source-license notice alongside the repository's no-code-comments rule. Keep the required license notice intact while that policy question is resolved.

Sources

Not verified

  • No local build, CTest, Windows/macOS build, GPU, or console run was performed. Hosted CI results above are separate and tied to this exact head.
  • The zlib license-notice policy decision remains pending in the PR discussion.

To get this to a good state

Use stable guest entity mirrors, preserve each document entity's owning GuestDoc*, and add the two regression cases above. On the new head I will rerun the precheck, diff check, and hosted-check inspection listed under What I ran, then run the focused XML CTest if an exact-head build is available.

@PrivacyOnion

Copy link
Copy Markdown
Author

@MarcZX10 thanks. b75abdf fixes both blocking items.

1. Entity results aliasing one temporary. SyncHostEntityToGuest no longer reuses a single thread_local entity. Each host xmlEntity now has its own GuestEntity, created on the first lookup and refreshed on later ones. The mirror lives for as long as the host entity:

  • predefined entities are static in libxml2, so their mirrors last for the process;
  • document entities are mirrored per document, and xmlFreeDoc drops them together with the document.

The same entity looked up twice returns the same pointer, and different entities never share one.

2. Document owner. GuestEntity::doc now points to the GuestDoc mirror of the entity's host document. If that document has no mirror yet, it is mirrored on the spot, the same way myDoc is. Predefined entities keep doc == NULL, as in libxml2.

Test. TestEntityResultsStayDistinct:

  • keeps xmlGetPredefinedEntity("amp"), looks up "lt", and checks that both still hold their own name and content, and that a second "amp" lookup returns the same pointer;
  • parses <!DOCTYPE r [<!ENTITY first "one"><!ENTITY second "two">]><r/> and keeps first while looking up second. It checks both contents, and checks that first->doc == second->doc == ctxt->myDoc.

Two negative controls:

  • against the previous Xml.cpp, the test fails with two predefined entities should have their own guest entities;
  • with only doc set back to NULL, it fails with a document entity should point to its owning document.

How I tested: GCC 15 on Windows, Release with tests on. ctest -R "guest_(nk_req|webkit_zlib)" passes 4/4 at b75abdf, and check_conventions.py passes. The libxml2 debt entry now describes the entity mirror lifetime.

On the zlib notice: it stays in place, unchanged, until the maintainers decide on the policy.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked — new head, no blocking findings

The new per-entity mirrors and regression tests address the prior aliasing and missing-document-owner findings; hosted Windows and several platform jobs are still running.

What I ran

  • Re-checked head b75abdf6b120847836beea01fbf91f6fd52cf0c3 against the PR-recorded base b28bb772177768f99874ca38f6252cd0139acf2a; compared the three changed files since my previous review at 31dabab2080fe1d6e94db59aaa477f1cf4479b5c. The precheck's current-main merge preview was clean against d1ae628bc2f62459cce1e9872ba5632c938c71f2.
  • Environment: Linux x86-64, Python 3.11.2, GCC 12.2.0. No build or compiler invocation was run.
  • Commands and inspections:
python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14421
git diff --check b28bb772177768f99874ca38f6252cd0139acf2a...b75abdf6b120847836beea01fbf91f6fd52cf0c3
git diff 31dabab2080fe1d6e94db59aaa477f1cf4479b5c..b75abdf6b120847836beea01fbf91f6fd52cf0c3 -- core/libs/prx/libSceNKWebKitRequirements/Xml.cpp core/libs/prx/libSceNKWebKitRequirements/tests/Xml.cpp docs/dev/debt/libSceNKWebKitRequirements.md
git show b75abdf6b120847836beea01fbf91f6fd52cf0c3:core/libs/prx/libSceNKWebKitRequirements/CMakeLists.txt
gh api repos/boykopovar/AnyPS5/commits/b75abdf6b120847836beea01fbf91f6fd52cf0c3/check-runs

Results

Check Result Note
Diff and conventions passed No whitespace or convention errors.
Merge preview passed Clean against current main.
Entity regression registration passed The XML test remains registered through the CMake topic loop.
ABI audit not applicable No export declarations changed in this update.
Hosted checks not run Seven passed; four pending; three skipped.
Build and runtime tests not run No local build was performed.
Commands and unedited outputs

Precheck command output:

/tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14421-b75abdf6b120/report.md
/tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14421-b75abdf6b120/report.json
Conclusion: checks failed

The precheck report says diff_check and conventions_and_pr_template passed, and the live merge preview passed. Its ABI and test-quality helper invocations failed because those scripts are absent from the isolated PR snapshot. The update does not alter export declarations; the full-diff lexical ABI audit emitted 23 warnings and is not treated as a correctness certificate.

The raw git diff --check output was empty (exit status 0).

Hosted check output at the final refresh:

[{"conclusion":"skipped","name":"write-watch","status":"completed"},{"conclusion":"skipped","name":"register-context","status":"completed"},{"conclusion":"skipped","name":"linux","status":"completed"},{"conclusion":"skipped","name":"edge-watch","status":"completed"},{"conclusion":null,"name":"relinker (macos-latest, clang++, x86_64)","status":"in_progress"},{"conclusion":"success","name":"relinker (macos-latest, clang++)","status":"completed"},{"conclusion":"success","name":"relinker (ubuntu-latest, g++)","status":"completed"},{"conclusion":null,"name":"relinker (windows-latest, g++)","status":"in_progress"},{"conclusion":null,"name":"windows","status":"in_progress"},{"conclusion":"success","name":"relinker-asan","status":"completed"},{"conclusion":"success","name":"eligibility","status":"completed"},{"conclusion":"success","name":"library-portability","status":"completed"},{"conclusion":"success","name":"report","status":"completed"},{"conclusion":"success","name":"conventions","status":"completed"},{"conclusion":null,"name":"labels","status":"queued"}]

Negative control

Not run: the added C++ regression needs an exact-head test binary, and no safe no-build mutation path was available.

Findings

Blocking

  • None in the three-file update. Each returned entity now has a stable mirror keyed by its host entity; document-owned mirrors are discarded with their document, and the new test checks distinct entities, repeat lookup identity, preserved fields, and GuestEntity::doc.

Non-blocking

  • The PR depends on #14374, which remains open at head 3979b705f3b87d75827d036a587be8c37bc6f148; that current dependency head is not included in this branch. The stack preview is clean against current main, so this is not a conflict finding. Defer dependency integration until #14374 lands.

Sources

Not verified

  • No local build, CTest, Linux runtime, Windows runtime, sanitizer, GPU, or console run was performed. The author-reported host-harness test for the entity mirrors is not independently reproduced.
  • At the final refresh, Windows, Windows relinker, macOS x86-64 relinker and labels were pending; the Linux/macOS relinker and ASan jobs passed, and three specialized jobs were skipped.
  • Dependency #14374 is still open; the current dependency head is not included in this stacked branch.

To get this to a good state

No blocking code change is needed in the reviewed update. Let the pending hosted checks finish and keep the #14374 stack relationship until that dependency lands. If this head changes, I will rerun the same no-build checks and review the new delta.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head b75abdf merged onto main 7feb37e · tested 2026-11-03 07:15 UTC · 17 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,674 passed · 14 failed (0 new, 13 also fail on main, 1 unstable on main) · 59 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1323 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.8 s, ⚠️ agc_driver_graphics 105.0 s, ⚠️ agc_driver_graphics_maps_fallback 101.4 s, ⚠️ public_headers 87.1 s, ⚠️ agc_driver_bda_device 60.5 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 5 of the last 7 main runs
Failing on main too (13, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_job_manager_compute_capacity
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_driver_submit_progress
agc_idle_publish_wait
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (4 IDs in 758 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (758 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 750 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (59)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWi
Reproduce
git fetch origin b75abdf6b120847836beea01fbf91f6fd52cf0c3 7feb37ee3e30e10139b483fb4df689d5b5b69ab8
git checkout 7feb37ee3e30e10139b483fb4df689d5b5b69ab8 && git merge --no-ff b75abdf6b120847836beea01fbf91f6fd52cf0c3
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants