Skip to content

Commit a61e958

Browse files
fix(profiling): reinstall signal handler if needed
1 parent 6a92ecd commit a61e958

File tree

2 files changed

+39
-8
lines changed

2 files changed

+39
-8
lines changed

ddtrace/internal/datadog/profiling/stack_v2/src/echion/danger.cc

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#include <echion/danger.h>
22
#include <echion/state.h>
33

4-
// hello
5-
64
#include <algorithm>
75
#include <cassert>
86
#include <cerrno>
@@ -82,13 +80,33 @@ init_segv_catcher()
8280
sigemptyset(&sa.sa_mask);
8381
// SA_SIGINFO for 3-arg handler; SA_ONSTACK to run on alt stack; SA_NODEFER to avoid having to use savemask
8482
sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER;
85-
if (sigaction(SIGSEGV, &sa, &g_old_segv) != 0) {
86-
return -1;
83+
84+
// Check each handler separately to avoid overwriting g_old_segv/g_old_bus
85+
// with our own handler (which would cause infinite loops on unhandled signals).
86+
struct sigaction current;
87+
88+
bool need_segv = true;
89+
if (sigaction(SIGSEGV, nullptr, &current) == 0 && current.sa_sigaction == segv_handler) {
90+
need_segv = false;
8791
}
88-
if (sigaction(SIGBUS, &sa, &g_old_bus) != 0) {
89-
// Try to roll back SIGSEGV install on failure.
90-
sigaction(SIGSEGV, &g_old_segv, nullptr);
91-
return -1;
92+
if (need_segv) {
93+
if (sigaction(SIGSEGV, &sa, &g_old_segv) != 0) {
94+
return -1;
95+
}
96+
}
97+
98+
bool need_bus = true;
99+
if (sigaction(SIGBUS, nullptr, &current) == 0 && current.sa_sigaction == segv_handler) {
100+
need_bus = false;
101+
}
102+
if (need_bus) {
103+
if (sigaction(SIGBUS, &sa, &g_old_bus) != 0) {
104+
if (need_segv) {
105+
// Roll back SIGSEGV install on failure.
106+
sigaction(SIGSEGV, &g_old_segv, nullptr);
107+
}
108+
return -1;
109+
}
92110
}
93111

94112
return 0;

ddtrace/internal/datadog/profiling/stack_v2/src/sampler.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
#include "dd_wrapper/include/ddup_interface.hpp"
44
#include "thread_span_links.hpp"
55

6+
#include "echion/danger.h"
67
#include "echion/errors.h"
78
#include "echion/greenlets.h"
89
#include "echion/interp.h"
910
#include "echion/tasks.h"
1011
#include "echion/threads.h"
12+
#include "echion/vm.h"
1113

14+
#include <mutex>
1215
#include <pthread.h>
1316

1417
using namespace Datadog;
@@ -143,6 +146,16 @@ Sampler::adapt_sampling_interval()
143146
void
144147
Sampler::sampling_thread(const uint64_t seq_num)
145148
{
149+
// Re-install SIGSEGV/SIGBUS handlers here, after Python initialization.
150+
// The handlers may have been installed during static init, but Python or
151+
// libraries (faulthandler, Django, FastAPI) can overwrite them afterwards.
152+
// Re-installing here ensures our handler is active when the sampling thread runs.
153+
// Only do this once to avoid overwriting g_old_segv with our own handler.
154+
static std::once_flag segv_handler_once;
155+
if (use_alternative_copy_memory()) {
156+
std::call_once(segv_handler_once, init_segv_catcher);
157+
}
158+
146159
using namespace std::chrono;
147160
auto sample_time_prev = steady_clock::now();
148161
auto interval_adjust_time_prev = sample_time_prev;

0 commit comments

Comments
 (0)