• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "absl/strings/string_view.h"
15 #include "rtc_base/string_utils.h"
16 #include "rtc_base/synchronization/mutex.h"
17 #include "rtc_base/thread_annotations.h"
18 
19 // Default implementation of histogram methods for WebRTC clients that do not
20 // want to provide their own implementation.
21 
22 namespace webrtc {
23 namespace metrics {
24 class Histogram;
25 
26 namespace {
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.
30 const int kMaxSampleMapSize = 300;
31 
32 class RtcHistogram {
33  public:
RtcHistogram(absl::string_view name,int min,int max,int bucket_count)34   RtcHistogram(absl::string_view name, int min, int max, int bucket_count)
35       : min_(min), max_(max), info_(name, min, max, bucket_count) {
36     RTC_DCHECK_GT(bucket_count, 0);
37   }
38 
39   RtcHistogram(const RtcHistogram&) = delete;
40   RtcHistogram& operator=(const RtcHistogram&) = delete;
41 
Add(int sample)42   void Add(int sample) {
43     sample = std::min(sample, max_);
44     sample = std::max(sample, min_ - 1);  // Underflow bucket.
45 
46     MutexLock lock(&mutex_);
47     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.
GetAndReset()55   std::unique_ptr<SampleInfo> GetAndReset() {
56     MutexLock lock(&mutex_);
57     if (info_.samples.empty())
58       return nullptr;
59 
60     SampleInfo* copy =
61         new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
62 
63     std::swap(info_.samples, copy->samples);
64 
65     return std::unique_ptr<SampleInfo>(copy);
66   }
67 
name() const68   const std::string& name() const { return info_.name; }
69 
70   // Functions only for testing.
Reset()71   void Reset() {
72     MutexLock lock(&mutex_);
73     info_.samples.clear();
74   }
75 
NumEvents(int sample) const76   int NumEvents(int sample) const {
77     MutexLock lock(&mutex_);
78     const auto it = info_.samples.find(sample);
79     return (it == info_.samples.end()) ? 0 : it->second;
80   }
81 
NumSamples() const82   int NumSamples() const {
83     int num_samples = 0;
84     MutexLock lock(&mutex_);
85     for (const auto& sample : info_.samples) {
86       num_samples += sample.second;
87     }
88     return num_samples;
89   }
90 
MinSample() const91   int MinSample() const {
92     MutexLock lock(&mutex_);
93     return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
94   }
95 
Samples() const96   std::map<int, int> Samples() const {
97     MutexLock lock(&mutex_);
98     return info_.samples;
99   }
100 
101  private:
102   mutable Mutex mutex_;
103   const int min_;
104   const int max_;
105   SampleInfo info_ RTC_GUARDED_BY(mutex_);
106 };
107 
108 class RtcHistogramMap {
109  public:
RtcHistogramMap()110   RtcHistogramMap() {}
~RtcHistogramMap()111   ~RtcHistogramMap() {}
112 
113   RtcHistogramMap(const RtcHistogramMap&) = delete;
114   RtcHistogramMap& operator=(const RtcHistogramMap&) = delete;
115 
GetCountsHistogram(absl::string_view name,int min,int max,int bucket_count)116   Histogram* GetCountsHistogram(absl::string_view name,
117                                 int min,
118                                 int max,
119                                 int bucket_count) {
120     MutexLock lock(&mutex_);
121     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);
126     map_.emplace(name, hist);
127     return reinterpret_cast<Histogram*>(hist);
128   }
129 
GetEnumerationHistogram(absl::string_view name,int boundary)130   Histogram* GetEnumerationHistogram(absl::string_view name, int boundary) {
131     MutexLock lock(&mutex_);
132     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);
137     map_.emplace(name, hist);
138     return reinterpret_cast<Histogram*>(hist);
139   }
140 
GetAndReset(std::map<std::string,std::unique_ptr<SampleInfo>,rtc::AbslStringViewCmp> * histograms)141   void GetAndReset(std::map<std::string,
142                             std::unique_ptr<SampleInfo>,
143                             rtc::AbslStringViewCmp>* histograms) {
144     MutexLock lock(&mutex_);
145     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.
Reset()153   void Reset() {
154     MutexLock lock(&mutex_);
155     for (const auto& kv : map_)
156       kv.second->Reset();
157   }
158 
NumEvents(absl::string_view name,int sample) const159   int NumEvents(absl::string_view name, int sample) const {
160     MutexLock lock(&mutex_);
161     const auto& it = map_.find(name);
162     return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
163   }
164 
NumSamples(absl::string_view name) const165   int NumSamples(absl::string_view name) const {
166     MutexLock lock(&mutex_);
167     const auto& it = map_.find(name);
168     return (it == map_.end()) ? 0 : it->second->NumSamples();
169   }
170 
MinSample(absl::string_view name) const171   int MinSample(absl::string_view name) const {
172     MutexLock lock(&mutex_);
173     const auto& it = map_.find(name);
174     return (it == map_.end()) ? -1 : it->second->MinSample();
175   }
176 
Samples(absl::string_view name) const177   std::map<int, int> Samples(absl::string_view name) const {
178     MutexLock lock(&mutex_);
179     const auto& it = map_.find(name);
180     return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
181   }
182 
183  private:
184   mutable Mutex mutex_;
185   std::map<std::string, std::unique_ptr<RtcHistogram>, rtc::AbslStringViewCmp>
186       map_ RTC_GUARDED_BY(mutex_);
187 };
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).
193 static std::atomic<RtcHistogramMap*> g_rtc_histogram_map(nullptr);
194 
CreateMap()195 void CreateMap() {
196   RtcHistogramMap* map = g_rtc_histogram_map.load(std::memory_order_acquire);
197   if (map == nullptr) {
198     RtcHistogramMap* new_map = new RtcHistogramMap();
199     if (!g_rtc_histogram_map.compare_exchange_strong(map, new_map))
200       delete new_map;
201   }
202 }
203 
204 // Set the first time we start using histograms. Used to make sure Enable() is
205 // not called thereafter.
206 #if RTC_DCHECK_IS_ON
207 static std::atomic<int> g_rtc_histogram_called(0);
208 #endif
209 
210 // Gets the map (or nullptr).
GetMap()211 RtcHistogramMap* GetMap() {
212 #if RTC_DCHECK_IS_ON
213   g_rtc_histogram_called.store(1, std::memory_order_release);
214 #endif
215   return g_rtc_histogram_map.load();
216 }
217 }  // namespace
218 
219 #ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
220 // Implementation of histogram methods in
221 // webrtc/system_wrappers/interface/metrics.h.
222 
223 // Histogram with exponentially spaced buckets.
224 // Creates (or finds) histogram.
225 // The returned histogram pointer is cached (and used for adding samples in
226 // subsequent calls).
HistogramFactoryGetCounts(absl::string_view name,int min,int max,int bucket_count)227 Histogram* HistogramFactoryGetCounts(absl::string_view name,
228                                      int min,
229                                      int max,
230                                      int bucket_count) {
231   // TODO(asapersson): Alternative implementation will be needed if this
232   // histogram type should be truly exponential.
233   return HistogramFactoryGetCountsLinear(name, min, max, bucket_count);
234 }
235 
236 // Histogram with linearly spaced buckets.
237 // Creates (or finds) histogram.
238 // The returned histogram pointer is cached (and used for adding samples in
239 // subsequent calls).
HistogramFactoryGetCountsLinear(absl::string_view name,int min,int max,int bucket_count)240 Histogram* HistogramFactoryGetCountsLinear(absl::string_view name,
241                                            int min,
242                                            int max,
243                                            int bucket_count) {
244   RtcHistogramMap* map = GetMap();
245   if (!map)
246     return nullptr;
247 
248   return map->GetCountsHistogram(name, min, max, bucket_count);
249 }
250 
251 // Histogram with linearly spaced buckets.
252 // Creates (or finds) histogram.
253 // The returned histogram pointer is cached (and used for adding samples in
254 // subsequent calls).
HistogramFactoryGetEnumeration(absl::string_view name,int boundary)255 Histogram* HistogramFactoryGetEnumeration(absl::string_view name,
256                                           int boundary) {
257   RtcHistogramMap* map = GetMap();
258   if (!map)
259     return nullptr;
260 
261   return map->GetEnumerationHistogram(name, boundary);
262 }
263 
264 // Our default implementation reuses the non-sparse histogram.
SparseHistogramFactoryGetEnumeration(absl::string_view name,int boundary)265 Histogram* SparseHistogramFactoryGetEnumeration(absl::string_view name,
266                                                 int boundary) {
267   return HistogramFactoryGetEnumeration(name, boundary);
268 }
269 
270 // Fast path. Adds `sample` to cached `histogram_pointer`.
HistogramAdd(Histogram * histogram_pointer,int sample)271 void HistogramAdd(Histogram* histogram_pointer, int sample) {
272   RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
273   ptr->Add(sample);
274 }
275 
276 #endif  // WEBRTC_EXCLUDE_METRICS_DEFAULT
277 
SampleInfo(absl::string_view name,int min,int max,size_t bucket_count)278 SampleInfo::SampleInfo(absl::string_view name,
279                        int min,
280                        int max,
281                        size_t bucket_count)
282     : name(name), min(min), max(max), bucket_count(bucket_count) {}
283 
~SampleInfo()284 SampleInfo::~SampleInfo() {}
285 
286 // Implementation of global functions in metrics.h.
Enable()287 void Enable() {
288   RTC_DCHECK(g_rtc_histogram_map.load() == nullptr);
289 #if RTC_DCHECK_IS_ON
290   RTC_DCHECK_EQ(0, g_rtc_histogram_called.load(std::memory_order_acquire));
291 #endif
292   CreateMap();
293 }
294 
GetAndReset(std::map<std::string,std::unique_ptr<SampleInfo>,rtc::AbslStringViewCmp> * histograms)295 void GetAndReset(
296     std::map<std::string, std::unique_ptr<SampleInfo>, rtc::AbslStringViewCmp>*
297         histograms) {
298   histograms->clear();
299   RtcHistogramMap* map = GetMap();
300   if (map)
301     map->GetAndReset(histograms);
302 }
303 
Reset()304 void Reset() {
305   RtcHistogramMap* map = GetMap();
306   if (map)
307     map->Reset();
308 }
309 
NumEvents(absl::string_view name,int sample)310 int NumEvents(absl::string_view name, int sample) {
311   RtcHistogramMap* map = GetMap();
312   return map ? map->NumEvents(name, sample) : 0;
313 }
314 
NumSamples(absl::string_view name)315 int NumSamples(absl::string_view name) {
316   RtcHistogramMap* map = GetMap();
317   return map ? map->NumSamples(name) : 0;
318 }
319 
MinSample(absl::string_view name)320 int MinSample(absl::string_view name) {
321   RtcHistogramMap* map = GetMap();
322   return map ? map->MinSample(name) : -1;
323 }
324 
Samples(absl::string_view name)325 std::map<int, int> Samples(absl::string_view name) {
326   RtcHistogramMap* map = GetMap();
327   return map ? map->Samples(name) : std::map<int, int>();
328 }
329 
330 }  // namespace metrics
331 }  // namespace webrtc
332