Skip to content

Commit 64d6eb7

Browse files
fix(profiling): crash with synchronized_sample_pool on uWSGI shutdown [backport 2.21] (#13401)
backport from #13370 (cherry picked from commit 9b8d0cb) ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
1 parent 1c3566d commit 64d6eb7

File tree

9 files changed

+76
-135
lines changed

9 files changed

+76
-135
lines changed

ddtrace/internal/datadog/profiling/dd_wrapper/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ add_library(
4949
src/receiver_interface.cpp
5050
src/sample.cpp
5151
src/sample_manager.cpp
52-
src/synchronized_sample_pool.cpp
52+
src/static_sample_pool.cpp
5353
src/uploader.cpp
5454
src/uploader_builder.cpp)
5555

ddtrace/internal/datadog/profiling/dd_wrapper/include/constants.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ constexpr unsigned int g_backend_max_nframes = 512;
1414
// Maximum amount of time, in milliseconds, to wait for crashtracker signal handler
1515
constexpr uint64_t g_crashtracker_timeout_ms = 5000;
1616

17-
// Default value for the max number of samples to keep in the SynchronizedSamplePool
17+
// Default value for the max number of samples to keep in the StaticSamplePool
1818
constexpr size_t g_default_sample_pool_capacity = 4;
1919

2020
// Default name of the runtime. This will almost certainly get overridden by the caller, but we set it here

ddtrace/internal/datadog/profiling/dd_wrapper/include/sample_manager.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include "constants.hpp"
44
#include "sample.hpp"
5-
#include "synchronized_sample_pool.hpp"
65
#include "types.hpp"
76

87
#include <array>
@@ -20,7 +19,6 @@ class SampleManager
2019
static inline unsigned int max_nframes{ g_default_max_nframes };
2120
static inline SampleType type_mask{ SampleType::All };
2221
static inline size_t sample_pool_capacity{ g_default_sample_pool_capacity };
23-
static inline std::unique_ptr<SynchronizedSamplePool> sample_pool{ nullptr };
2422

2523
public:
2624
// Configuration
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
#include <optional>
5+
6+
#include "sample.hpp"
7+
8+
namespace Datadog {
9+
10+
class StaticSamplePool
11+
{
12+
public:
13+
static constexpr std::size_t CAPACITY = g_default_sample_pool_capacity;
14+
15+
static std::optional<Sample*> take_sample();
16+
static std::optional<Sample*> return_sample(Sample* sample);
17+
18+
private:
19+
StaticSamplePool() = delete;
20+
StaticSamplePool(const StaticSamplePool&) = delete;
21+
StaticSamplePool& operator=(const StaticSamplePool&) = delete;
22+
};
23+
24+
} // namespace Datadog

ddtrace/internal/datadog/profiling/dd_wrapper/include/synchronized_sample_pool.hpp

Lines changed: 0 additions & 30 deletions
This file was deleted.

ddtrace/internal/datadog/profiling/dd_wrapper/src/sample_manager.cpp

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "sample_manager.hpp"
2+
#include "static_sample_pool.hpp"
23
#include "types.hpp"
34

45
void
@@ -38,11 +39,7 @@ Datadog::SampleManager::set_sample_pool_capacity(size_t capacity)
3839
Datadog::Sample*
3940
Datadog::SampleManager::start_sample()
4041
{
41-
if (sample_pool == nullptr) {
42-
return new Datadog::Sample(type_mask, max_nframes); // NOLINT(cppcoreguidelines-owning-memory)
43-
}
44-
45-
auto sample_opt = sample_pool->take_sample();
42+
auto sample_opt = StaticSamplePool::take_sample();
4643
if (sample_opt.has_value()) {
4744
return sample_opt.value();
4845
}
@@ -56,14 +53,7 @@ Datadog::SampleManager::start_sample()
5653
void
5754
Datadog::SampleManager::drop_sample(Datadog::Sample* sample)
5855
{
59-
if (sample_pool == nullptr) {
60-
delete sample; // NOLINT(cppcoreguidelines-owning-memory)
61-
return;
62-
}
63-
64-
sample->clear_buffers();
65-
66-
std::optional<Sample*> result_opt = sample_pool->return_sample(sample);
56+
std::optional<Sample*> result_opt = StaticSamplePool::return_sample(sample);
6757
// If the pool is full, the pool returns the pointer and we need to delete the sample.
6858
if (result_opt.has_value()) {
6959
delete result_opt.value(); // NOLINT(cppcoreguidelines-owning-memory)
@@ -74,19 +64,10 @@ void
7464
Datadog::SampleManager::postfork_child()
7565
{
7666
Datadog::Sample::postfork_child();
77-
if (sample_pool != nullptr) {
78-
// Clear the pool to make sure it's in a consistent state.
79-
// Suppose there was a thread that was adding/removing sample from the pool
80-
// and the fork happened in the middle of that operation.
81-
sample_pool = std::make_unique<SynchronizedSamplePool>(sample_pool_capacity);
82-
}
8367
}
8468

8569
void
8670
Datadog::SampleManager::init()
8771
{
88-
if (sample_pool == nullptr) {
89-
sample_pool = std::make_unique<SynchronizedSamplePool>(sample_pool_capacity);
90-
}
9172
Datadog::Sample::profile_state.one_time_init(type_mask, max_nframes);
9273
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include "static_sample_pool.hpp"
2+
#include "sample.hpp"
3+
#include <optional>
4+
#include <atomic>
5+
#include <cstddef>
6+
7+
namespace Datadog {
8+
9+
static constexpr std::size_t CAPACITY = StaticSamplePool::CAPACITY;
10+
static std::atomic<Sample*> pool[CAPACITY];
11+
12+
struct StaticSamplePoolInitializer {
13+
StaticSamplePoolInitializer() {
14+
for (std::size_t i = 0; i < CAPACITY; ++i) {
15+
pool[i].store(nullptr, std::memory_order_relaxed);
16+
}
17+
}
18+
} initializer;
19+
20+
std::optional<Sample*> StaticSamplePool::take_sample() {
21+
for (std::size_t i = 0; i < CAPACITY; ++i) {
22+
Sample* s = pool[i].exchange(nullptr, std::memory_order_acq_rel);
23+
if (s != nullptr) {
24+
return s;
25+
}
26+
}
27+
return std::nullopt;
28+
}
29+
30+
std::optional<Sample*> StaticSamplePool::return_sample(Sample* sample) {
31+
for (std::size_t i = 0; i < CAPACITY; ++i) {
32+
Sample* expected = nullptr;
33+
if (pool[i].compare_exchange_strong(expected, sample,
34+
std::memory_order_acq_rel,
35+
std::memory_order_relaxed)) {
36+
return std::nullopt;
37+
}
38+
}
39+
return sample;
40+
}
41+
} // namespace Datadog

ddtrace/internal/datadog/profiling/dd_wrapper/src/synchronized_sample_pool.cpp

Lines changed: 0 additions & 79 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fixes:
3+
- |
4+
profiling: fixed an issue in the ``SynchronizedSamplePool`` where pool
5+
could be null when calling into ``ddog_ArrayQueue_`` functions, leading to
6+
segfaults in the uWSGI shutdown

0 commit comments

Comments
 (0)