Skip to content

feat(libc): record traced import calls on Windows - #4546

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
codingreapermaps:feat/libc-call-trace-runtime
Oct 13, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
codingreapermaps:feat/libc-call-trace-runtime

Conversation

@codingreapermaps

@codingreapermaps codingreapermaps commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds the runtime half of an opt-in call trace on Windows. It covers part of #3815: the entry of a call, not its arguments or its result.

TraceImport_nid_no_patch(target, name, library) returns target unchanged unless library is named in APS5_TRACE_CALLS (a comma-separated list, or all). For a selected library it returns a thunk that records the import, the thread and the caller's address in a ring of the last 65536 calls, counts the call, and jumps to the function. The thunk saves the integer argument registers and xmm0 to xmm7, so stack arguments, varargs and struct returns pass through. One target keeps one thunk, so function pointers compare equal.

The crash and abort reports end with the last 64 calls and the 32 most called imports:

last traced system library calls (APS5_TRACE_CALLS):
  #1 thread 15168 libkernel.prx k+AXqu2-eBc from 0x0000000140010006 output.exe+0x10006
most called traced imports (1 calls in total):
               1 libkernel.prx k+AXqu2-eBc

Nothing calls TraceImport_nid_no_patch in this PR, so no title is affected. The relinker option that binds imports through it is a separate PR that depends on this one. Limits (Windows only, entry only, one process-wide ring, wider vector registers untested) are in docs/dev/debt/libc.md.

Tested

Windows 11, MinGW-w64 GCC 15.2.0 (WinLibs r7), on main 43e3a70.

  • guest_call_trace (new): integer, floating-point, mixed, variadic and struct-return calls through a thunk return the same result as the direct call; the ring holds the calls in order with the right thread, names and a caller address inside the test image; 4 threads of 20000 calls leave the last 65536 calls with consecutive sequence numbers; the counters match.
  • The same test fails when the thunk is changed to clobber rax, r9 or xmm3 before the jump.
  • guest_call_trace_abort (new): the abort report lists the two traced calls.
  • windows_backtrace_report still passes.
  • tools/check_conventions.py and tools/check_test_registration.py pass.

With the relinker option from the follow-up PR, DOOM Shareware (PPSA99666, lowbit/ps5-doom v1.1.1, Intel i7-1165G7, before the rebase onto current main) ran with every library traced to the same point as untraced: 901,307,599 calls recorded, normal process exit. Not built on Linux or macOS; the source file is compiled only on Windows.

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 in TechnicalDebt
  • 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: none
  • Unblocks: none
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

@github-actions github-actions Bot added libc build docs needs rebase Conflicts with main, rebase needed labels Oct 12, 2026
@codingreapermaps
codingreapermaps force-pushed the feat/libc-call-trace-runtime branch from 5bddaa0 to 885276d Compare October 12, 2026 22:23
@codingreapermaps
codingreapermaps force-pushed the feat/libc-call-trace-runtime branch from 885276d to 0991921 Compare October 12, 2026 22:31
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 12, 2026
@codingreapermaps
codingreapermaps force-pushed the feat/libc-call-trace-runtime branch from 0991921 to 121221c Compare October 13, 2026 09:17
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 13, 2026
TraceImport_nid_no_patch wraps a function of a library named in
APS5_TRACE_CALLS in a thunk that records the import, the thread and
the caller in a ring and counts the call, then jumps to the function.
One target keeps one thunk, so function pointers compare equal. The
crash and abort reports print the last 64 calls and the 32 most called
imports. Nothing calls TraceImport_nid_no_patch yet.

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

Copy link
Copy Markdown
Collaborator

Reviewed the thunk (register save/restore, 16-byte stack alignment, return address read at the right offset, stack arguments and rax for varargs preserved) and the ring/count logic; no findings. The only conflict with main was a docs/dev/debt/libc.md adjacency, so I rebased and moved the debt line a few lines up. The Windows-only code and its tests are verified by the full Build run before merging; rebasing and merging.

@adriadam10
adriadam10 force-pushed the feat/libc-call-trace-runtime branch from 121221c to 3953a8d Compare October 13, 2026 14:19
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 13, 2026
@adriadam10
adriadam10 merged commit c496fd5 into boykopovar:main Oct 13, 2026
14 checks passed
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.

2 participants