Skip to content

gh-150723: Fix perf jitdump files on macOS#150728

Merged
pablogsal merged 3 commits into
python:mainfrom
canova:jitdump
Jun 3, 2026
Merged

gh-150723: Fix perf jitdump files on macOS#150728
pablogsal merged 3 commits into
python:mainfrom
canova:jitdump

Conversation

@canova
Copy link
Copy Markdown
Contributor

@canova canova commented Jun 1, 2026

Python's jitdump output was unusable on macOS, thanks to @not-matthias who noticed it. Profilers like samply showed every Python frame as a raw hex address. There were two separate bugs, both in how python writes this jitdump file:

  1. The thread_id field in JR_CODE_LOAD records was written as a 64-bit value instead of the 32-bit value the format requires. The extra bytes shifted every field after it, so parsers misread the records.

  2. jitdump timestamps used clock_gettime(CLOCK_MONOTONIC). That works on Linux because perf timestamps its samples with the same clock, but macOS profilers use mach_absolute_time(), which is a different clock domain. The two can drift apart by days, so the JIT mappings never lined up with the samples and nothing resolved before. On macOS we now use mach_absolute_time(). Linux keeps CLOCK_MONOTONIC.

With both fixes, samply resolves Python frames from the jitdump end to end.

Note that PYTHONPERFSUPPORT=1 was working since it doesn't use jitdumps. PYTHON_PERF_JIT_SUPPORT=1 was broken.

I also added a test that runs the -Xperf_jit backend through samply and checks that the frames show up (it skips when samply isn't installed similary to the other tests I added before). The existing samply test only covered the -Xperf perf-map path, which wasn't broken.

The perf jitdump format defines the thread id field of the JR_CODE_LOAD
record as a 32-bit value, but on macOS it was declared as a uint64_t
(since pthread_threadid_np() returns a uint64_t). Those extra 8 bytes
plus alignment padding shifted every following field, so parsers reading
the file by the spec misread code_size as the code address and failed to
resolve any Python frames.

Declare thread_id as uint32_t on all platforms and truncate the macOS
thread id when writing the record. The value is only informational.
Symbols are resolved by address, and not thread ids so truncation is
safe here.
@canova
Copy link
Copy Markdown
Contributor Author

canova commented Jun 1, 2026

Hi @pablogsal! Would you mind reviewing this PR?

On macOS the jitdump file is consumed by profilers such as samply, which
timestamp their samples using mach_absolute_time(). The jitdump events were
stamped with clock_gettime(CLOCK_MONOTONIC), a different clock domain that
keeps advancing while the system is asleep, so the JIT code mappings could be
off by days relative to the samples and no Python frame would resolve. Stamp
jitdump events with mach_absolute_time() on macOS so they share the sampler's
clock domain. Linux continues to use CLOCK_MONOTONIC to stay aligned with perf.
Exercise the -Xperf_jit (jitdump) backend through samply and assert that
Python frames resolve, exercising the binary jitdump path end to end.
Skipped when samply is not installed.
Copy link
Copy Markdown
Member

@pablogsal pablogsal left a comment

Choose a reason for hiding this comment

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

I built and tested this on macOS arm64 with samply 0.13.1.

Checks run:

  • ./configure --with-pydebug
  • make -j8
  • ./python.exe -m test test_samply_profiler -v
  • manual samply record --save-only against ./python.exe -Xperf_jit, verifying py::foo:<string>, py::bar:<string>, and py::baz:<string> resolved in the captured profile

The jitdump layout/timestamp changes look correct to me, and the new regression coverage exercises the failing path end to end. Approved.

@pablogsal
Copy link
Copy Markdown
Member

Thanks a lot for looking into this @canova

@pablogsal pablogsal merged commit 494f2e3 into python:main Jun 3, 2026
104 of 106 checks passed
@pablogsal pablogsal added the needs backport to 3.15 pre-release feature fixes, bugs and security fixes label Jun 3, 2026
@miss-islington-app
Copy link
Copy Markdown

Thanks @canova for the PR, and @pablogsal for merging it 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖 I'm not a witch! I'm not a witch!

@bedevere-app
Copy link
Copy Markdown

bedevere-app Bot commented Jun 3, 2026

GH-150832 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 pre-release feature fixes, bugs and security fixes label Jun 3, 2026
pablogsal pushed a commit that referenced this pull request Jun 3, 2026
gh-150723: Fix perf jitdump files on macOS (GH-150728)

The perf jitdump format defines the thread id field of the JR_CODE_LOAD
record as a 32-bit value, but on macOS it was declared as a uint64_t
(since pthread_threadid_np() returns a uint64_t). Those extra 8 bytes
plus alignment padding shifted every following field, so parsers reading
the file by the spec misread code_size as the code address and failed to
resolve any Python frames.

Declare thread_id as uint32_t on all platforms and truncate the macOS
thread id when writing the record. The value is only informational.
Symbols are resolved by address, and not thread ids so truncation is
safe here.

* Use mach_absolute_time for macOS jitdump timestamps

On macOS the jitdump file is consumed by profilers such as samply, which
timestamp their samples using mach_absolute_time(). The jitdump events were
stamped with clock_gettime(CLOCK_MONOTONIC), a different clock domain that
keeps advancing while the system is asleep, so the JIT code mappings could be
off by days relative to the samples and no Python frame would resolve. Stamp
jitdump events with mach_absolute_time() on macOS so they share the sampler's
clock domain. Linux continues to use CLOCK_MONOTONIC to stay aligned with perf.

Exercise the -Xperf_jit (jitdump) backend through samply and assert that
Python frames resolve, exercising the binary jitdump path end to end.
Skipped when samply is not installed.
(cherry picked from commit 494f2e3)

Co-authored-by: Nazım Can Altınova <canaltinova@gmail.com>
@canova
Copy link
Copy Markdown
Contributor Author

canova commented Jun 3, 2026

Thanks a lot for the quick review @pablogsal!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants