blob: 8c9cf0c5744b49f15007099b21048c789abcbbc2 [file] [log] [blame]
asapersson@webrtc.org580d3672014-10-23 12:57:56 +00001// 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
Mirko Bonadeic1c2a882018-09-06 13:34:51 +020010#include "system_wrappers/include/metrics.h"
asapersson01d70a32016-05-20 06:29:46 -070011
asapersson1731c9c2016-11-30 00:29:09 -080012#include <algorithm>
13
Markus Handell85585f42020-07-08 23:04:37 +020014#include "rtc_base/synchronization/mutex.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "rtc_base/thread_annotations.h"
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000016
17// Default implementation of histogram methods for WebRTC clients that do not
18// want to provide their own implementation.
19
20namespace webrtc {
21namespace metrics {
asapersson01d70a32016-05-20 06:29:46 -070022class Histogram;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000023
asapersson01d70a32016-05-20 06:29:46 -070024namespace {
25// Limit for the maximum number of sample values that can be stored.
26// TODO(asapersson): Consider using bucket count (and set up
27// linearly/exponentially spaced buckets) if samples are logged more frequently.
asapersson7d569972016-05-24 06:03:38 -070028const int kMaxSampleMapSize = 300;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000029
asapersson01d70a32016-05-20 06:29:46 -070030class RtcHistogram {
31 public:
32 RtcHistogram(const std::string& name, int min, int max, int bucket_count)
33 : min_(min), max_(max), info_(name, min, max, bucket_count) {
34 RTC_DCHECK_GT(bucket_count, 0);
35 }
36
Byoungchan Leec065e732022-01-18 09:35:48 +090037 RtcHistogram(const RtcHistogram&) = delete;
38 RtcHistogram& operator=(const RtcHistogram&) = delete;
39
asapersson01d70a32016-05-20 06:29:46 -070040 void Add(int sample) {
asapersson1731c9c2016-11-30 00:29:09 -080041 sample = std::min(sample, max_);
42 sample = std::max(sample, min_ - 1); // Underflow bucket.
asapersson01d70a32016-05-20 06:29:46 -070043
Markus Handell85585f42020-07-08 23:04:37 +020044 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070045 if (info_.samples.size() == kMaxSampleMapSize &&
46 info_.samples.find(sample) == info_.samples.end()) {
47 return;
48 }
49 ++info_.samples[sample];
50 }
51
52 // Returns a copy (or nullptr if there are no samples) and clears samples.
53 std::unique_ptr<SampleInfo> GetAndReset() {
Markus Handell85585f42020-07-08 23:04:37 +020054 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070055 if (info_.samples.empty())
56 return nullptr;
57
58 SampleInfo* copy =
59 new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
asapersson1731c9c2016-11-30 00:29:09 -080060
61 std::swap(info_.samples, copy->samples);
62
asapersson01d70a32016-05-20 06:29:46 -070063 return std::unique_ptr<SampleInfo>(copy);
64 }
65
66 const std::string& name() const { return info_.name; }
67
68 // Functions only for testing.
69 void Reset() {
Markus Handell85585f42020-07-08 23:04:37 +020070 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070071 info_.samples.clear();
72 }
73
74 int NumEvents(int sample) const {
Markus Handell85585f42020-07-08 23:04:37 +020075 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070076 const auto it = info_.samples.find(sample);
77 return (it == info_.samples.end()) ? 0 : it->second;
78 }
79
80 int NumSamples() const {
81 int num_samples = 0;
Markus Handell85585f42020-07-08 23:04:37 +020082 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070083 for (const auto& sample : info_.samples) {
84 num_samples += sample.second;
85 }
86 return num_samples;
87 }
88
89 int MinSample() const {
Markus Handell85585f42020-07-08 23:04:37 +020090 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070091 return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
92 }
93
Steve Antonc1e6e862019-03-04 14:43:44 -080094 std::map<int, int> Samples() const {
Markus Handell85585f42020-07-08 23:04:37 +020095 MutexLock lock(&mutex_);
Steve Antonc1e6e862019-03-04 14:43:44 -080096 return info_.samples;
97 }
98
asapersson01d70a32016-05-20 06:29:46 -070099 private:
Markus Handell85585f42020-07-08 23:04:37 +0200100 mutable Mutex mutex_;
asapersson01d70a32016-05-20 06:29:46 -0700101 const int min_;
102 const int max_;
Markus Handell85585f42020-07-08 23:04:37 +0200103 SampleInfo info_ RTC_GUARDED_BY(mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700104};
105
106class RtcHistogramMap {
107 public:
108 RtcHistogramMap() {}
109 ~RtcHistogramMap() {}
110
Byoungchan Leec065e732022-01-18 09:35:48 +0900111 RtcHistogramMap(const RtcHistogramMap&) = delete;
112 RtcHistogramMap& operator=(const RtcHistogramMap&) = delete;
113
asapersson01d70a32016-05-20 06:29:46 -0700114 Histogram* GetCountsHistogram(const std::string& name,
115 int min,
116 int max,
117 int bucket_count) {
Markus Handell85585f42020-07-08 23:04:37 +0200118 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700119 const auto& it = map_.find(name);
120 if (it != map_.end())
121 return reinterpret_cast<Histogram*>(it->second.get());
122
123 RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count);
124 map_[name].reset(hist);
125 return reinterpret_cast<Histogram*>(hist);
126 }
127
128 Histogram* GetEnumerationHistogram(const std::string& name, int boundary) {
Markus Handell85585f42020-07-08 23:04:37 +0200129 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700130 const auto& it = map_.find(name);
131 if (it != map_.end())
132 return reinterpret_cast<Histogram*>(it->second.get());
133
134 RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1);
135 map_[name].reset(hist);
136 return reinterpret_cast<Histogram*>(hist);
137 }
138
139 void GetAndReset(
140 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
Markus Handell85585f42020-07-08 23:04:37 +0200141 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700142 for (const auto& kv : map_) {
143 std::unique_ptr<SampleInfo> info = kv.second->GetAndReset();
144 if (info)
145 histograms->insert(std::make_pair(kv.first, std::move(info)));
146 }
147 }
148
149 // Functions only for testing.
150 void Reset() {
Markus Handell85585f42020-07-08 23:04:37 +0200151 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700152 for (const auto& kv : map_)
153 kv.second->Reset();
154 }
155
156 int NumEvents(const std::string& name, int sample) const {
Markus Handell85585f42020-07-08 23:04:37 +0200157 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700158 const auto& it = map_.find(name);
159 return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
160 }
161
162 int NumSamples(const std::string& name) const {
Markus Handell85585f42020-07-08 23:04:37 +0200163 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700164 const auto& it = map_.find(name);
165 return (it == map_.end()) ? 0 : it->second->NumSamples();
166 }
167
168 int MinSample(const std::string& name) const {
Markus Handell85585f42020-07-08 23:04:37 +0200169 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700170 const auto& it = map_.find(name);
171 return (it == map_.end()) ? -1 : it->second->MinSample();
172 }
173
Steve Antonc1e6e862019-03-04 14:43:44 -0800174 std::map<int, int> Samples(const std::string& name) const {
Markus Handell85585f42020-07-08 23:04:37 +0200175 MutexLock lock(&mutex_);
Steve Antonc1e6e862019-03-04 14:43:44 -0800176 const auto& it = map_.find(name);
177 return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
178 }
179
asapersson01d70a32016-05-20 06:29:46 -0700180 private:
Markus Handell85585f42020-07-08 23:04:37 +0200181 mutable Mutex mutex_;
danilchapa37de392017-09-09 04:17:22 -0700182 std::map<std::string, std::unique_ptr<RtcHistogram>> map_
Markus Handell85585f42020-07-08 23:04:37 +0200183 RTC_GUARDED_BY(mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700184};
185
186// RtcHistogramMap is allocated upon call to Enable().
187// The histogram getter functions, which return pointer values to the histograms
188// in the map, are cached in WebRTC. Therefore, this memory is not freed by the
189// application (the memory will be reclaimed by the OS).
190static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr;
191
192void CreateMap() {
193 RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map);
194 if (map == nullptr) {
195 RtcHistogramMap* new_map = new RtcHistogramMap();
196 RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr(
197 &g_rtc_histogram_map, static_cast<RtcHistogramMap*>(nullptr), new_map);
198 if (old_map != nullptr)
199 delete new_map;
200 }
201}
202
203// Set the first time we start using histograms. Used to make sure Enable() is
204// not called thereafter.
205#if RTC_DCHECK_IS_ON
206static volatile int g_rtc_histogram_called = 0;
207#endif
208
209// Gets the map (or nullptr).
210RtcHistogramMap* GetMap() {
211#if RTC_DCHECK_IS_ON
212 rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1);
213#endif
214 return g_rtc_histogram_map;
215}
216} // namespace
217
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200218#ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
asapersson01d70a32016-05-20 06:29:46 -0700219// Implementation of histogram methods in
220// webrtc/system_wrappers/interface/metrics.h.
221
222// Histogram with exponentially spaced buckets.
223// Creates (or finds) histogram.
224// The returned histogram pointer is cached (and used for adding samples in
225// subsequent calls).
226Histogram* HistogramFactoryGetCounts(const std::string& name,
227 int min,
228 int max,
229 int bucket_count) {
henrik.lundinf29e05d2016-12-01 09:58:45 -0800230 // TODO(asapersson): Alternative implementation will be needed if this
231 // histogram type should be truly exponential.
232 return HistogramFactoryGetCountsLinear(name, min, max, bucket_count);
233}
234
235// Histogram with linearly spaced buckets.
236// Creates (or finds) histogram.
237// The returned histogram pointer is cached (and used for adding samples in
238// subsequent calls).
239Histogram* HistogramFactoryGetCountsLinear(const std::string& name,
240 int min,
241 int max,
242 int bucket_count) {
asapersson01d70a32016-05-20 06:29:46 -0700243 RtcHistogramMap* map = GetMap();
244 if (!map)
245 return nullptr;
246
247 return map->GetCountsHistogram(name, min, max, bucket_count);
248}
249
250// Histogram with linearly spaced buckets.
251// Creates (or finds) histogram.
252// The returned histogram pointer is cached (and used for adding samples in
253// subsequent calls).
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000254Histogram* HistogramFactoryGetEnumeration(const std::string& name,
asapersson01d70a32016-05-20 06:29:46 -0700255 int boundary) {
256 RtcHistogramMap* map = GetMap();
257 if (!map)
258 return nullptr;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000259
asapersson01d70a32016-05-20 06:29:46 -0700260 return map->GetEnumerationHistogram(name, boundary);
261}
262
Qingsi Wangd6eb71e2018-06-26 12:30:04 -0700263// Our default implementation reuses the non-sparse histogram.
264Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name,
265 int boundary) {
266 return HistogramFactoryGetEnumeration(name, boundary);
267}
268
Artem Titovf0671922021-07-27 12:40:17 +0200269// Fast path. Adds `sample` to cached `histogram_pointer`.
sakal2a5f3712016-09-09 00:11:48 -0700270void HistogramAdd(Histogram* histogram_pointer, int sample) {
sakal2a5f3712016-09-09 00:11:48 -0700271 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
272 ptr->Add(sample);
273}
274
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200275#endif // WEBRTC_EXCLUDE_METRICS_DEFAULT
276
asapersson01d70a32016-05-20 06:29:46 -0700277SampleInfo::SampleInfo(const std::string& name,
278 int min,
279 int max,
280 size_t bucket_count)
281 : name(name), min(min), max(max), bucket_count(bucket_count) {}
282
283SampleInfo::~SampleInfo() {}
284
Mirko Bonadei17f48782018-09-28 08:51:10 +0200285// Implementation of global functions in metrics.h.
asapersson01d70a32016-05-20 06:29:46 -0700286void Enable() {
287 RTC_DCHECK(g_rtc_histogram_map == nullptr);
288#if RTC_DCHECK_IS_ON
289 RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called));
290#endif
291 CreateMap();
292}
293
294void GetAndReset(
295 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
296 histograms->clear();
297 RtcHistogramMap* map = GetMap();
298 if (map)
299 map->GetAndReset(histograms);
300}
301
302void Reset() {
303 RtcHistogramMap* map = GetMap();
304 if (map)
305 map->Reset();
306}
307
308int NumEvents(const std::string& name, int sample) {
309 RtcHistogramMap* map = GetMap();
310 return map ? map->NumEvents(name, sample) : 0;
311}
312
313int NumSamples(const std::string& name) {
314 RtcHistogramMap* map = GetMap();
315 return map ? map->NumSamples(name) : 0;
316}
317
318int MinSample(const std::string& name) {
319 RtcHistogramMap* map = GetMap();
320 return map ? map->MinSample(name) : -1;
321}
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000322
Steve Antonc1e6e862019-03-04 14:43:44 -0800323std::map<int, int> Samples(const std::string& name) {
324 RtcHistogramMap* map = GetMap();
325 return map ? map->Samples(name) : std::map<int, int>();
326}
327
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000328} // namespace metrics
329} // namespace webrtc