File tree Expand file tree Collapse file tree
ddtrace/internal/datadog/profiling/stack/src Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -262,6 +262,8 @@ Sampler::capture_samples(const microsecond_t wall_time_us)
262262 const bool interpreter_snapshot_complete =
263263 for_each_interp (runtime, [&](InterpreterInfo& interp) { interpreter_candidates.push_back (interp); });
264264#if PY_VERSION_HEX >= 0x030e0000
265+ // This lock-free snapshot can race with code destruction during the sampling cycle. In that case, the current
266+ // cycle may use stale frame metadata; the next cycle observes the generation change and clears the cache.
265267 if (!echion->update_code_object_generations (interpreter_candidates, interpreter_snapshot_complete)) {
266268 return ;
267269 }
@@ -604,6 +606,11 @@ Sampler::postfork_child()
604606 new (&pause_mutex_) std::mutex ();
605607 new (&pause_cv_) std::condition_variable ();
606608
609+ // The parent sampling thread may have been mutating these vectors when fork took its snapshot. Abandon their
610+ // inherited storage instead of traversing potentially inconsistent state in clear() or push_back().
611+ new (&interpreter_candidates) std::vector<InterpreterInfo>();
612+ new (&thread_candidates) std::vector<PyThreadState>();
613+
607614 // Clear stale echion state (mutexes, maps) from parent process
608615 if (echion) {
609616 echion->postfork_child ();
You can’t perform that action at this time.
0 commit comments