blob: 00634057524bc0396a7fbe2fb96d889d0bfd9349 [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
Ali Tofigh969c1352022-05-13 10:26:58 +020014#include "absl/strings/string_view.h"
15#include "rtc_base/string_utils.h"
Markus Handell85585f42020-07-08 23:04:37 +020016#include "rtc_base/synchronization/mutex.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/thread_annotations.h"
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000018
19// Default implementation of histogram methods for WebRTC clients that do not
20// want to provide their own implementation.
21
22namespace webrtc {
23namespace metrics {
asapersson01d70a32016-05-20 06:29:46 -070024class Histogram;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000025
asapersson01d70a32016-05-20 06:29:46 -070026namespace {
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.
asapersson7d569972016-05-24 06:03:38 -070030const int kMaxSampleMapSize = 300;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000031
asapersson01d70a32016-05-20 06:29:46 -070032class RtcHistogram {
33 public:
Ali Tofigh969c1352022-05-13 10:26:58 +020034 RtcHistogram(absl::string_view name, int min, int max, int bucket_count)
asapersson01d70a32016-05-20 06:29:46 -070035 : min_(min), max_(max), info_(name, min, max, bucket_count) {
36 RTC_DCHECK_GT(bucket_count, 0);
37 }
38
Byoungchan Leec065e732022-01-18 09:35:48 +090039 RtcHistogram(const RtcHistogram&) = delete;
40 RtcHistogram& operator=(const RtcHistogram&) = delete;
41
asapersson01d70a32016-05-20 06:29:46 -070042 void Add(int sample) {
asapersson1731c9c2016-11-30 00:29:09 -080043 sample = std::min(sample, max_);
44 sample = std::max(sample, min_ - 1); // Underflow bucket.
asapersson01d70a32016-05-20 06:29:46 -070045
Markus Handell85585f42020-07-08 23:04:37 +020046 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070047 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.
55 std::unique_ptr<SampleInfo> GetAndReset() {
Markus Handell85585f42020-07-08 23:04:37 +020056 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070057 if (info_.samples.empty())
58 return nullptr;
59
60 SampleInfo* copy =
61 new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
asapersson1731c9c2016-11-30 00:29:09 -080062
63 std::swap(info_.samples, copy->samples);
64
asapersson01d70a32016-05-20 06:29:46 -070065 return std::unique_ptr<SampleInfo>(copy);
66 }
67
68 const std::string& name() const { return info_.name; }
69
70 // Functions only for testing.
71 void Reset() {
Markus Handell85585f42020-07-08 23:04:37 +020072 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070073 info_.samples.clear();
74 }
75
76 int NumEvents(int sample) const {
Markus Handell85585f42020-07-08 23:04:37 +020077 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070078 const auto it = info_.samples.find(sample);
79 return (it == info_.samples.end()) ? 0 : it->second;
80 }
81
82 int NumSamples() const {
83 int num_samples = 0;
Markus Handell85585f42020-07-08 23:04:37 +020084 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070085 for (const auto& sample : info_.samples) {
86 num_samples += sample.second;
87 }
88 return num_samples;
89 }
90
91 int MinSample() const {
Markus Handell85585f42020-07-08 23:04:37 +020092 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070093 return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
94 }
95
Steve Antonc1e6e862019-03-04 14:43:44 -080096 std::map<int, int> Samples() const {
Markus Handell85585f42020-07-08 23:04:37 +020097 MutexLock lock(&mutex_);
Steve Antonc1e6e862019-03-04 14:43:44 -080098 return info_.samples;
99 }
100
asapersson01d70a32016-05-20 06:29:46 -0700101 private:
Markus Handell85585f42020-07-08 23:04:37 +0200102 mutable Mutex mutex_;
asapersson01d70a32016-05-20 06:29:46 -0700103 const int min_;
104 const int max_;
Markus Handell85585f42020-07-08 23:04:37 +0200105 SampleInfo info_ RTC_GUARDED_BY(mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700106};
107
108class RtcHistogramMap {
109 public:
110 RtcHistogramMap() {}
111 ~RtcHistogramMap() {}
112
Byoungchan Leec065e732022-01-18 09:35:48 +0900113 RtcHistogramMap(const RtcHistogramMap&) = delete;
114 RtcHistogramMap& operator=(const RtcHistogramMap&) = delete;
115
Ali Tofigh969c1352022-05-13 10:26:58 +0200116 Histogram* GetCountsHistogram(absl::string_view name,
asapersson01d70a32016-05-20 06:29:46 -0700117 int min,
118 int max,
119 int bucket_count) {
Markus Handell85585f42020-07-08 23:04:37 +0200120 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700121 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);
Ali Tofigh969c1352022-05-13 10:26:58 +0200126 map_.emplace(name, hist);
asapersson01d70a32016-05-20 06:29:46 -0700127 return reinterpret_cast<Histogram*>(hist);
128 }
129
Ali Tofigh969c1352022-05-13 10:26:58 +0200130 Histogram* GetEnumerationHistogram(absl::string_view name, int boundary) {
Markus Handell85585f42020-07-08 23:04:37 +0200131 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700132 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);
Ali Tofigh969c1352022-05-13 10:26:58 +0200137 map_.emplace(name, hist);
asapersson01d70a32016-05-20 06:29:46 -0700138 return reinterpret_cast<Histogram*>(hist);
139 }
140
Ali Tofigh969c1352022-05-13 10:26:58 +0200141 void GetAndReset(std::map<std::string,
142 std::unique_ptr<SampleInfo>,
143 rtc::AbslStringViewCmp>* histograms) {
Markus Handell85585f42020-07-08 23:04:37 +0200144 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700145 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.
153 void Reset() {
Markus Handell85585f42020-07-08 23:04:37 +0200154 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700155 for (const auto& kv : map_)
156 kv.second->Reset();
157 }
158
Ali Tofigh969c1352022-05-13 10:26:58 +0200159 int NumEvents(absl::string_view name, int sample) const {
Markus Handell85585f42020-07-08 23:04:37 +0200160 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700161 const auto& it = map_.find(name);
162 return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
163 }
164
Ali Tofigh969c1352022-05-13 10:26:58 +0200165 int NumSamples(absl::string_view name) const {
Markus Handell85585f42020-07-08 23:04:37 +0200166 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700167 const auto& it = map_.find(name);
168 return (it == map_.end()) ? 0 : it->second->NumSamples();
169 }
170
Ali Tofigh969c1352022-05-13 10:26:58 +0200171 int MinSample(absl::string_view name) const {
Markus Handell85585f42020-07-08 23:04:37 +0200172 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700173 const auto& it = map_.find(name);
174 return (it == map_.end()) ? -1 : it->second->MinSample();
175 }
176
Ali Tofigh969c1352022-05-13 10:26:58 +0200177 std::map<int, int> Samples(absl::string_view name) const {
Markus Handell85585f42020-07-08 23:04:37 +0200178 MutexLock lock(&mutex_);
Steve Antonc1e6e862019-03-04 14:43:44 -0800179 const auto& it = map_.find(name);
180 return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
181 }
182
asapersson01d70a32016-05-20 06:29:46 -0700183 private:
Markus Handell85585f42020-07-08 23:04:37 +0200184 mutable Mutex mutex_;
Ali Tofigh969c1352022-05-13 10:26:58 +0200185 std::map<std::string, std::unique_ptr<RtcHistogram>, rtc::AbslStringViewCmp>
186 map_ RTC_GUARDED_BY(mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700187};
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).
193static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr;
194
195void CreateMap() {
196 RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map);
197 if (map == nullptr) {
198 RtcHistogramMap* new_map = new RtcHistogramMap();
199 RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr(
200 &g_rtc_histogram_map, static_cast<RtcHistogramMap*>(nullptr), new_map);
201 if (old_map != nullptr)
202 delete new_map;
203 }
204}
205
206// Set the first time we start using histograms. Used to make sure Enable() is
207// not called thereafter.
208#if RTC_DCHECK_IS_ON
209static volatile int g_rtc_histogram_called = 0;
210#endif
211
212// Gets the map (or nullptr).
213RtcHistogramMap* GetMap() {
214#if RTC_DCHECK_IS_ON
215 rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1);
216#endif
217 return g_rtc_histogram_map;
218}
219} // namespace
220
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200221#ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
asapersson01d70a32016-05-20 06:29:46 -0700222// Implementation of histogram methods in
223// webrtc/system_wrappers/interface/metrics.h.
224
225// Histogram with exponentially spaced buckets.
226// Creates (or finds) histogram.
227// The returned histogram pointer is cached (and used for adding samples in
228// subsequent calls).
Ali Tofigh969c1352022-05-13 10:26:58 +0200229Histogram* HistogramFactoryGetCounts(absl::string_view name,
asapersson01d70a32016-05-20 06:29:46 -0700230 int min,
231 int max,
232 int bucket_count) {
henrik.lundinf29e05d2016-12-01 09:58:45 -0800233 // TODO(asapersson): Alternative implementation will be needed if this
234 // histogram type should be truly exponential.
235 return HistogramFactoryGetCountsLinear(name, min, max, bucket_count);
236}
237
238// Histogram with linearly spaced buckets.
239// Creates (or finds) histogram.
240// The returned histogram pointer is cached (and used for adding samples in
241// subsequent calls).
Ali Tofigh969c1352022-05-13 10:26:58 +0200242Histogram* HistogramFactoryGetCountsLinear(absl::string_view name,
henrik.lundinf29e05d2016-12-01 09:58:45 -0800243 int min,
244 int max,
245 int bucket_count) {
asapersson01d70a32016-05-20 06:29:46 -0700246 RtcHistogramMap* map = GetMap();
247 if (!map)
248 return nullptr;
249
250 return map->GetCountsHistogram(name, min, max, bucket_count);
251}
252
253// Histogram with linearly spaced buckets.
254// Creates (or finds) histogram.
255// The returned histogram pointer is cached (and used for adding samples in
256// subsequent calls).
Ali Tofigh969c1352022-05-13 10:26:58 +0200257Histogram* HistogramFactoryGetEnumeration(absl::string_view name,
asapersson01d70a32016-05-20 06:29:46 -0700258 int boundary) {
259 RtcHistogramMap* map = GetMap();
260 if (!map)
261 return nullptr;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000262
asapersson01d70a32016-05-20 06:29:46 -0700263 return map->GetEnumerationHistogram(name, boundary);
264}
265
Qingsi Wangd6eb71e2018-06-26 12:30:04 -0700266// Our default implementation reuses the non-sparse histogram.
Ali Tofigh969c1352022-05-13 10:26:58 +0200267Histogram* SparseHistogramFactoryGetEnumeration(absl::string_view name,
Qingsi Wangd6eb71e2018-06-26 12:30:04 -0700268 int boundary) {
269 return HistogramFactoryGetEnumeration(name, boundary);
270}
271
Artem Titovf0671922021-07-27 12:40:17 +0200272// Fast path. Adds `sample` to cached `histogram_pointer`.
sakal2a5f3712016-09-09 00:11:48 -0700273void HistogramAdd(Histogram* histogram_pointer, int sample) {
sakal2a5f3712016-09-09 00:11:48 -0700274 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
275 ptr->Add(sample);
276}
277
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200278#endif // WEBRTC_EXCLUDE_METRICS_DEFAULT
279
Ali Tofigh969c1352022-05-13 10:26:58 +0200280SampleInfo::SampleInfo(absl::string_view name,
asapersson01d70a32016-05-20 06:29:46 -0700281 int min,
282 int max,
283 size_t bucket_count)
284 : name(name), min(min), max(max), bucket_count(bucket_count) {}
285
286SampleInfo::~SampleInfo() {}
287
Mirko Bonadei17f48782018-09-28 08:51:10 +0200288// Implementation of global functions in metrics.h.
asapersson01d70a32016-05-20 06:29:46 -0700289void Enable() {
290 RTC_DCHECK(g_rtc_histogram_map == nullptr);
291#if RTC_DCHECK_IS_ON
292 RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called));
293#endif
294 CreateMap();
295}
296
297void GetAndReset(
Ali Tofigh969c1352022-05-13 10:26:58 +0200298 std::map<std::string, std::unique_ptr<SampleInfo>, rtc::AbslStringViewCmp>*
299 histograms) {
asapersson01d70a32016-05-20 06:29:46 -0700300 histograms->clear();
301 RtcHistogramMap* map = GetMap();
302 if (map)
303 map->GetAndReset(histograms);
304}
305
306void Reset() {
307 RtcHistogramMap* map = GetMap();
308 if (map)
309 map->Reset();
310}
311
Ali Tofigh969c1352022-05-13 10:26:58 +0200312int NumEvents(absl::string_view name, int sample) {
asapersson01d70a32016-05-20 06:29:46 -0700313 RtcHistogramMap* map = GetMap();
314 return map ? map->NumEvents(name, sample) : 0;
315}
316
Ali Tofigh969c1352022-05-13 10:26:58 +0200317int NumSamples(absl::string_view name) {
asapersson01d70a32016-05-20 06:29:46 -0700318 RtcHistogramMap* map = GetMap();
319 return map ? map->NumSamples(name) : 0;
320}
321
Ali Tofigh969c1352022-05-13 10:26:58 +0200322int MinSample(absl::string_view name) {
asapersson01d70a32016-05-20 06:29:46 -0700323 RtcHistogramMap* map = GetMap();
324 return map ? map->MinSample(name) : -1;
325}
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000326
Ali Tofigh969c1352022-05-13 10:26:58 +0200327std::map<int, int> Samples(absl::string_view name) {
Steve Antonc1e6e862019-03-04 14:43:44 -0800328 RtcHistogramMap* map = GetMap();
329 return map ? map->Samples(name) : std::map<int, int>();
330}
331
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000332} // namespace metrics
333} // namespace webrtc