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