Skip to content

fix(profiling): re-initialise string tables post-fork#18063

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 1 commit into
mainfrom
kowalski/fix-profiling-clear-string-tables-post-fork
May 21, 2026
Merged

fix(profiling): re-initialise string tables post-fork#18063
gh-worker-dd-mergequeue-cf854d[bot] merged 1 commit into
mainfrom
kowalski/fix-profiling-clear-string-tables-post-fork

Conversation

@KowalskiThomas

@KowalskiThomas KowalskiThomas commented May 13, 2026

Copy link
Copy Markdown
Contributor

Description

This PR ensures we clear the string tables post-fork. This is meant to address a crash happening on (at least) 4.8.0rc5.

Error UnixSignal: Process terminated with SEGV_MAPERR (SIGSEGV)
#0   0x0000000000000000 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#1   0x000073afd44ec124 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#2   0x000073afd44ec124 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_before_node(unsigned long, unsigned long const&, unsigned long) const [clone .constprop.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1580)
#3   0x000073afd44ece4b std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_node(unsigned long, unsigned long const&, unsigned long) const [clone .isra.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:694)
#4   0x0000000000000000 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#5   0x0000000000000000 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#6   0x000073afd44e4e61 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#7   0x000073afd44e4e61 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#8   0x000073afd44e4e61 StringTable::key(_object*, StringTag) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/strings.cc:58)
#9   0x000073afd44e517f Frame::create(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:24)
#10  0x000073afd44e5436 Frame::get(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:214)
#11  0x000073afd44e55b1 Frame::read(EchionSampler&, _PyInterpreterFrame*, _PyInterpreterFrame**) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:153)
#12  0x000073afd44e56c0 unwind_frame(EchionSampler&, _object*, FrameStack&, unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:56)
#13  0x000073afd44e5859 unwind_python_stack(EchionSampler&, _ts*, FrameStack&) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:103)
#14  0x000073afd44e7bbf ThreadInfo::unwind(EchionSampler&, _ts*) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:15)
#15  0x0000000000000000 __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#16  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#17  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#18  0x000073afd44eb2eb __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#19  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#20  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#21  0x000073afd44eb2eb ThreadInfo::sample(EchionSampler&, _ts*, long) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:698)
#22  0x000073afd44eb789 Datadog::Sampler::sampling_thread(unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:287)
#23  0x000073afd44ebad0 call_sampling_thread(void*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:45)
#24  0x000073afdf06bac3 start_thread (nptl/nptl/pthread_create.c:442)
#25  0x000073afdf0fd8d0 __clone3 

@KowalskiThomas KowalskiThomas added Profiling Continous Profling identified-by:crashtracking Identified by Crash Tracking labels May 13, 2026
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented May 13, 2026

Copy link
Copy Markdown

Codeowners resolved as

ddtrace/internal/datadog/profiling/stack/echion/echion/strings.h        @DataDog/profiling-python
ddtrace/internal/datadog/profiling/stack/src/sampler.cpp                @DataDog/profiling-python
releasenotes/notes/profiling-fix-post-fork-crash-string-table-2b3c3c6749b2c102.yaml  @DataDog/apm-python

@KowalskiThomas KowalskiThomas marked this pull request as ready for review May 13, 2026 08:41
@KowalskiThomas KowalskiThomas requested review from a team as code owners May 13, 2026 08:41

@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: a9deb21451

ℹ️ 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".

Comment thread ddtrace/internal/datadog/profiling/stack/echion/echion/strings.h Outdated
@KowalskiThomas KowalskiThomas force-pushed the kowalski/fix-profiling-clear-string-tables-post-fork branch 2 times, most recently from b37f328 to 17cbcbc Compare May 13, 2026 12:33

@emmettbutler emmettbutler left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Oh cool. I wonder if the same logic is applied to the string table that underlies the TraceWriter.

@KowalskiThomas

Copy link
Copy Markdown
Contributor Author

@emmettbutler From what I know, I don't think so. We only need to do that in the Profiling Sampling Thread because it's a native Thread (not a PeriodicThread nor Python-owned Thread) that we don't stop at fork, which means post-fork everything that Thread touches without synchronisation can be in any kind of weird state. If the Trace Writer Thread is a Periodic Thread (like for example the Profiling Scheduler / Uploader Thread is), then I believe now we definitely stop/join those Threads before fork which means they can't corrupt anything from suddenly stopping to exist at fork. If it's not a PeriodicThread, though, things may be different (but I'd be surprised, as I haven't seen any crashes that would indicate so).

@KowalskiThomas KowalskiThomas changed the title fix(profiling): clear string tables post-fork fix(profiling): re-initialise string tables post-fork May 15, 2026
@KowalskiThomas KowalskiThomas force-pushed the kowalski/fix-profiling-clear-string-tables-post-fork branch from 17cbcbc to 512674c Compare May 15, 2026 14:54
@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Pipelines  Tests

Fix all issues with BitsAI

⚠️ Warnings

🚦 8 Pipeline jobs failed

DataDog/apm-reliability/dd-trace-py | build linux serverless: [arm64, cp315-cp315, v113741589-d2b8243-musllinux_1_2_aarch64, 1]   View in Datadog   GitLab

🔧 Fix in code (Fix with Cursor). AssertionError: Failed with DD_IAST_ENABLED=0: 1 during native module load test.

DataDog/apm-reliability/dd-trace-py | build linux serverless: [amd64, cp315-cp315, v113741238-d2b8243-manylinux2014_x86_64, 1]   View in Datadog   GitLab

🔄 Retry job. This looks flaky and may succeed on retry. Job failed to schedule due to insufficient resources and node affinity issues across multiple nodes.

DataDog/apm-reliability/dd-trace-py | build linux serverless: [arm64, cp315-cp315, v113741357-d2b8243-manylinux2014_aarch64, 1]   View in Datadog   GitLab

🔄 Retry job. This looks flaky and may succeed on retry. Failed to create pod sandbox: unable to allocate IP via local cilium agent: no IPs currently available on the node.

View all 8 failed jobs.

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: d9381a6 | Docs | Datadog PR Page | Give us feedback!

Comment thread ddtrace/internal/datadog/profiling/stack/src/sampler.cpp Outdated
Comment thread ddtrace/internal/datadog/profiling/stack/echion/echion/strings.h
@KowalskiThomas KowalskiThomas force-pushed the kowalski/fix-profiling-clear-string-tables-post-fork branch 2 times, most recently from 4450da7 to 74cd85b Compare May 20, 2026 13:54
@KowalskiThomas KowalskiThomas force-pushed the kowalski/fix-profiling-clear-string-tables-post-fork branch from 74cd85b to d9381a6 Compare May 21, 2026 07:59
@KowalskiThomas

Copy link
Copy Markdown
Contributor Author

/merge -f --reason "microbenchmarks broken for unrelated reason"

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented May 21, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-05-21 12:17:42 UTC ℹ️ Start processing command /merge -f --reason "microbenchmarks broken for unrelated reason"


2026-05-21 12:17:47 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in main is approximately 0s (p90).


2026-05-21 12:17:59 UTC ℹ️ MergeQueue: This merge request was merged

Warning

This change was merged without running any pre merge CI checks

Reason: microbenchmarks broken for unrelated reason

@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot merged commit f1f6901 into main May 21, 2026
582 of 584 checks passed
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot deleted the kowalski/fix-profiling-clear-string-tables-post-fork branch May 21, 2026 12:17
vlad-scherbich added a commit that referenced this pull request Jun 8, 2026
…s to CI

**Deterministic unit test** (`test_ddup_atfork_handler_registered_before_stack_sampler`):
- Mocks `ddup.start` and `StackCollector._init` to record their call order
  during profiler startup, then asserts ddup was first.
- POSIX guarantees FIFO ordering for post-fork child handlers, so whichever
  component calls pthread_atfork first gets its child handler run first.
  ddup must run first to rebuild profile state before the stack sampler
  clears and re-registers threads.
- Catches the regression deterministically without needing timing luck or
  fork() calls — a swap of the init order in profiler.py fails immediately.
- Regression guard for PRs #17183/#17042/#18063.

**CI: stress-ng CPU saturation + 20 iterations**:
- Runs `stress-ng --cpu $(nproc)` in the background during the fork loop to
  saturate all CPUs and widen the pthread_atfork timing race window — the
  same load profile that makes the bug manifest in production Gunicorn/gthread.
- Increased iterations from 10 to 20 to give more chances to surface the race.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

This change is marked for backport to 4.8 and it does not conflict with that branch.
The command used to test backporting was

git checkout 4.8 && git cherry-pick -x --mainline 1 f1f69015b6bf5f5cd0caf30cb9132b121565edcf

github-actions Bot added a commit that referenced this pull request Jun 9, 2026
## Description

This PR ensures we clear the string tables post-fork. This is meant to address a crash happening on (at least) 4.8.0rc5.

```
Error UnixSignal: Process terminated with SEGV_MAPERR (SIGSEGV)
#0   0x0000000000000000 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#1   0x000073afd44ec124 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#2   0x000073afd44ec124 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_before_node(unsigned long, unsigned long const&, unsigned long) const [clone .constprop.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1580)
#3   0x000073afd44ece4b std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_node(unsigned long, unsigned long const&, unsigned long) const [clone .isra.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:694)
#4   0x0000000000000000 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#5   0x0000000000000000 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#6   0x000073afd44e4e61 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#7   0x000073afd44e4e61 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#8   0x000073afd44e4e61 StringTable::key(_object*, StringTag) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/strings.cc:58)
#9   0x000073afd44e517f Frame::create(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:24)
#10  0x000073afd44e5436 Frame::get(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:214)
#11  0x000073afd44e55b1 Frame::read(EchionSampler&, _PyInterpreterFrame*, _PyInterpreterFrame**) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:153)
#12  0x000073afd44e56c0 unwind_frame(EchionSampler&, _object*, FrameStack&, unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:56)
#13  0x000073afd44e5859 unwind_python_stack(EchionSampler&, _ts*, FrameStack&) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:103)
#14  0x000073afd44e7bbf ThreadInfo::unwind(EchionSampler&, _ts*) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:15)
#15  0x0000000000000000 __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#16  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#17  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#18  0x000073afd44eb2eb __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#19  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#20  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#21  0x000073afd44eb2eb ThreadInfo::sample(EchionSampler&, _ts*, long) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:698)
#22  0x000073afd44eb789 Datadog::Sampler::sampling_thread(unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:287)
#23  0x000073afd44ebad0 call_sampling_thread(void*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:45)
#24  0x000073afdf06bac3 start_thread (nptl/nptl/pthread_create.c:442)
#25  0x000073afdf0fd8d0 __clone3
```

Co-authored-by: thomas.kowalski <thomas.kowalski@datadoghq.com>
(cherry picked from commit f1f6901)

Co-authored-by: Thomas Kowalski <thomas.kowalski@datadoghq.com>
KowalskiThomas added a commit that referenced this pull request Jun 9, 2026
…18531)

Backport #18063 to 4.8

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Thomas Kowalski <thomas.kowalski@datadoghq.com>
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

This change is marked for backport to 4.9 and it does not conflict with that branch.
The command used to test backporting was

git checkout 4.9 && git cherry-pick -x --mainline 1 f1f69015b6bf5f5cd0caf30cb9132b121565edcf

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

This change is marked for backport to 4.10 and it does not conflict with that branch.
The command used to test backporting was

git checkout 4.10 && git cherry-pick -x --mainline 1 f1f69015b6bf5f5cd0caf30cb9132b121565edcf

github-actions Bot added a commit that referenced this pull request Jun 9, 2026
## Description

This PR ensures we clear the string tables post-fork. This is meant to address a crash happening on (at least) 4.8.0rc5.

```
Error UnixSignal: Process terminated with SEGV_MAPERR (SIGSEGV)
#0   0x0000000000000000 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#1   0x000073afd44ec124 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#2   0x000073afd44ec124 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_before_node(unsigned long, unsigned long const&, unsigned long) const [clone .constprop.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1580)
#3   0x000073afd44ece4b std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_node(unsigned long, unsigned long const&, unsigned long) const [clone .isra.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:694)
#4   0x0000000000000000 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#5   0x0000000000000000 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#6   0x000073afd44e4e61 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#7   0x000073afd44e4e61 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#8   0x000073afd44e4e61 StringTable::key(_object*, StringTag) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/strings.cc:58)
#9   0x000073afd44e517f Frame::create(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:24)
#10  0x000073afd44e5436 Frame::get(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:214)
#11  0x000073afd44e55b1 Frame::read(EchionSampler&, _PyInterpreterFrame*, _PyInterpreterFrame**) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:153)
#12  0x000073afd44e56c0 unwind_frame(EchionSampler&, _object*, FrameStack&, unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:56)
#13  0x000073afd44e5859 unwind_python_stack(EchionSampler&, _ts*, FrameStack&) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:103)
#14  0x000073afd44e7bbf ThreadInfo::unwind(EchionSampler&, _ts*) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:15)
#15  0x0000000000000000 __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#16  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#17  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#18  0x000073afd44eb2eb __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#19  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#20  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#21  0x000073afd44eb2eb ThreadInfo::sample(EchionSampler&, _ts*, long) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:698)
#22  0x000073afd44eb789 Datadog::Sampler::sampling_thread(unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:287)
#23  0x000073afd44ebad0 call_sampling_thread(void*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:45)
#24  0x000073afdf06bac3 start_thread (nptl/nptl/pthread_create.c:442)
#25  0x000073afdf0fd8d0 __clone3
```

Co-authored-by: thomas.kowalski <thomas.kowalski@datadoghq.com>
(cherry picked from commit f1f6901)

Co-authored-by: Thomas Kowalski <thomas.kowalski@datadoghq.com>
github-actions Bot added a commit that referenced this pull request Jun 9, 2026
## Description

This PR ensures we clear the string tables post-fork. This is meant to address a crash happening on (at least) 4.8.0rc5.

```
Error UnixSignal: Process terminated with SEGV_MAPERR (SIGSEGV)
#0   0x0000000000000000 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#1   0x000073afd44ec124 std::__detail::_Hashtable_base<unsigned long, std::pair<unsigned long const, std::string>, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<false, false, true> >::_M_equals(unsigned long const&, unsigned long, std::__detail::_Hash_node<std::pair<unsigned long const, std::string>, false>*) const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable_policy.h:1804)
#2   0x000073afd44ec124 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_before_node(unsigned long, unsigned long const&, unsigned long) const [clone .constprop.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1580)
#3   0x000073afd44ece4b std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_node(unsigned long, unsigned long const&, unsigned long) const [clone .isra.0] (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:694)
#4   0x0000000000000000 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#5   0x0000000000000000 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#6   0x000073afd44e4e61 std::_Hashtable<unsigned long, std::pair<unsigned long const, std::string>, std::allocator<std::pair<unsigned long const, std::string> >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/hashtable.h:1455)
#7   0x000073afd44e4e61 std::unordered_map<unsigned long, std::string, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::string> > >::find(unsigned long const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unordered_map.h:920)
#8   0x000073afd44e4e61 StringTable::key(_object*, StringTag) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/strings.cc:58)
#9   0x000073afd44e517f Frame::create(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:24)
#10  0x000073afd44e5436 Frame::get(EchionSampler&, PyCodeObject*, int) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:214)
#11  0x000073afd44e55b1 Frame::read(EchionSampler&, _PyInterpreterFrame*, _PyInterpreterFrame**) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/frame.cc:153)
#12  0x000073afd44e56c0 unwind_frame(EchionSampler&, _object*, FrameStack&, unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:56)
#13  0x000073afd44e5859 unwind_python_stack(EchionSampler&, _ts*, FrameStack&) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/stacks.cc:103)
#14  0x000073afd44e7bbf ThreadInfo::unwind(EchionSampler&, _ts*) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:15)
#15  0x0000000000000000 __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#16  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#17  0x0000000000000000 std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#18  0x000073afd44eb2eb __gnu_cxx::__normal_iterator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const*, std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > > >::__normal_iterator(std::unique_ptr<StackInfo, std::default_delete<StackInfo> > const* const&) (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_iterator.h:979)
#19  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::end() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:839)
#20  0x000073afd44eb2eb std::vector<std::unique_ptr<StackInfo, std::default_delete<StackInfo> >, std::allocator<std::unique_ptr<StackInfo, std::default_delete<StackInfo> > > >::empty() const (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/stl_vector.h:1008)
#21  0x000073afd44eb2eb ThreadInfo::sample(EchionSampler&, _ts*, long) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/echion/threads.cc:698)
#22  0x000073afd44eb789 Datadog::Sampler::sampling_thread(unsigned long) [clone .localalias] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:287)
#23  0x000073afd44ebad0 call_sampling_thread(void*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/internal/datadog/profiling/stack/src/sampler.cpp:45)
#24  0x000073afdf06bac3 start_thread (nptl/nptl/pthread_create.c:442)
#25  0x000073afdf0fd8d0 __clone3
```

Co-authored-by: thomas.kowalski <thomas.kowalski@datadoghq.com>
(cherry picked from commit f1f6901)

Co-authored-by: Thomas Kowalski <thomas.kowalski@datadoghq.com>
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.

3 participants