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
42 namespace google {
43 namespace protobuf {
44 namespace internal {
45
46 #if defined(PROTOBUF_ARENAZ_SAMPLE)
47 struct ThreadSafeArenaStats;
48 void RecordResetSlow(ThreadSafeArenaStats* info);
49 void 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`.
54 struct 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];
RecordAllocateStatsThreadSafeArenaStats85 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
92 ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
93 void UnsampleSlow(ThreadSafeArenaStats* info);
94
95 class ThreadSafeArenaStatsHandle {
96 public:
97 explicit ThreadSafeArenaStatsHandle() = default;
ThreadSafeArenaStatsHandle(ThreadSafeArenaStats * info)98 explicit ThreadSafeArenaStatsHandle(ThreadSafeArenaStats* info)
99 : info_(info) {}
100
~ThreadSafeArenaStatsHandle()101 ~ThreadSafeArenaStatsHandle() {
102 if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
103 UnsampleSlow(info_);
104 }
105
ThreadSafeArenaStatsHandle(ThreadSafeArenaStatsHandle && other)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
RecordReset()118 void RecordReset() {
119 if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
120 RecordResetSlow(info_);
121 }
122
MutableStats()123 ThreadSafeArenaStats* MutableStats() { return info_; }
124
swap(ThreadSafeArenaStatsHandle & lhs,ThreadSafeArenaStatsHandle & rhs)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
136 using ThreadSafeArenazSampler =
137 ::absl::profiling_internal::SampleRecorder<ThreadSafeArenaStats>;
138
139 extern 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.
Sample()143 inline 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
151 struct ThreadSafeArenaStats {
152 static void RecordAllocateStats(ThreadSafeArenaStats*, size_t /*requested*/,
153 size_t /*allocated*/, size_t /*wasted*/) {}
154 };
155
156 ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
157 void UnsampleSlow(ThreadSafeArenaStats* info);
158
159 class 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
174 class 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.
182 inline ThreadSafeArenaStatsHandle Sample() {
183 return ThreadSafeArenaStatsHandle(nullptr);
184 }
185 #endif // defined(PROTOBUF_ARENAZ_SAMPLE)
186
187 // Returns a global Sampler.
188 ThreadSafeArenazSampler& GlobalThreadSafeArenazSampler();
189
190 // Enables or disables sampling for thread safe arenas.
191 void SetThreadSafeArenazEnabled(bool enabled);
192
193 // Sets the rate at which thread safe arena will be sampled.
194 void SetThreadSafeArenazSampleParameter(int32_t rate);
195
196 // Sets a soft max for the number of samples that will be kept.
197 void SetThreadSafeArenazMaxSamples(int32_t max);
198
199 // Sets the current value for when arenas should be next sampled.
200 void 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