Repository navigation
fix(profiling): drop leftover memcpy after no-syscall foreign takeover - #21233
vlad-scherbich wants to merge 2 commits into
Conversation
Dependency direction analysis
|
Codeowners resolved asResolved from the full PR diff against |
There was a problem hiding this comment.
🔵 Needs a closer look
Native signal-handler and fork safety need human review backed by compiled regression results, including deterministic coverage of the restart guards.
1 open issue
What changed in this PR
This PR completes the profiling safety stack by refusing sampling after foreign signal-handler takeover leaves no safe syscall fallback.
Changes:
- Adds startup guards and clears fast-copy state when fallback fails.
- Restores recovery state when a usable copy method becomes available.
- Adds a Linux-only test override and regressions for takeover, fork, and re-enable behavior.
| File | Description |
|---|---|
| tests/profiling/collector/test_copy_memory_stats.py | Adds no-fallback takeover, fork, and recovery regressions. |
| ddtrace/internal/datadog/profiling/stack/src/stack.cpp | Exposes the test-only syscall availability override. |
| ddtrace/internal/datadog/profiling/stack/src/sampler.cpp | Guards startup after takeover and refreshes failure metadata. |
| ddtrace/internal/datadog/profiling/stack/src/echion/vm.cc | Updates active and failure flags when copy methods change. |
| ddtrace/internal/datadog/profiling/stack/echion/echion/vm.h | Documents the availability override in a comment. |
| ddtrace/internal/datadog/profiling/stack/_stack.pyi | Declares the test-only override. |
| ddtrace/internal/datadog/profiling/stack/__init__.pyi | Clarifies test-helper access in comments. |
Review effort: Balanced
9186291 to
2f3e02f
Compare
0591002 to
476f9b5
Compare
dd8fe47 to
0a920dd
Compare
476f9b5 to
2ec0060
Compare
2ec0060 to
d4975e8
Compare
bdca954 to
d316274
Compare
When sticky takeover leaves no process_vm_readv fallback, stop sampling and clear leftover safe_memcpy so a forked child cannot sample under a foreign handler. Adds the test-only process_vm_readv override used by the regression.
Add test-only hooks so start() refuses sticky takeover with leftover safe_memcpy before failed_safe_copy is latched, and ship the PR4 reno.
d4975e8 to
f01b8f0
Compare
❌ ErrorsYour PR has failed checks. Please review the issues below and take necessary action before merging. 🚦 1 Pipeline job failed
ℹ️ InfoNo other issues found (see more)🧪 All tests passed Useful? React with 👍 / 👎 This comment will be updated automatically if new data arrives.🔗 Commit SHA: f01b8f0 | Docs | View more details | Give us feedback! |
BenchmarksBenchmark execution time: 2026-10-27 23:30:42 Comparing candidate commit f01b8f0 in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 582 metrics, 10 unstable metrics, 1 flaky benchmarks without significant changes.
|
| // Returns true if the caller may continue sampling; false if there is no safe | ||
| // fallback left and the caller must stop the sampler. | ||
| static bool | ||
| drop_fast_copy_after_foreign_takeover() |
There was a problem hiding this comment.
| drop_fast_copy_after_foreign_takeover() | |
| can_safely_continue_sampling() |
Let's rename that for clarity?
| } | ||
|
|
||
| static PyObject* | ||
| stack_set_process_vm_readv_available(PyObject* Py_UNUSED(self), PyObject* args) |
There was a problem hiding this comment.
Why do we need to expose that?
| // Test-only: set sticky foreign takeover without changing the active copy method | ||
| // or latching failed_safe_copy. Pins start()/sampling_thread() leftover-memcpy | ||
| // guards independently of the failed-disable path. | ||
| static PyObject* | ||
| stack_mark_fast_copy_foreign_takeover(PyObject* Py_UNUSED(self), PyObject* Py_UNUSED(args)) | ||
| { | ||
| mark_fast_copy_foreign_takeover(); | ||
| Py_RETURN_NONE; | ||
| } | ||
|
|
||
| // Test-only: set fast_copy_active without changing safe_copy. Reproduces the | ||
| // inconsistent active==false / leftover-memcpy state a concurrent fork can leave. | ||
| static PyObject* | ||
| stack_force_fast_copy_active(PyObject* Py_UNUSED(self), PyObject* args) | ||
| { |
There was a problem hiding this comment.
If that is purely in order to get test coverage... I don't think we should. Having getters for internal native state isn't great but it's acceptable, having setters that emulate a different internal state just feels wrong...
There was a problem hiding this comment.
when sticky foreign takeover leaves no
process_vm_readv fallback
I don't understand what that means so I'm having a hard time understanding what I'm reviewing to be honest. Why would a foreign handler takeover "leave" or "not leave" a fallback? Isn't the fallback a property of whether it's supported by the platform?
stop sampling and clear leftover
safe_memcpyso a forked child cannot sample under a foreign handler
IIUC, previously not having a fallback would put us into a state where we stayed in "safe memcpy is active" (because we can't fall back to anything) and we would thus keep sampling with it despite not having the handler?

Description
If this PR merges alone on top of PR3: when sticky foreign takeover leaves no
process_vm_readvfallback, stop sampling and clear leftoversafe_memcpyso a forked child cannot sample under a foreign handler. Adds the test-only_set_process_vm_readv_availableoverride used by the regression.drop_fast_copy_after_foreign_takeoverTesting
Risks
Additional Notes
Stack: 4/4 (base PR3). Tree matches preserved #19972 tip
refs/preserved/pr-19972-tip-20261023.Pre-review checklist
https://datadoghq.atlassian.net/browse/PROF-16020