blob: 2383272887d13b5304e4461b609ea3ab372c403e [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
Steve Anton10542f22019-01-11 09:11:00 -080014#include "rtc_base/critical_section.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
37 void Add(int sample) {
asapersson1731c9c2016-11-30 00:29:09 -080038 sample = std::min(sample, max_);
39 sample = std::max(sample, min_ - 1); // Underflow bucket.
asapersson01d70a32016-05-20 06:29:46 -070040
41 rtc::CritScope cs(&crit_);
42 if (info_.samples.size() == kMaxSampleMapSize &&
43 info_.samples.find(sample) == info_.samples.end()) {
44 return;
45 }
46 ++info_.samples[sample];
47 }
48
49 // Returns a copy (or nullptr if there are no samples) and clears samples.
50 std::unique_ptr<SampleInfo> GetAndReset() {
51 rtc::CritScope cs(&crit_);
52 if (info_.samples.empty())
53 return nullptr;
54
55 SampleInfo* copy =
56 new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
asapersson1731c9c2016-11-30 00:29:09 -080057
58 std::swap(info_.samples, copy->samples);
59
asapersson01d70a32016-05-20 06:29:46 -070060 return std::unique_ptr<SampleInfo>(copy);
61 }
62
63 const std::string& name() const { return info_.name; }
64
65 // Functions only for testing.
66 void Reset() {
67 rtc::CritScope cs(&crit_);
68 info_.samples.clear();
69 }
70
71 int NumEvents(int sample) const {
72 rtc::CritScope cs(&crit_);
73 const auto it = info_.samples.find(sample);
74 return (it == info_.samples.end()) ? 0 : it->second;
75 }
76
77 int NumSamples() const {
78 int num_samples = 0;
79 rtc::CritScope cs(&crit_);
80 for (const auto& sample : info_.samples) {
81 num_samples += sample.second;
82 }
83 return num_samples;
84 }
85
86 int MinSample() const {
87 rtc::CritScope cs(&crit_);
88 return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
89 }
90
Steve Antonc1e6e862019-03-04 14:43:44 -080091 std::map<int, int> Samples() const {
92 rtc::CritScope cs(&crit_);
93 return info_.samples;
94 }
95
asapersson01d70a32016-05-20 06:29:46 -070096 private:
97 rtc::CriticalSection crit_;
98 const int min_;
99 const int max_;
danilchapa37de392017-09-09 04:17:22 -0700100 SampleInfo info_ RTC_GUARDED_BY(crit_);
asapersson01d70a32016-05-20 06:29:46 -0700101
102 RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram);
103};
104
105class RtcHistogramMap {
106 public:
107 RtcHistogramMap() {}
108 ~RtcHistogramMap() {}
109
110 Histogram* GetCountsHistogram(const std::string& name,
111 int min,
112 int max,
113 int bucket_count) {
114 rtc::CritScope cs(&crit_);
115 const auto& it = map_.find(name);
116 if (it != map_.end())
117 return reinterpret_cast<Histogram*>(it->second.get());
118
119 RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count);
120 map_[name].reset(hist);
121 return reinterpret_cast<Histogram*>(hist);
122 }
123
124 Histogram* GetEnumerationHistogram(const std::string& name, int boundary) {
125 rtc::CritScope cs(&crit_);
126 const auto& it = map_.find(name);
127 if (it != map_.end())
128 return reinterpret_cast<Histogram*>(it->second.get());
129
130 RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1);
131 map_[name].reset(hist);
132 return reinterpret_cast<Histogram*>(hist);
133 }
134
135 void GetAndReset(
136 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
137 rtc::CritScope cs(&crit_);
138 for (const auto& kv : map_) {
139 std::unique_ptr<SampleInfo> info = kv.second->GetAndReset();
140 if (info)
141 histograms->insert(std::make_pair(kv.first, std::move(info)));
142 }
143 }
144
145 // Functions only for testing.
146 void Reset() {
147 rtc::CritScope cs(&crit_);
148 for (const auto& kv : map_)
149 kv.second->Reset();
150 }
151
152 int NumEvents(const std::string& name, int sample) const {
153 rtc::CritScope cs(&crit_);
154 const auto& it = map_.find(name);
155 return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
156 }
157
158 int NumSamples(const std::string& name) const {
159 rtc::CritScope cs(&crit_);
160 const auto& it = map_.find(name);
161 return (it == map_.end()) ? 0 : it->second->NumSamples();
162 }
163
164 int MinSample(const std::string& name) const {
165 rtc::CritScope cs(&crit_);
166 const auto& it = map_.find(name);
167 return (it == map_.end()) ? -1 : it->second->MinSample();
168 }
169
Steve Antonc1e6e862019-03-04 14:43:44 -0800170 std::map<int, int> Samples(const std::string& name) const {
171 rtc::CritScope cs(&crit_);
172 const auto& it = map_.find(name);
173 return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
174 }
175
asapersson01d70a32016-05-20 06:29:46 -0700176 private:
177 rtc::CriticalSection crit_;
danilchapa37de392017-09-09 04:17:22 -0700178 std::map<std::string, std::unique_ptr<RtcHistogram>> map_
179 RTC_GUARDED_BY(crit_);
asapersson01d70a32016-05-20 06:29:46 -0700180
181 RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogramMap);
182};
183
184// RtcHistogramMap is allocated upon call to Enable().
185// The histogram getter functions, which return pointer values to the histograms
186// in the map, are cached in WebRTC. Therefore, this memory is not freed by the
187// application (the memory will be reclaimed by the OS).
188static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr;
189
190void CreateMap() {
191 RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map);
192 if (map == nullptr) {
193 RtcHistogramMap* new_map = new RtcHistogramMap();
194 RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr(
195 &g_rtc_histogram_map, static_cast<RtcHistogramMap*>(nullptr), new_map);
196 if (old_map != nullptr)
197 delete new_map;
198 }
199}
200
201// Set the first time we start using histograms. Used to make sure Enable() is
202// not called thereafter.
203#if RTC_DCHECK_IS_ON
204static volatile int g_rtc_histogram_called = 0;
205#endif
206
207// Gets the map (or nullptr).
208RtcHistogramMap* GetMap() {
209#if RTC_DCHECK_IS_ON
210 rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1);
211#endif
212 return g_rtc_histogram_map;
213}
214} // namespace
215
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200216#ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
asapersson01d70a32016-05-20 06:29:46 -0700217// Implementation of histogram methods in
218// webrtc/system_wrappers/interface/metrics.h.
219
220// Histogram with exponentially spaced buckets.
221// Creates (or finds) histogram.
222// The returned histogram pointer is cached (and used for adding samples in
223// subsequent calls).
224Histogram* HistogramFactoryGetCounts(const std::string& name,
225 int min,
226 int max,
227 int bucket_count) {
henrik.lundinf29e05d2016-12-01 09:58:45 -0800228 // TODO(asapersson): Alternative implementation will be needed if this
229 // histogram type should be truly exponential.
230 return HistogramFactoryGetCountsLinear(name, min, max, bucket_count);
231}
232
233// Histogram with linearly spaced buckets.
234// Creates (or finds) histogram.
235// The returned histogram pointer is cached (and used for adding samples in
236// subsequent calls).
237Histogram* HistogramFactoryGetCountsLinear(const std::string& name,
238 int min,
239 int max,
240 int bucket_count) {
asapersson01d70a32016-05-20 06:29:46 -0700241 RtcHistogramMap* map = GetMap();
242 if (!map)
243 return nullptr;
244
245 return map->GetCountsHistogram(name, min, max, bucket_count);
246}
247
248// Histogram with linearly spaced buckets.
249// Creates (or finds) histogram.
250// The returned histogram pointer is cached (and used for adding samples in
251// subsequent calls).
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000252Histogram* HistogramFactoryGetEnumeration(const std::string& name,
asapersson01d70a32016-05-20 06:29:46 -0700253 int boundary) {
254 RtcHistogramMap* map = GetMap();
255 if (!map)
256 return nullptr;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000257
asapersson01d70a32016-05-20 06:29:46 -0700258 return map->GetEnumerationHistogram(name, boundary);
259}
260
Qingsi Wangd6eb71e2018-06-26 12:30:04 -0700261// Our default implementation reuses the non-sparse histogram.
262Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name,
263 int boundary) {
264 return HistogramFactoryGetEnumeration(name, boundary);
265}
266
sakal2a5f3712016-09-09 00:11:48 -0700267// Fast path. Adds |sample| to cached |histogram_pointer|.
268void HistogramAdd(Histogram* histogram_pointer, int sample) {
sakal2a5f3712016-09-09 00:11:48 -0700269 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
270 ptr->Add(sample);
271}
272
Mirko Bonadeic1c2a882018-09-06 13:34:51 +0200273#endif // WEBRTC_EXCLUDE_METRICS_DEFAULT
274
asapersson01d70a32016-05-20 06:29:46 -0700275SampleInfo::SampleInfo(const std::string& name,
276 int min,
277 int max,
278 size_t bucket_count)
279 : name(name), min(min), max(max), bucket_count(bucket_count) {}
280
281SampleInfo::~SampleInfo() {}
282
Mirko Bonadei17f48782018-09-28 08:51:10 +0200283// Implementation of global functions in metrics.h.
asapersson01d70a32016-05-20 06:29:46 -0700284void Enable() {
285 RTC_DCHECK(g_rtc_histogram_map == nullptr);
286#if RTC_DCHECK_IS_ON
287 RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called));
288#endif
289 CreateMap();
290}
291
292void GetAndReset(
293 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
294 histograms->clear();
295 RtcHistogramMap* map = GetMap();
296 if (map)
297 map->GetAndReset(histograms);
298}
299
300void Reset() {
301 RtcHistogramMap* map = GetMap();
302 if (map)
303 map->Reset();
304}
305
306int NumEvents(const std::string& name, int sample) {
307 RtcHistogramMap* map = GetMap();
308 return map ? map->NumEvents(name, sample) : 0;
309}
310
311int NumSamples(const std::string& name) {
312 RtcHistogramMap* map = GetMap();
313 return map ? map->NumSamples(name) : 0;
314}
315
316int MinSample(const std::string& name) {
317 RtcHistogramMap* map = GetMap();
318 return map ? map->MinSample(name) : -1;
319}
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000320
Steve Antonc1e6e862019-03-04 14:43:44 -0800321std::map<int, int> Samples(const std::string& name) {
322 RtcHistogramMap* map = GetMap();
323 return map ? map->Samples(name) : std::map<int, int>();
324}
325
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000326} // namespace metrics
327} // namespace webrtc