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 "rtc_base/constructor_magic.h"
15 #include "rtc_base/synchronization/mutex.h"
16 #include "rtc_base/thread_annotations.h"
17
18 // Default implementation of histogram methods for WebRTC clients that do not
19 // want to provide their own implementation.
20
21 namespace webrtc {
22 namespace metrics {
23 class Histogram;
24
25 namespace {
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.
29 const int kMaxSampleMapSize = 300;
30
31 class RtcHistogram {
32 public:
RtcHistogram(const std::string & name,int min,int max,int bucket_count)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
Add(int sample)38 void Add(int sample) {
39 sample = std::min(sample, max_);
40 sample = std::max(sample, min_ - 1); // Underflow bucket.
41
42 MutexLock lock(&mutex_);
43 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.
GetAndReset()51 std::unique_ptr<SampleInfo> GetAndReset() {
52 MutexLock lock(&mutex_);
53 if (info_.samples.empty())
54 return nullptr;
55
56 SampleInfo* copy =
57 new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
58
59 std::swap(info_.samples, copy->samples);
60
61 return std::unique_ptr<SampleInfo>(copy);
62 }
63
name() const64 const std::string& name() const { return info_.name; }
65
66 // Functions only for testing.
Reset()67 void Reset() {
68 MutexLock lock(&mutex_);
69 info_.samples.clear();
70 }
71
NumEvents(int sample) const72 int NumEvents(int sample) const {
73 MutexLock lock(&mutex_);
74 const auto it = info_.samples.find(sample);
75 return (it == info_.samples.end()) ? 0 : it->second;
76 }
77
NumSamples() const78 int NumSamples() const {
79 int num_samples = 0;
80 MutexLock lock(&mutex_);
81 for (const auto& sample : info_.samples) {
82 num_samples += sample.second;
83 }
84 return num_samples;
85 }
86
MinSample() const87 int MinSample() const {
88 MutexLock lock(&mutex_);
89 return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
90 }
91
Samples() const92 std::map<int, int> Samples() const {
93 MutexLock lock(&mutex_);
94 return info_.samples;
95 }
96
97 private:
98 mutable Mutex mutex_;
99 const int min_;
100 const int max_;
101 SampleInfo info_ RTC_GUARDED_BY(mutex_);
102
103 RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram);
104 };
105
106 class RtcHistogramMap {
107 public:
RtcHistogramMap()108 RtcHistogramMap() {}
~RtcHistogramMap()109 ~RtcHistogramMap() {}
110
GetCountsHistogram(const std::string & name,int min,int max,int bucket_count)111 Histogram* GetCountsHistogram(const std::string& name,
112 int min,
113 int max,
114 int bucket_count) {
115 MutexLock lock(&mutex_);
116 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
GetEnumerationHistogram(const std::string & name,int boundary)125 Histogram* GetEnumerationHistogram(const std::string& name, int boundary) {
126 MutexLock lock(&mutex_);
127 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
GetAndReset(std::map<std::string,std::unique_ptr<SampleInfo>> * histograms)136 void GetAndReset(
137 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
138 MutexLock lock(&mutex_);
139 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.
Reset()147 void Reset() {
148 MutexLock lock(&mutex_);
149 for (const auto& kv : map_)
150 kv.second->Reset();
151 }
152
NumEvents(const std::string & name,int sample) const153 int NumEvents(const std::string& name, int sample) const {
154 MutexLock lock(&mutex_);
155 const auto& it = map_.find(name);
156 return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
157 }
158
NumSamples(const std::string & name) const159 int NumSamples(const std::string& name) const {
160 MutexLock lock(&mutex_);
161 const auto& it = map_.find(name);
162 return (it == map_.end()) ? 0 : it->second->NumSamples();
163 }
164
MinSample(const std::string & name) const165 int MinSample(const std::string& name) const {
166 MutexLock lock(&mutex_);
167 const auto& it = map_.find(name);
168 return (it == map_.end()) ? -1 : it->second->MinSample();
169 }
170
Samples(const std::string & name) const171 std::map<int, int> Samples(const std::string& name) const {
172 MutexLock lock(&mutex_);
173 const auto& it = map_.find(name);
174 return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
175 }
176
177 private:
178 mutable Mutex mutex_;
179 std::map<std::string, std::unique_ptr<RtcHistogram>> map_
180 RTC_GUARDED_BY(mutex_);
181
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).
189 static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr;
190
CreateMap()191 void 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
205 static volatile int g_rtc_histogram_called = 0;
206 #endif
207
208 // Gets the map (or nullptr).
GetMap()209 RtcHistogramMap* 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
217 #ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
218 // 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).
HistogramFactoryGetCounts(const std::string & name,int min,int max,int bucket_count)225 Histogram* HistogramFactoryGetCounts(const std::string& name,
226 int min,
227 int max,
228 int bucket_count) {
229 // 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).
HistogramFactoryGetCountsLinear(const std::string & name,int min,int max,int bucket_count)238 Histogram* HistogramFactoryGetCountsLinear(const std::string& name,
239 int min,
240 int max,
241 int bucket_count) {
242 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).
HistogramFactoryGetEnumeration(const std::string & name,int boundary)253 Histogram* HistogramFactoryGetEnumeration(const std::string& name,
254 int boundary) {
255 RtcHistogramMap* map = GetMap();
256 if (!map)
257 return nullptr;
258
259 return map->GetEnumerationHistogram(name, boundary);
260 }
261
262 // Our default implementation reuses the non-sparse histogram.
SparseHistogramFactoryGetEnumeration(const std::string & name,int boundary)263 Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name,
264 int boundary) {
265 return HistogramFactoryGetEnumeration(name, boundary);
266 }
267
268 // Fast path. Adds |sample| to cached |histogram_pointer|.
HistogramAdd(Histogram * histogram_pointer,int sample)269 void HistogramAdd(Histogram* histogram_pointer, int sample) {
270 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
271 ptr->Add(sample);
272 }
273
274 #endif // WEBRTC_EXCLUDE_METRICS_DEFAULT
275
SampleInfo(const std::string & name,int min,int max,size_t bucket_count)276 SampleInfo::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
~SampleInfo()282 SampleInfo::~SampleInfo() {}
283
284 // Implementation of global functions in metrics.h.
Enable()285 void 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
GetAndReset(std::map<std::string,std::unique_ptr<SampleInfo>> * histograms)293 void 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
Reset()301 void Reset() {
302 RtcHistogramMap* map = GetMap();
303 if (map)
304 map->Reset();
305 }
306
NumEvents(const std::string & name,int sample)307 int NumEvents(const std::string& name, int sample) {
308 RtcHistogramMap* map = GetMap();
309 return map ? map->NumEvents(name, sample) : 0;
310 }
311
NumSamples(const std::string & name)312 int NumSamples(const std::string& name) {
313 RtcHistogramMap* map = GetMap();
314 return map ? map->NumSamples(name) : 0;
315 }
316
MinSample(const std::string & name)317 int MinSample(const std::string& name) {
318 RtcHistogramMap* map = GetMap();
319 return map ? map->MinSample(name) : -1;
320 }
321
Samples(const std::string & name)322 std::map<int, int> Samples(const std::string& name) {
323 RtcHistogramMap* map = GetMap();
324 return map ? map->Samples(name) : std::map<int, int>();
325 }
326
327 } // namespace metrics
328 } // namespace webrtc
329