1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#ifndef GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
32#define GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
33
34#include <atomic>
35#include <cstddef>
36#include <cstdint>
37
38
39// Must be included last.
40#include <google/protobuf/port_def.inc>
41
42namespace google {
43namespace protobuf {
44namespace internal {
45
46#if defined(PROTOBUF_ARENAZ_SAMPLE)
47struct ThreadSafeArenaStats;
48void RecordResetSlow(ThreadSafeArenaStats* info);
49void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t requested,
50 size_t allocated, size_t wasted);
51// Stores information about a sampled thread safe arena. All mutations to this
52// *must* be made through `Record*` functions below. All reads from this *must*
53// only occur in the callback to `ThreadSafeArenazSampler::Iterate`.
54struct ThreadSafeArenaStats
55 : public absl::profiling_internal::Sample<ThreadSafeArenaStats> {
56 // Constructs the object but does not fill in any fields.
57 ThreadSafeArenaStats();
58 ~ThreadSafeArenaStats();
59
60 // Puts the object into a clean state, fills in the logically `const` members,
61 // blocking for any readers that are currently sampling the object.
62 void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
63
64 // These fields are mutated by the various Record* APIs and need to be
65 // thread-safe.
66 std::atomic<int> num_allocations;
67 std::atomic<int> num_resets;
68 std::atomic<size_t> bytes_requested;
69 std::atomic<size_t> bytes_allocated;
70 std::atomic<size_t> bytes_wasted;
71 // Records the largest size an arena ever had. Maintained across resets.
72 std::atomic<size_t> max_bytes_allocated;
73 // Bit i when set to 1 indicates that a thread with tid % 63 = i accessed the
74 // underlying arena. The field is maintained across resets.
75 std::atomic<uint64_t> thread_ids;
76
77 // All of the fields below are set by `PrepareForSampling`, they must not
78 // be mutated in `Record*` functions. They are logically `const` in that
79 // sense. These are guarded by init_mu, but that is not externalized to
80 // clients, who can only read them during
81 // `ThreadSafeArenazSampler::Iterate` which will hold the lock.
82 static constexpr int kMaxStackDepth = 64;
83 int32_t depth;
84 void* stack[kMaxStackDepth];
85 static void RecordAllocateStats(ThreadSafeArenaStats* info, size_t requested,
86 size_t allocated, size_t wasted) {
87 if (PROTOBUF_PREDICT_TRUE(info == nullptr)) return;
88 RecordAllocateSlow(info, requested, allocated, wasted);
89 }
90};
91
92ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
93void UnsampleSlow(ThreadSafeArenaStats* info);
94
95class ThreadSafeArenaStatsHandle {
96 public:
97 explicit ThreadSafeArenaStatsHandle() = default;
98 explicit ThreadSafeArenaStatsHandle(ThreadSafeArenaStats* info)
99 : info_(info) {}
100
101 ~ThreadSafeArenaStatsHandle() {
102 if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
103 UnsampleSlow(info_);
104 }
105
106 ThreadSafeArenaStatsHandle(ThreadSafeArenaStatsHandle&& other) noexcept
107 : info_(absl::exchange(other.info_, nullptr)) {}
108
109 ThreadSafeArenaStatsHandle& operator=(
110 ThreadSafeArenaStatsHandle&& other) noexcept {
111 if (PROTOBUF_PREDICT_FALSE(info_ != nullptr)) {
112 UnsampleSlow(info_);
113 }
114 info_ = absl::exchange(other.info_, nullptr);
115 return *this;
116 }
117
118 void RecordReset() {
119 if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
120 RecordResetSlow(info_);
121 }
122
123 ThreadSafeArenaStats* MutableStats() { return info_; }
124
125 friend void swap(ThreadSafeArenaStatsHandle& lhs,
126 ThreadSafeArenaStatsHandle& rhs) {
127 std::swap(lhs.info_, rhs.info_);
128 }
129
130 friend class ThreadSafeArenaStatsHandlePeer;
131
132 private:
133 ThreadSafeArenaStats* info_ = nullptr;
134};
135
136using ThreadSafeArenazSampler =
137 ::absl::profiling_internal::SampleRecorder<ThreadSafeArenaStats>;
138
139extern PROTOBUF_THREAD_LOCAL int64_t global_next_sample;
140
141// Returns an RAII sampling handle that manages registration and unregistation
142// with the global sampler.
143inline ThreadSafeArenaStatsHandle Sample() {
144 if (PROTOBUF_PREDICT_TRUE(--global_next_sample > 0)) {
145 return ThreadSafeArenaStatsHandle(nullptr);
146 }
147 return ThreadSafeArenaStatsHandle(SampleSlow(&global_next_sample));
148}
149
150#else
151struct ThreadSafeArenaStats {
152 static void RecordAllocateStats(ThreadSafeArenaStats*, size_t /*requested*/,
153 size_t /*allocated*/, size_t /*wasted*/) {}
154};
155
156ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
157void UnsampleSlow(ThreadSafeArenaStats* info);
158
159class ThreadSafeArenaStatsHandle {
160 public:
161 explicit ThreadSafeArenaStatsHandle() = default;
162 explicit ThreadSafeArenaStatsHandle(ThreadSafeArenaStats*) {}
163
164 void RecordReset() {}
165
166 ThreadSafeArenaStats* MutableStats() { return nullptr; }
167
168 friend void swap(ThreadSafeArenaStatsHandle&, ThreadSafeArenaStatsHandle&) {}
169
170 private:
171 friend class ThreadSafeArenaStatsHandlePeer;
172};
173
174class ThreadSafeArenazSampler {
175 public:
176 void Unregister(ThreadSafeArenaStats*) {}
177 void SetMaxSamples(int32_t) {}
178};
179
180// Returns an RAII sampling handle that manages registration and unregistation
181// with the global sampler.
182inline ThreadSafeArenaStatsHandle Sample() {
183 return ThreadSafeArenaStatsHandle(nullptr);
184}
185#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
186
187// Returns a global Sampler.
188ThreadSafeArenazSampler& GlobalThreadSafeArenazSampler();
189
190// Enables or disables sampling for thread safe arenas.
191void SetThreadSafeArenazEnabled(bool enabled);
192
193// Sets the rate at which thread safe arena will be sampled.
194void SetThreadSafeArenazSampleParameter(int32_t rate);
195
196// Sets a soft max for the number of samples that will be kept.
197void SetThreadSafeArenazMaxSamples(int32_t max);
198
199// Sets the current value for when arenas should be next sampled.
200void SetThreadSafeArenazGlobalNextSample(int64_t next_sample);
201
202} // namespace internal
203} // namespace protobuf
204} // namespace google
205
206#include <google/protobuf/port_undef.inc>
207#endif // GOOGLE_PROTOBUF_SRC_PROTOBUF_ARENAZ_SAMPLER_H__
208