blob: b14eef461840e2cf2e5b04c19da458dea6392dea [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/constructor_magic.h"
15#include "rtc_base/synchronization/mutex.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/thread_annotations.h"
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000017
18// Default implementation of histogram methods for WebRTC clients that do not
19// want to provide their own implementation.
20
21namespace webrtc {
22namespace metrics {
asapersson01d70a32016-05-20 06:29:46 -070023class Histogram;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000024
asapersson01d70a32016-05-20 06:29:46 -070025namespace {
26// Limit for the maximum number of sample values that can be stored.
27// TODO(asapersson): Consider using bucket count (and set up
28// linearly/exponentially spaced buckets) if samples are logged more frequently.
asapersson7d569972016-05-24 06:03:38 -070029const int kMaxSampleMapSize = 300;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +000030
asapersson01d70a32016-05-20 06:29:46 -070031class RtcHistogram {
32 public:
33 RtcHistogram(const std::string& name, int min, int max, int bucket_count)
34 : min_(min), max_(max), info_(name, min, max, bucket_count) {
35 RTC_DCHECK_GT(bucket_count, 0);
36 }
37
38 void Add(int sample) {
asapersson1731c9c2016-11-30 00:29:09 -080039 sample = std::min(sample, max_);
40 sample = std::max(sample, min_ - 1); // Underflow bucket.
asapersson01d70a32016-05-20 06:29:46 -070041
Markus Handell85585f42020-07-08 23:04:37 +020042 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070043 if (info_.samples.size() == kMaxSampleMapSize &&
44 info_.samples.find(sample) == info_.samples.end()) {
45 return;
46 }
47 ++info_.samples[sample];
48 }
49
50 // Returns a copy (or nullptr if there are no samples) and clears samples.
51 std::unique_ptr<SampleInfo> GetAndReset() {
Markus Handell85585f42020-07-08 23:04:37 +020052 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070053 if (info_.samples.empty())
54 return nullptr;
55
56 SampleInfo* copy =
57 new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
asapersson1731c9c2016-11-30 00:29:09 -080058
59 std::swap(info_.samples, copy->samples);
60
asapersson01d70a32016-05-20 06:29:46 -070061 return std::unique_ptr<SampleInfo>(copy);
62 }
63
64 const std::string& name() const { return info_.name; }
65
66 // Functions only for testing.
67 void Reset() {
Markus Handell85585f42020-07-08 23:04:37 +020068 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070069 info_.samples.clear();
70 }
71
72 int NumEvents(int sample) const {
Markus Handell85585f42020-07-08 23:04:37 +020073 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070074 const auto it = info_.samples.find(sample);
75 return (it == info_.samples.end()) ? 0 : it->second;
76 }
77
78 int NumSamples() const {
79 int num_samples = 0;
Markus Handell85585f42020-07-08 23:04:37 +020080 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070081 for (const auto& sample : info_.samples) {
82 num_samples += sample.second;
83 }
84 return num_samples;
85 }
86
87 int MinSample() const {
Markus Handell85585f42020-07-08 23:04:37 +020088 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -070089 return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
90 }
91
Steve Antonc1e6e862019-03-04 14:43:44 -080092 std::map<int, int> Samples() const {
Markus Handell85585f42020-07-08 23:04:37 +020093 MutexLock lock(&mutex_);
Steve Antonc1e6e862019-03-04 14:43:44 -080094 return info_.samples;
95 }
96
asapersson01d70a32016-05-20 06:29:46 -070097 private:
Markus Handell85585f42020-07-08 23:04:37 +020098 mutable Mutex mutex_;
asapersson01d70a32016-05-20 06:29:46 -070099 const int min_;
100 const int max_;
Markus Handell85585f42020-07-08 23:04:37 +0200101 SampleInfo info_ RTC_GUARDED_BY(mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700102
103 RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram);
104};
105
106class RtcHistogramMap {
107 public:
108 RtcHistogramMap() {}
109 ~RtcHistogramMap() {}
110
111 Histogram* GetCountsHistogram(const std::string& name,
112 int min,
113 int max,
114 int bucket_count) {
Markus Handell85585f42020-07-08 23:04:37 +0200115 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700116 const auto& it = map_.find(name);
117 if (it != map_.end())
118 return reinterpret_cast<Histogram*>(it->second.get());
119
120 RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count);
121 map_[name].reset(hist);
122 return reinterpret_cast<Histogram*>(hist);
123 }
124
125 Histogram* GetEnumerationHistogram(const std::string& name, int boundary) {
Markus Handell85585f42020-07-08 23:04:37 +0200126 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700127 const auto& it = map_.find(name);
128 if (it != map_.end())
129 return reinterpret_cast<Histogram*>(it->second.get());
130
131 RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1);
132 map_[name].reset(hist);
133 return reinterpret_cast<Histogram*>(hist);
134 }
135
136 void GetAndReset(
137 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
Markus Handell85585f42020-07-08 23:04:37 +0200138 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700139 for (const auto& kv : map_) {
140 std::unique_ptr<SampleInfo> info = kv.second->GetAndReset();
141 if (info)
142 histograms->insert(std::make_pair(kv.first, std::move(info)));
143 }
144 }
145
146 // Functions only for testing.
147 void Reset() {
Markus Handell85585f42020-07-08 23:04:37 +0200148 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700149 for (const auto& kv : map_)
150 kv.second->Reset();
151 }
152
153 int NumEvents(const std::string& name, int sample) const {
Markus Handell85585f42020-07-08 23:04:37 +0200154 MutexLock lock(&mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700155 const auto& it = map_.find(name);
156 return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
157 }
158
159 int NumSamples(const std::string& name) 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->NumSamples();
163 }
164
165 int MinSample(const std::string& 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()) ? -1 : it->second->MinSample();
169 }
170
Steve Antonc1e6e862019-03-04 14:43:44 -0800171 std::map<int, int> Samples(const std::string& name) const {
Markus Handell85585f42020-07-08 23:04:37 +0200172 MutexLock lock(&mutex_);
Steve Antonc1e6e862019-03-04 14:43:44 -0800173 const auto& it = map_.find(name);
174 return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
175 }
176
asapersson01d70a32016-05-20 06:29:46 -0700177 private:
Markus Handell85585f42020-07-08 23:04:37 +0200178 mutable Mutex mutex_;
danilchapa37de392017-09-09 04:17:22 -0700179 std::map<std::string, std::unique_ptr<RtcHistogram>> map_
Markus Handell85585f42020-07-08 23:04:37 +0200180 RTC_GUARDED_BY(mutex_);
asapersson01d70a32016-05-20 06:29:46 -0700181
182 RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogramMap);
183};
184
185// RtcHistogramMap is allocated upon call to Enable().
186// The histogram getter functions, which return pointer values to the histograms
187// in the map, are cached in WebRTC. Therefore, this memory is not freed by the
188// application (the memory will be reclaimed by the OS).
189static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr;
190
191void CreateMap() {
192 RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map);
193 if (map == nullptr) {
194 RtcHistogramMap* new_map = new RtcHistogramMap();
195 RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr(
196 &g_rtc_histogram_map, static_cast<RtcHistogramMap*>(nullptr), new_map);
197 if (old_map != nullptr)
198 delete new_map;
199 }
200}
201
202// Set the first time we start using histograms. Used to make sure Enable() is
203// not called thereafter.
204#if RTC_DCHECK_IS_ON
205static volatile int g_rtc_histogram_called = 0;
206#endif
207
208// Gets the map (or nullptr).
209RtcHistogramMap* GetMap() {
210#if RTC_DCHECK_IS_ON
211 rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1);
212#endif
213 return g_rtc_histogram_map;
214}
215} // namespace
216
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200217#ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
asapersson01d70a32016-05-20 06:29:46 -0700218// Implementation of histogram methods in
219// webrtc/system_wrappers/interface/metrics.h.
220
221// Histogram with exponentially spaced buckets.
222// Creates (or finds) histogram.
223// The returned histogram pointer is cached (and used for adding samples in
224// subsequent calls).
225Histogram* HistogramFactoryGetCounts(const std::string& name,
226 int min,
227 int max,
228 int bucket_count) {
henrik.lundinf29e05d2016-12-01 09:58:45 -0800229 // TODO(asapersson): Alternative implementation will be needed if this
230 // histogram type should be truly exponential.
231 return HistogramFactoryGetCountsLinear(name, min, max, bucket_count);
232}
233
234// Histogram with linearly spaced buckets.
235// Creates (or finds) histogram.
236// The returned histogram pointer is cached (and used for adding samples in
237// subsequent calls).
238Histogram* HistogramFactoryGetCountsLinear(const std::string& name,
239 int min,
240 int max,
241 int bucket_count) {
asapersson01d70a32016-05-20 06:29:46 -0700242 RtcHistogramMap* map = GetMap();
243 if (!map)
244 return nullptr;
245
246 return map->GetCountsHistogram(name, min, max, bucket_count);
247}
248
249// Histogram with linearly spaced buckets.
250// Creates (or finds) histogram.
251// The returned histogram pointer is cached (and used for adding samples in
252// subsequent calls).
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000253Histogram* HistogramFactoryGetEnumeration(const std::string& name,
asapersson01d70a32016-05-20 06:29:46 -0700254 int boundary) {
255 RtcHistogramMap* map = GetMap();
256 if (!map)
257 return nullptr;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000258
asapersson01d70a32016-05-20 06:29:46 -0700259 return map->GetEnumerationHistogram(name, boundary);
260}
261
Qingsi Wangd6eb71e2018-06-26 12:30:04 -0700262// Our default implementation reuses the non-sparse histogram.
263Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name,
264 int boundary) {
265 return HistogramFactoryGetEnumeration(name, boundary);
266}
267
Artem Titovf0671922021-07-27 12:40:17 +0200268// Fast path. Adds `sample` to cached `histogram_pointer`.
sakal2a5f3712016-09-09 00:11:48 -0700269void HistogramAdd(Histogram* histogram_pointer, int sample) {
sakal2a5f3712016-09-09 00:11:48 -0700270 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
271 ptr->Add(sample);
272}
273
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200274#endif // WEBRTC_EXCLUDE_METRICS_DEFAULT
275
asapersson01d70a32016-05-20 06:29:46 -0700276SampleInfo::SampleInfo(const std::string& name,
277 int min,
278 int max,
279 size_t bucket_count)
280 : name(name), min(min), max(max), bucket_count(bucket_count) {}
281
282SampleInfo::~SampleInfo() {}
283
Mirko Bonadei17f48782018-09-28 08:51:10 +0200284// Implementation of global functions in metrics.h.
asapersson01d70a32016-05-20 06:29:46 -0700285void Enable() {
286 RTC_DCHECK(g_rtc_histogram_map == nullptr);
287#if RTC_DCHECK_IS_ON
288 RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called));
289#endif
290 CreateMap();
291}
292
293void GetAndReset(
294 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
295 histograms->clear();
296 RtcHistogramMap* map = GetMap();
297 if (map)
298 map->GetAndReset(histograms);
299}
300
301void Reset() {
302 RtcHistogramMap* map = GetMap();
303 if (map)
304 map->Reset();
305}
306
307int NumEvents(const std::string& name, int sample) {
308 RtcHistogramMap* map = GetMap();
309 return map ? map->NumEvents(name, sample) : 0;
310}
311
312int NumSamples(const std::string& name) {
313 RtcHistogramMap* map = GetMap();
314 return map ? map->NumSamples(name) : 0;
315}
316
317int MinSample(const std::string& name) {
318 RtcHistogramMap* map = GetMap();
319 return map ? map->MinSample(name) : -1;
320}
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000321
Steve Antonc1e6e862019-03-04 14:43:44 -0800322std::map<int, int> Samples(const std::string& name) {
323 RtcHistogramMap* map = GetMap();
324 return map ? map->Samples(name) : std::map<int, int>();
325}
326
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000327} // namespace metrics
328} // namespace webrtc