Skip to content

fix(profiling): keep fast-copy intent across warmup and fork - #19972

Draft
vlad-scherbich wants to merge 33 commits into
mainfrom
vlad/prof-15342-fastcopy-warmup-fork
Draft

vlad-scherbich wants to merge 33 commits into
mainfrom
vlad/prof-15342-fastcopy-warmup-fork

Conversation

@vlad-scherbich

@vlad-scherbich vlad-scherbich commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Description

The sampler treated fast_copy_active as "we want safe_memcpy". Warmup clears that flag while our SIGSEGV handler is still installed, which caused four permanent / unsafe outcomes:

  1. faulthandler.enable() during warmup took the handler unnoticed, and the warmup deadline pinned the process to the syscall copy for life.
  2. A child forked mid-warmup inherited fast_copy_active == false and never upgraded (prefork workers).
  3. After a foreign SIGSEGV takeover, the sticky fast_copy_foreign_takeover flag must survive stop → set_fast_copy(True) → restart so we never reclaim the handler.
  4. After a foreign takeover with no process_vm_readv, a forked child must not keep sampling leftover safe_memcpy under the foreign handler.

Code Changes

  • Persistent fast_copy_desired (warmup does not clear it).
  • Sticky fast_copy_foreign_takeover across fork and across stop / set_fast_copy / restart.
  • Drop leftover memcpy sampling when takeover leaves no syscall fallback.
  • Publish desired + foreign-takeover in internal metadata.

Testing

Against origin/main's native tree these fail; with the fix they pass in test_copy_memory_stats.py / test_faulthandler.py:

  • test_fast_copy_faulthandler_enable_during_warmup
  • test_fast_copy_fork_during_warmup
  • test_fast_copy_foreign_handler_takeover_metadata (sticky across stop / set_fast_copy / restart)
  • test_fast_copy_fork_after_takeover_without_syscall (no leftover memcpy in the child)

Pre-review checklist

  • The changes have been tested in real services in staging
  • The PR does not result in new crashes

https://datadoghq.atlassian.net/browse/PROF-16020

@linear-code

linear-code Bot commented Aug 31, 2026

Copy link
Copy Markdown

PROF-15

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Codeowners resolved as

Resolved from the full PR diff against main using the target branch CODEOWNERS file.
CODEOWNERS team requests not listed below are not required by the current file set.

ddtrace/internal/core/crashtracking.py                                  @DataDog/profiling-python @DataDog/apm-core-python
ddtrace/internal/datadog/profiling/dd_wrapper/include/profiler_stats.hpp  @DataDog/profiling-python
ddtrace/internal/datadog/profiling/dd_wrapper/src/profiler_stats.cpp    @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/__init__.pyi                   @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/_stack.pyi                     @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/echion/echion/danger.h         @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/echion/echion/vm.h             @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/include/sampler.hpp            @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/src/echion/danger.cc           @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/src/echion/vm.cc               @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/src/sampler.cpp                @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/src/stack.cpp                  @DataDog/profiling-python
ddtrace/profiling/_faulthandler.py                                      @DataDog/profiling-python
releasenotes/notes/profiling-fast-copy-warmup-fork-3f1c8a4d92b7e05a.yaml  @DataDog/apm-python
tests/crashtracker/test_crashtracking_handler_swap.py                   @DataDog/profiling-python
tests/profiling/collector/test_copy_memory_stats.py                     @DataDog/profiling-python
tests/profiling/collector/test_utils.py                                 @DataDog/profiling-python
tests/profiling/test_faulthandler.py                                    @DataDog/profiling-python
tests/profiling/test_main.py                                            @DataDog/profiling-python

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Dependency direction analysis

⚠️ Existing dependency direction violations

There are 53 dependency direction violations that already exist on the base branch and have not been changed by this PR.

Show existing violations (showing 5 of 53 highest severity)
ddtrace.internal.tracemethods -×-> ddtrace.trace  (internal-core -> product:tracing, score=121)
ddtrace.internal.openfeature._span_enrichment -×-> ddtrace.trace  (product:openfeature -> product:tracing, score=119)
ddtrace.internal.ci_visibility.git_client -×-> ddtrace.trace  (product:ci_visibility -> product:tracing, score=119)
ddtrace.internal.ci_visibility.filters -×-> ddtrace.trace  (product:ci_visibility -> product:tracing, score=119)
ddtrace.aiguard._api_client -×-> ddtrace.trace  (product:aiguard -> product:tracing, score=119)

To see all violations, download the layers-base.json and layers-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/layers.py compare layers-base.json layers-pr.json

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Circular import analysis

⚠️ Existing circular imports

There are 1 circular imports that already exist on the base branch and have not been changed by this PR.

ddtrace.errortracking._handled_exceptions.bytecode_injector -> ddtrace.errortracking._handled_exceptions.callbacks -> ddtrace.errortracking._handled_exceptions.collector -> ddtrace.errortracking._handled_exceptions.bytecode_reporting -> ddtrace.errortracking._handled_exceptions.bytecode_injector

@datadog-official

datadog-official Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Tests  Pipelines

⚠️ Warnings

Your PR has warnings. Please review the issues below.

❄️ 2 New flaky tests detected

test_fast_copy_reenable_clears_failed_safe_copy[py3.10] from test_copy_memory_stats.py
Expected status 0, got 1.
=== Captured STDOUT ===
b''=== End of captured STDOUT ===
=== Captured STDERR ===
b'No safe memory copy method available (safe_memcpy and process_vm_readv both failed); disabling stack profiling\nddtrace stack profiler: no longer owns SIGSEGV and SIGBUS; falling back to syscall-based memory copy to avoid crashing. Handler owners: SIGSEGV=SIG_DFL, SIGBUS=SIG_DFL\nNo safe memory copy method available (safe_memcpy and process_vm_readv both failed); disabling stack profiling\nddtrace stack profiler: no safe memory-copy fallback available; stopping stack sampling to avoid crashing. Handler owners: SIGSEGV=SIG_DFL, SIGBUS=SIG_DFL\nTraceback (most recent call last):\n  File "tests/profiling/collector/test_copy_memory_stats.py", line 542, in <module>\n    assert wait_for_fast_copy_state(_stack, True, timeout=20.0), "sampler failed to restart after re-enable"\nAssertionError: sampler failed to restart after re-enable\n'=== End of captured STDERR ===
test_fast_copy_reenable_clears_failed_safe_copy[py3.11] from test_copy_memory_stats.py
Expected status 0, got 1.
=== Captured STDOUT ===
b''=== End of captured STDOUT ===
=== Captured STDERR ===
b'No safe memory copy method available (safe_memcpy and process_vm_readv both failed); disabling stack profiling\nddtrace stack profiler: no longer owns SIGSEGV and SIGBUS; falling back to syscall-based memory copy to avoid crashing. Handler owners: SIGSEGV=SIG_DFL, SIGBUS=SIG_DFL\nNo safe memory copy method available (safe_memcpy and process_vm_readv both failed); disabling stack profiling\nddtrace stack profiler: no safe memory-copy fallback available; stopping stack sampling to avoid crashing. Handler owners: SIGSEGV=SIG_DFL, SIGBUS=SIG_DFL\nTraceback (most recent call last):\n  File "tests/profiling/collector/test_copy_memory_stats.py", line 542, in <module>\n    assert wait_for_fast_copy_state(_stack, True, timeout=20.0), "sampler failed to restart after re-enable"\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: sampler failed to restart after re-enable\n'=== End of captured STDERR ===

View in Flaky Test Management

ℹ️ Info

No other issues found (see more)

🧪 All tests passed

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 6c7a5d0 | Docs | View more details | Give us feedback!

@vlad-scherbich vlad-scherbich added the Profiling Continous Profling label Sep 1, 2026
@vlad-scherbich vlad-scherbich changed the title fix(profiling): keep fast-copy intent across warmup and fork (PROF-15… fix(profiling): keep fast-copy intent across warmup and fork (PROF-15342) Sep 1, 2026
@vlad-scherbich
vlad-scherbich requested a lite review from Copilot September 1, 2026 17:13
@vlad-scherbich

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e79a85d4d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:483

  • Same as above: when falling back after a mid-run handler takeover, update the process-static ProfilerStats metadata after flipping fast_copy_foreign_takeover / syscall fallback. Otherwise profiles can continue to report the initial fast-copy metadata even after the permanent fallback.
                handler_fallback_done = true;
                mark_fast_copy_syscall_fallback();
                fast_copy_foreign_takeover.store(true, std::memory_order_relaxed);

@vlad-scherbich
vlad-scherbich force-pushed the vlad/prof-15342-fastcopy-warmup-fork branch 3 times, most recently from 7487407 to ffdaec6 Compare September 4, 2026 14:05
@vlad-scherbich
vlad-scherbich changed the base branch from main to vlad/prof-segv-chainback-siginfo September 4, 2026 14:08
@vlad-scherbich
vlad-scherbich force-pushed the vlad/prof-15342-fastcopy-warmup-fork branch from 4ea4450 to 8e54f58 Compare September 15, 2026 22:00
@vlad-scherbich
vlad-scherbich changed the base branch from vlad/prof-segv-chainback-siginfo to main September 15, 2026 22:00
@vlad-scherbich
vlad-scherbich force-pushed the vlad/prof-15342-fastcopy-warmup-fork branch from 8e54f58 to 2db34a5 Compare September 25, 2026 19:41
@pr-commenter

pr-commenter Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-10-23 17:26:42

Comparing candidate commit 6c7a5d0 in PR branch vlad/prof-15342-fastcopy-warmup-fork with baseline commit b8fdb38 in branch main.

📊 Benchmarking dashboard

Found 1 performance improvements and 0 performance regressions! Performance is the same for 586 metrics, 10 unstable metrics, 24 flaky benchmarks without significant changes.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:iastaspects-upper_aspect

  • 🟩 execution_time [-22.458µs; -18.770µs] or [-8.645%; -7.226%]

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:coreapiscenario-context_with_data_listeners

  • unstable execution_time [-778.481ns; +681.713ns] or [-7.277%; +6.372%]

scenario:coreapiscenario-core_dispatch_1_listener

  • unstable execution_time [-45.642ns; +33.167ns] or [-6.689%; +4.861%]

scenario:coreapiscenario-core_dispatch_50_listeners

  • unstable execution_time [-2333.755ns; +1505.454ns] or [-11.579%; +7.469%]

scenario:coreapiscenario-core_dispatch_exception_listeners

  • unstable execution_time [-2100.404ns; +1531.840ns] or [-10.966%; +7.997%]

scenario:coreapiscenario-core_dispatch_listeners

  • unstable execution_time [-444.043ns; +320.605ns] or [-10.316%; +7.448%]

scenario:coreapiscenario-core_dispatch_no_args_listeners

  • unstable execution_time [-291.059ns; +165.389ns] or [-10.703%; +6.082%]

scenario:coreapiscenario-core_dispatch_with_results_1_listener

  • unstable execution_time [-109.239ns; +77.556ns] or [-7.636%; +5.422%]

scenario:coreapiscenario-core_dispatch_with_results_50_listeners

  • unstable execution_time [-4991.220ns; +4716.973ns] or [-9.876%; +9.333%]

scenario:coreapiscenario-core_dispatch_with_results_listeners

  • unstable execution_time [-1085.280ns; +905.978ns] or [-10.177%; +8.495%]

scenario:packagesupdateimporteddependencies-import_many_stdlib_cached

  • unstable execution_time [-55530.664ns; +54856.697ns] or [-9.667%; +9.550%]

Known flaky benchmarks

These benchmarks are marked as flaky and will not trigger a failure. Modify FLAKY_BENCHMARKS_REGEX to control which benchmarks are marked as flaky.

Known flaky benchmarks without significant changes:

  • scenario:errortrackingflasksqli-baseline
  • scenario:flasksimple-iast-get
  • scenario:httppropagationinject-ids_only
  • scenario:iastaspects-casefold_aspect
  • scenario:iastaspects-casefold_noaspect
  • scenario:iastaspects-index_aspect
  • scenario:iastaspects-ljust_noaspect
  • scenario:iastaspects-lower_aspect
  • scenario:iastaspects-replace_aspect
  • scenario:iastaspects-rstrip_aspect
  • scenario:iastaspects-swapcase_aspect
  • scenario:iastaspects-title_noaspect
  • scenario:iastaspects-translate_aspect
  • scenario:iastaspects-translate_noaspect
  • scenario:iastaspects-upper_noaspect
  • scenario:iastaspectsospath-ospathbasename_aspect
  • scenario:iastaspectssplit-rsplit_aspect
  • scenario:packagespackageforrootmodulemapping-cache_off
  • scenario:packagespackageforrootmodulemapping-cache_on
  • scenario:sethttpmeta-all-enabled
  • scenario:span-start
  • scenario:telemetryaddmetric-1-count-metric-1-times
  • scenario:telemetryaddmetric-record-100-metrics
  • scenario:tracer-small

@vlad-scherbich
vlad-scherbich marked this pull request as ready for review September 29, 2026 15:49
@vlad-scherbich
vlad-scherbich requested review from a team as code owners September 29, 2026 15:49
@vlad-scherbich
vlad-scherbich marked this pull request as draft September 29, 2026 15:57
@vlad-scherbich vlad-scherbich changed the title fix(profiling): keep fast-copy intent across warmup and fork (PROF-15342) fix(profiling): keep fast-copy intent across warmup and fork Sep 29, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b12dc1f33

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copilot AI 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.

🔵 Needs a closer look

Four unresolved moderate findings remain, including concurrency races and a non-Linux test failure.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Crashtracker fallback races with sampler shutdown

ddtrace/​internal/​datadog/​profiling/​stack/​src/​stack.cpp:1056

stack_uninstall_segv_handler is also called by crashtracking.start() without pausing the sampler (ddtrace/internal/core/crashtracking.py:277-283), not only by the faulthandler wrapper. When a foreign handler is already installed, this new branch calls set_fast_copy_enabled(false) and may call Sampler::stop() while the sampling thread is in safe_memcpy/reading fast_copy_active, creating a data race and an unsafe handler/copy transition. Move this fallback/stop to the sampling thread, or make the crashtracker path coordinate a pause before entering this branch.

1 resolved since last review

Review effort: Lite

Post-warmup foreign takeover without process_vm_readv snapshotted
enabled before disable and broke before the end-of-cycle update.
Seed before launching the sampling thread to avoid racing the
non-atomic strategy flags, and reseed when disable/start fails.
uninstall_segv_handler can disable fast copy or stop the sampler on the
sticky-foreign path; crashtracking.start must pause first, matching the
faulthandler wrapper, so that does not race the sampling thread.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: da61646747

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread ddtrace/profiling/_faulthandler.py Resolved
Comment thread ddtrace/internal/core/crashtracking.py Resolved
On pause timeout, skip faulthandler/crashtracker install so an in-flight
safe_memcpy is not left without its recovery handler. After a failed
disable with no process_vm_readv, uninstall leftover recovery handlers
so later coordinated installs do not sit above us.

Copilot AI 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.

🟡 Changes recommended

Signal-handler initialization and cleanup can race with active safe-copy sampling, risking unsafe fault handling.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Avoid enabling handlers while safe-copy sampling remains active

ddtrace/​profiling/​_faulthandler.py:48

When pause_sampling() returns None, the sampler is still running and its contract explicitly says the caller must not swap signal handlers. However, this path still calls _original_enable below, replacing SIGSEGV/SIGBUS while an in-flight safe_memcpy may be executing; a fault before the sampling loop notices the takeover will reach faulthandler instead of the recovery handler and can terminate the process. Defer the enable (or otherwise stop/disable safe-copy sampling) until the handler swap is safe; skipping only uninstall/reinstall is not sufficient.

Review effort: Lite

Comment thread ddtrace/internal/core/crashtracking.py Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fb25fb21f0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread ddtrace/internal/core/crashtracking.py Resolved

@taegyunkim taegyunkim 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.

I tried to review this in more detail but didn't want to delay the review as well. So here are some comments regarding vm.h for the first iteration

Failed disable without process_vm_readv uninstalled the handler, but
set_fast_copy(True) only restored the copy function. Clear the user-
disabled latch on re-enable, gate crashtracker coordination on
stack.is_available, and assert the handler is back in the regression
test.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e816bfa790

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

…sable

A successful set_fast_copy(False) left the SIGSEGV/SIGBUS recovery handler
installed while handler ops were off, so faulthandler/crashtracker could
chain to the dormant handler and set_fast_copy(True) reclaim formed a cycle.

Copilot AI 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.

🟡 Changes recommended

Unresolved critical signal-handler and fallback-state issues could allow unsafe sampling or leave the profiler recovery handler uninstalled.

3 open issues
1 resolved since last review

Review effort: Lite

Comment thread ddtrace/internal/core/crashtracking.py Resolved
@vlad-scherbich
vlad-scherbich marked this pull request as draft October 23, 2026 16:14
Avoid overwriting a foreign SIGSEGV/SIGBUS owner on set_fast_copy(True),
fall back when init_segv_catcher fails after arming safe_memcpy, and
reinstall the stack recovery handler in crashtracker start finally
before resuming sampling.
@vlad-scherbich
vlad-scherbich requested a lite review from Copilot October 23, 2026 16:24

Copilot AI 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.

🟡 Changes recommended

A critical signal-handler ownership issue remains unresolved in stack.cpp.

1 open issue
3 resolved since last review

Review effort: Lite

Copilot AI 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.

🔵 Needs a closer look

The changes involve complex native signal-handler ownership, fork recovery, and profiler lifecycle coordination requiring final human review.

No open issues 🎉

1 resolved since last review

Review effort: Lite

@datadog-official datadog-official Bot 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.

Bits Code Review: FAIL

During warmup, faulthandler.disable() can reclaim a foreign signal handler without detecting takeover, allowing fast-copy activation and potentially creating a handler cycle that bypasses crash reporting.

Open Bits AI session

🤖 Bits Code Review · Commit 6c7a5d0

// for non-recovery faults, so both systems coexist correctly.
if (fast_copy_active) {
// Reclaim after coordinated swap; skipped on foreign takeover (see Sampler::sampling_thread).
if (fast_copy_handler_ops_enabled()) {

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.

P2 Check foreign ownership before reinstalling through disable

During warmup, if another library takes over a recovery signal and the application calls faulthandler.disable() while faulthandler is already disabled, _patched_disable() calls reinstall_segv_handler() without checking ownership. The new desired-intent gate permits this reinstall despite fast copy being inactive. It overwrites the foreign handler without marking takeover, so warmup upgrades to safe_memcpy instead of retaining syscall copy. A foreign handler that saved the profiler handler can also form a cycle that bypasses crash reporting. Detect takeover before disable restores dispositions, while preserving legitimate coordinated restores; the fix requires coordinating the Python wrapper and native handling.

React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it

What each reaction means
  • 👍 Useful: correct, and you're fixing it or will.
  • 👎 Wrong: the finding is incorrect, for example the bug can't happen or the code was misread.
  • 😕 Unclear: you can't tell what it means or why it matters.
  • 👀 Right, not acting: correct, but intended, out of scope for this PR, a known risk, or too minor.

🤖 Bits Code Review · Open Bits AI session

@vlad-scherbich

Copy link
Copy Markdown
Contributor Author

This PR is getting out of hand, with new AI comments appearing after each past one has been addressed. Moving into Draft status, will try to break it into several components, as the LoC size has ballooned with all the suggestions to fix.

@vlad-scherbich

Copy link
Copy Markdown
Contributor Author

Split into a draft stack of 4 PRs (one Summary bullet each) to cut review thrash / LoC size. Leaving this PR open as the old tip; tip SHA preserved as refs/preserved/pr-19972-tip-20261023 (6c7a5d0f1d).

Stack (bottom → top):

  1. fix(profiling): keep fast-copy intent across faulthandler during warmup #21230 — bullet 1: faulthandler during warmup / fast_copy_desired
  2. fix(profiling): re-run fast-copy warmup after mid-warmup fork #21231 — bullet 2: mid-warmup fork child upgrade (pin; mechanism in Matt/pkg #1)
  3. fix(profiling): sticky foreign SIGSEGV takeover across stop/restart #21232 — bullet 3: sticky fast_copy_foreign_takeover across stop/restart (+ faulthandler/crashtracker coordination that co-travels)
  4. fix(profiling): drop leftover memcpy after no-syscall foreign takeover #21233 — bullet 4: drop leftover memcpy after no-syscall foreign takeover

PR4 tree matches the preserved tip. Prefer reviewing the stack; we can close this PR once you migrate.

If you instead want this PR retitled as PR1 with 2–4 stacked on top, say so and we can reshuffle.

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.

5 participants