File indexing completed on 2025-01-18 09:27:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #ifndef ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_
0025 #define ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_
0026
0027 #include <atomic>
0028 #include <cstddef>
0029 #include <functional>
0030
0031 #include "absl/base/config.h"
0032 #include "absl/base/thread_annotations.h"
0033 #include "absl/synchronization/mutex.h"
0034 #include "absl/time/time.h"
0035
0036 namespace absl {
0037 ABSL_NAMESPACE_BEGIN
0038 namespace profiling_internal {
0039
0040
0041
0042 template <typename T>
0043 struct Sample {
0044
0045
0046 absl::Mutex init_mu;
0047 T* next = nullptr;
0048 T* dead ABSL_GUARDED_BY(init_mu) = nullptr;
0049 int64_t weight;
0050 };
0051
0052
0053
0054
0055
0056 template <typename T>
0057 class SampleRecorder {
0058 public:
0059 SampleRecorder();
0060 ~SampleRecorder();
0061
0062
0063 template <typename... Targs>
0064 T* Register(Targs&&... args);
0065
0066
0067 void Unregister(T* sample);
0068
0069
0070
0071
0072
0073 using DisposeCallback = void (*)(const T&);
0074 DisposeCallback SetDisposeCallback(DisposeCallback f);
0075
0076
0077
0078 int64_t Iterate(const std::function<void(const T& stack)>& f);
0079
0080 size_t GetMaxSamples() const;
0081 void SetMaxSamples(size_t max);
0082
0083 private:
0084 void PushNew(T* sample);
0085 void PushDead(T* sample);
0086 template <typename... Targs>
0087 T* PopDead(Targs... args);
0088
0089 std::atomic<size_t> dropped_samples_;
0090 std::atomic<size_t> size_estimate_;
0091 std::atomic<size_t> max_samples_{1 << 20};
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118 std::atomic<T*> all_;
0119 T graveyard_;
0120
0121 std::atomic<DisposeCallback> dispose_;
0122 };
0123
0124 template <typename T>
0125 typename SampleRecorder<T>::DisposeCallback
0126 SampleRecorder<T>::SetDisposeCallback(DisposeCallback f) {
0127 return dispose_.exchange(f, std::memory_order_relaxed);
0128 }
0129
0130 template <typename T>
0131 SampleRecorder<T>::SampleRecorder()
0132 : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) {
0133 absl::MutexLock l(&graveyard_.init_mu);
0134 graveyard_.dead = &graveyard_;
0135 }
0136
0137 template <typename T>
0138 SampleRecorder<T>::~SampleRecorder() {
0139 T* s = all_.load(std::memory_order_acquire);
0140 while (s != nullptr) {
0141 T* next = s->next;
0142 delete s;
0143 s = next;
0144 }
0145 }
0146
0147 template <typename T>
0148 void SampleRecorder<T>::PushNew(T* sample) {
0149 sample->next = all_.load(std::memory_order_relaxed);
0150 while (!all_.compare_exchange_weak(sample->next, sample,
0151 std::memory_order_release,
0152 std::memory_order_relaxed)) {
0153 }
0154 }
0155
0156 template <typename T>
0157 void SampleRecorder<T>::PushDead(T* sample) {
0158 if (auto* dispose = dispose_.load(std::memory_order_relaxed)) {
0159 dispose(*sample);
0160 }
0161
0162 absl::MutexLock graveyard_lock(&graveyard_.init_mu);
0163 absl::MutexLock sample_lock(&sample->init_mu);
0164 sample->dead = graveyard_.dead;
0165 graveyard_.dead = sample;
0166 }
0167
0168 template <typename T>
0169 template <typename... Targs>
0170 T* SampleRecorder<T>::PopDead(Targs... args) {
0171 absl::MutexLock graveyard_lock(&graveyard_.init_mu);
0172
0173
0174
0175
0176 T* sample = graveyard_.dead;
0177 if (sample == &graveyard_) return nullptr;
0178
0179 absl::MutexLock sample_lock(&sample->init_mu);
0180 graveyard_.dead = sample->dead;
0181 sample->dead = nullptr;
0182 sample->PrepareForSampling(std::forward<Targs>(args)...);
0183 return sample;
0184 }
0185
0186 template <typename T>
0187 template <typename... Targs>
0188 T* SampleRecorder<T>::Register(Targs&&... args) {
0189 size_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed);
0190 if (size > max_samples_.load(std::memory_order_relaxed)) {
0191 size_estimate_.fetch_sub(1, std::memory_order_relaxed);
0192 dropped_samples_.fetch_add(1, std::memory_order_relaxed);
0193 return nullptr;
0194 }
0195
0196 T* sample = PopDead(args...);
0197 if (sample == nullptr) {
0198
0199 sample = new T();
0200 {
0201 absl::MutexLock sample_lock(&sample->init_mu);
0202
0203
0204
0205
0206
0207
0208
0209 sample->init_mu.ForgetDeadlockInfo();
0210 sample->PrepareForSampling(std::forward<Targs>(args)...);
0211 }
0212 PushNew(sample);
0213 }
0214
0215 return sample;
0216 }
0217
0218 template <typename T>
0219 void SampleRecorder<T>::Unregister(T* sample) {
0220 PushDead(sample);
0221 size_estimate_.fetch_sub(1, std::memory_order_relaxed);
0222 }
0223
0224 template <typename T>
0225 int64_t SampleRecorder<T>::Iterate(
0226 const std::function<void(const T& stack)>& f) {
0227 T* s = all_.load(std::memory_order_acquire);
0228 while (s != nullptr) {
0229 absl::MutexLock l(&s->init_mu);
0230 if (s->dead == nullptr) {
0231 f(*s);
0232 }
0233 s = s->next;
0234 }
0235
0236 return dropped_samples_.load(std::memory_order_relaxed);
0237 }
0238
0239 template <typename T>
0240 void SampleRecorder<T>::SetMaxSamples(size_t max) {
0241 max_samples_.store(max, std::memory_order_release);
0242 }
0243
0244 template <typename T>
0245 size_t SampleRecorder<T>::GetMaxSamples() const {
0246 return max_samples_.load(std::memory_order_acquire);
0247 }
0248
0249 }
0250 ABSL_NAMESPACE_END
0251 }
0252
0253 #endif