fix(profiling) wall-time crash after PHP runs on a native thread in ext-grpc - #4198
realFlowControl wants to merge 8 commits into
Conversation
❌ ErrorsYour PR has failed checks. Please review the issues below and take necessary action before merging. 🚦 38 Pipeline jobs failed
|
| Test | New execution time | Base Execution time | Increase | DataDog link |
|---|---|---|---|---|
profiling/tests/phpt/allocation_generator_01.phpt ([profiling] profiling should not crash during a from PHP.profiling.tests.phpt |
25.67s | 616.266006ms | +25.05s (+4065%) | View in Datadog |
profiling/tests/phpt/allocation_generator_01.phpt ([profiling] profiling should not crash during a from php.profiling.tests.phpt |
25.05s | 623.21545ms | +24.43s (+3920%) | View in Datadog |
ℹ️ Info
No other issues found (see more)
❄️ No new flaky tests detected
🎯 Code Coverage (details)
• Patch Coverage: 100.00%
• Overall Coverage: 67.44% (-0.02%)
Useful? React with 👍 / 👎
This comment will be updated automatically if new data arrives.🔗 Commit SHA: 9eb1463 | Docs | View more details | Give us feedback!
ext-grpc
Benchmarks [ profiler ]Benchmark execution time: 2026-09-21 15:52:35 Comparing candidate commit 9eb1463 in PR branch Found 0 performance improvements and 6 performance regressions! Performance is the same for 23 metrics, 7 unstable metrics.
|
0506219 to
8fb0387
Compare
8fb0387 to
0dae555
Compare
efaf119 to
7b5940b
Compare
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e638a4a8d9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pub allocation_profiling_stats: UnsafeCell<MaybeUninit<allocation::AllocationProfilingStats>>, | ||
| /// String cache backing pointers stored in PHP runtime cache slots. | ||
| #[cfg(php_run_time_cache)] | ||
| pub cached_strings: UnsafeCell<MaybeUninit<RefCell<StringSet>>>, |
There was a problem hiding this comment.
Synchronize the cache shared by NTS native threads
When an NTS process receives a sample from an ext-grpc native thread while its PHP thread is also collecting a sample, both threads now access this process-global RefCell<StringSet> through try_borrow_mut(). RefCell is not thread-safe, so its borrow state and the underlying StringSet can race, causing undefined behavior or another crash in the concurrent native-thread workload this change is intended to support. The shared cache needs synchronization, or stack collection must otherwise be serialized.
Useful? React with 👍 / 👎.
Run a PHP closure on a native thread, join it, then call it again on the main thread in the regression test.
Benchmarks [ tracer ]Benchmark execution time: 2026-09-21 16:44:41 Comparing candidate commit 9eb1463 in PR branch Found 2 performance improvements and 1 performance regressions! Performance is the same for 191 metrics, 0 unstable metrics.
|
Why
The
ext-grpcextension may execute PHP code on native threads that are not initialized as PHP threads, meaning they never go through GINIT/GSHUTDOWN.CACHED_STRINGS. At this point, everything is fine.CACHED_STRINGS, which frees the strings, but the runtime cache slots still contain pointers to them.Why did our earlier tests not catch this?
We disable the runtime-cache optimization for CLI, where PHPTs are executed, so we could not see it.
Description
This PR stores the string cache in the profiler's existing PHP module globals. PHP makes it process-global on NTS and thread-local through TSRM on ZTS, matching the runtime cache lifecycle without custom Rust TLS or an unsafe
Syncimplementation.The PHP global is resolved and borrowed once around each full stack walk, not once per frame. GINIT creates the cache and GSHUTDOWN drops it.
The native-thread regression now executes a PHP closure that allocates on a background thread. The helper joins that thread before returning, then the test calls the same closure on the main thread and checks both results and the call count.
I/O profiling is disabled when
ext-grpcis loaded, even with all experimental features enabled. gRPC can execute PHP on a native thread while the main thread does I/O, so sampling that I/O can race with changes to the shared NTS PHP stack. The profiler logs the reason and shows it in phpinfo.The gRPC PHPT sets
ZEND_DONT_UNLOAD_MODULES=1because gRPC itself leaks on unload, even without the profiler. Keeping modules loaded preserves its global references for LeakSanitizer; leak detection stays enabled.Testing
grpc.soand fails against the earlier profiler binaryReviewer checklist