1 // Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 //
9
10 #include "system_wrappers/include/metrics.h"
11
12 #include <algorithm>
13
14 #include "absl/strings/string_view.h"
15 #include "rtc_base/string_utils.h"
16 #include "rtc_base/synchronization/mutex.h"
17 #include "rtc_base/thread_annotations.h"
18
19 // Default implementation of histogram methods for WebRTC clients that do not
20 // want to provide their own implementation.
21
22 namespace webrtc {
23 namespace metrics {
24 class Histogram;
25
26 namespace {
27 // Limit for the maximum number of sample values that can be stored.
28 // TODO(asapersson): Consider using bucket count (and set up
29 // linearly/exponentially spaced buckets) if samples are logged more frequently.
30 const int kMaxSampleMapSize = 300;
31
32 class RtcHistogram {
33 public:
RtcHistogram(absl::string_view name,int min,int max,int bucket_count)34 RtcHistogram(absl::string_view name, int min, int max, int bucket_count)
35 : min_(min), max_(max), info_(name, min, max, bucket_count) {
36 RTC_DCHECK_GT(bucket_count, 0);
37 }
38
39 RtcHistogram(const RtcHistogram&) = delete;
40 RtcHistogram& operator=(const RtcHistogram&) = delete;
41
Add(int sample)42 void Add(int sample) {
43 sample = std::min(sample, max_);
44 sample = std::max(sample, min_ - 1); // Underflow bucket.
45
46 MutexLock lock(&mutex_);
47 if (info_.samples.size() == kMaxSampleMapSize &&
48 info_.samples.find(sample) == info_.samples.end()) {
49 return;
50 }
51 ++info_.samples[sample];
52 }
53
54 // Returns a copy (or nullptr if there are no samples) and clears samples.
GetAndReset()55 std::unique_ptr<SampleInfo> GetAndReset() {
56 MutexLock lock(&mutex_);
57 if (info_.samples.empty())
58 return nullptr;
59
60 SampleInfo* copy =
61 new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
62
63 std::swap(info_.samples, copy->samples);
64
65 return std::unique_ptr<SampleInfo>(copy);
66 }
67
name() const68 const std::string& name() const { return info_.name; }
69
70 // Functions only for testing.
Reset()71 void Reset() {
72 MutexLock lock(&mutex_);
73 info_.samples.clear();
74 }
75
NumEvents(int sample) const76 int NumEvents(int sample) const {
77 MutexLock lock(&mutex_);
78 const auto it = info_.samples.find(sample);
79 return (it == info_.samples.end()) ? 0 : it->second;
80 }
81
NumSamples() const82 int NumSamples() const {
83 int num_samples = 0;
84 MutexLock lock(&mutex_);
85 for (const auto& sample : info_.samples) {
86 num_samples += sample.second;
87 }
88 return num_samples;
89 }
90
MinSample() const91 int MinSample() const {
92 MutexLock lock(&mutex_);
93 return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
94 }
95
Samples() const96 std::map<int, int> Samples() const {
97 MutexLock lock(&mutex_);
98 return info_.samples;
99 }
100
101 private:
102 mutable Mutex mutex_;
103 const int min_;
104 const int max_;
105 SampleInfo info_ RTC_GUARDED_BY(mutex_);
106 };
107
108 class RtcHistogramMap {
109 public:
RtcHistogramMap()110 RtcHistogramMap() {}
~RtcHistogramMap()111 ~RtcHistogramMap() {}
112
113 RtcHistogramMap(const RtcHistogramMap&) = delete;
114 RtcHistogramMap& operator=(const RtcHistogramMap&) = delete;
115
GetCountsHistogram(absl::string_view name,int min,int max,int bucket_count)116 Histogram* GetCountsHistogram(absl::string_view name,
117 int min,
118 int max,
119 int bucket_count) {
120 MutexLock lock(&mutex_);
121 const auto& it = map_.find(name);
122 if (it != map_.end())
123 return reinterpret_cast<Histogram*>(it->second.get());
124
125 RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count);
126 map_.emplace(name, hist);
127 return reinterpret_cast<Histogram*>(hist);
128 }
129
GetEnumerationHistogram(absl::string_view name,int boundary)130 Histogram* GetEnumerationHistogram(absl::string_view name, int boundary) {
131 MutexLock lock(&mutex_);
132 const auto& it = map_.find(name);
133 if (it != map_.end())
134 return reinterpret_cast<Histogram*>(it->second.get());
135
136 RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1);
137 map_.emplace(name, hist);
138 return reinterpret_cast<Histogram*>(hist);
139 }
140
GetAndReset(std::map<std::string,std::unique_ptr<SampleInfo>,rtc::AbslStringViewCmp> * histograms)141 void GetAndReset(std::map<std::string,
142 std::unique_ptr<SampleInfo>,
143 rtc::AbslStringViewCmp>* histograms) {
144 MutexLock lock(&mutex_);
145 for (const auto& kv : map_) {
146 std::unique_ptr<SampleInfo> info = kv.second->GetAndReset();
147 if (info)
148 histograms->insert(std::make_pair(kv.first, std::move(info)));
149 }
150 }
151
152 // Functions only for testing.
Reset()153 void Reset() {
154 MutexLock lock(&mutex_);
155 for (const auto& kv : map_)
156 kv.second->Reset();
157 }
158
NumEvents(absl::string_view name,int sample) const159 int NumEvents(absl::string_view name, int sample) const {
160 MutexLock lock(&mutex_);
161 const auto& it = map_.find(name);
162 return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
163 }
164
NumSamples(absl::string_view name) const165 int NumSamples(absl::string_view name) const {
166 MutexLock lock(&mutex_);
167 const auto& it = map_.find(name);
168 return (it == map_.end()) ? 0 : it->second->NumSamples();
169 }
170
MinSample(absl::string_view name) const171 int MinSample(absl::string_view name) const {
172 MutexLock lock(&mutex_);
173 const auto& it = map_.find(name);
174 return (it == map_.end()) ? -1 : it->second->MinSample();
175 }
176
Samples(absl::string_view name) const177 std::map<int, int> Samples(absl::string_view name) const {
178 MutexLock lock(&mutex_);
179 const auto& it = map_.find(name);
180 return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
181 }
182
183 private:
184 mutable Mutex mutex_;
185 std::map<std::string, std::unique_ptr<RtcHistogram>, rtc::AbslStringViewCmp>
186 map_ RTC_GUARDED_BY(mutex_);
187 };
188
189 // RtcHistogramMap is allocated upon call to Enable().
190 // The histogram getter functions, which return pointer values to the histograms
191 // in the map, are cached in WebRTC. Therefore, this memory is not freed by the
192 // application (the memory will be reclaimed by the OS).
193 static std::atomic<RtcHistogramMap*> g_rtc_histogram_map(nullptr);
194
CreateMap()195 void CreateMap() {
196 RtcHistogramMap* map = g_rtc_histogram_map.load(std::memory_order_acquire);
197 if (map == nullptr) {
198 RtcHistogramMap* new_map = new RtcHistogramMap();
199 if (!g_rtc_histogram_map.compare_exchange_strong(map, new_map))
200 delete new_map;
201 }
202 }
203
204 // Set the first time we start using histograms. Used to make sure Enable() is
205 // not called thereafter.
206 #if RTC_DCHECK_IS_ON
207 static std::atomic<int> g_rtc_histogram_called(0);
208 #endif
209
210 // Gets the map (or nullptr).
GetMap()211 RtcHistogramMap* GetMap() {
212 #if RTC_DCHECK_IS_ON
213 g_rtc_histogram_called.store(1, std::memory_order_release);
214 #endif
215 return g_rtc_histogram_map.load();
216 }
217 } // namespace
218
219 #ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
220 // Implementation of histogram methods in
221 // webrtc/system_wrappers/interface/metrics.h.
222
223 // Histogram with exponentially spaced buckets.
224 // Creates (or finds) histogram.
225 // The returned histogram pointer is cached (and used for adding samples in
226 // subsequent calls).
HistogramFactoryGetCounts(absl::string_view name,int min,int max,int bucket_count)227 Histogram* HistogramFactoryGetCounts(absl::string_view name,
228 int min,
229 int max,
230 int bucket_count) {
231 // TODO(asapersson): Alternative implementation will be needed if this
232 // histogram type should be truly exponential.
233 return HistogramFactoryGetCountsLinear(name, min, max, bucket_count);
234 }
235
236 // Histogram with linearly spaced buckets.
237 // Creates (or finds) histogram.
238 // The returned histogram pointer is cached (and used for adding samples in
239 // subsequent calls).
HistogramFactoryGetCountsLinear(absl::string_view name,int min,int max,int bucket_count)240 Histogram* HistogramFactoryGetCountsLinear(absl::string_view name,
241 int min,
242 int max,
243 int bucket_count) {
244 RtcHistogramMap* map = GetMap();
245 if (!map)
246 return nullptr;
247
248 return map->GetCountsHistogram(name, min, max, bucket_count);
249 }
250
251 // Histogram with linearly spaced buckets.
252 // Creates (or finds) histogram.
253 // The returned histogram pointer is cached (and used for adding samples in
254 // subsequent calls).
HistogramFactoryGetEnumeration(absl::string_view name,int boundary)255 Histogram* HistogramFactoryGetEnumeration(absl::string_view name,
256 int boundary) {
257 RtcHistogramMap* map = GetMap();
258 if (!map)
259 return nullptr;
260
261 return map->GetEnumerationHistogram(name, boundary);
262 }
263
264 // Our default implementation reuses the non-sparse histogram.
SparseHistogramFactoryGetEnumeration(absl::string_view name,int boundary)265 Histogram* SparseHistogramFactoryGetEnumeration(absl::string_view name,
266 int boundary) {
267 return HistogramFactoryGetEnumeration(name, boundary);
268 }
269
270 // Fast path. Adds `sample` to cached `histogram_pointer`.
HistogramAdd(Histogram * histogram_pointer,int sample)271 void HistogramAdd(Histogram* histogram_pointer, int sample) {
272 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
273 ptr->Add(sample);
274 }
275
276 #endif // WEBRTC_EXCLUDE_METRICS_DEFAULT
277
SampleInfo(absl::string_view name,int min,int max,size_t bucket_count)278 SampleInfo::SampleInfo(absl::string_view name,
279 int min,
280 int max,
281 size_t bucket_count)
282 : name(name), min(min), max(max), bucket_count(bucket_count) {}
283
~SampleInfo()284 SampleInfo::~SampleInfo() {}
285
286 // Implementation of global functions in metrics.h.
Enable()287 void Enable() {
288 RTC_DCHECK(g_rtc_histogram_map.load() == nullptr);
289 #if RTC_DCHECK_IS_ON
290 RTC_DCHECK_EQ(0, g_rtc_histogram_called.load(std::memory_order_acquire));
291 #endif
292 CreateMap();
293 }
294
GetAndReset(std::map<std::string,std::unique_ptr<SampleInfo>,rtc::AbslStringViewCmp> * histograms)295 void GetAndReset(
296 std::map<std::string, std::unique_ptr<SampleInfo>, rtc::AbslStringViewCmp>*
297 histograms) {
298 histograms->clear();
299 RtcHistogramMap* map = GetMap();
300 if (map)
301 map->GetAndReset(histograms);
302 }
303
Reset()304 void Reset() {
305 RtcHistogramMap* map = GetMap();
306 if (map)
307 map->Reset();
308 }
309
NumEvents(absl::string_view name,int sample)310 int NumEvents(absl::string_view name, int sample) {
311 RtcHistogramMap* map = GetMap();
312 return map ? map->NumEvents(name, sample) : 0;
313 }
314
NumSamples(absl::string_view name)315 int NumSamples(absl::string_view name) {
316 RtcHistogramMap* map = GetMap();
317 return map ? map->NumSamples(name) : 0;
318 }
319
MinSample(absl::string_view name)320 int MinSample(absl::string_view name) {
321 RtcHistogramMap* map = GetMap();
322 return map ? map->MinSample(name) : -1;
323 }
324
Samples(absl::string_view name)325 std::map<int, int> Samples(absl::string_view name) {
326 RtcHistogramMap* map = GetMap();
327 return map ? map->Samples(name) : std::map<int, int>();
328 }
329
330 } // namespace metrics
331 } // namespace webrtc
332