blob: d7d24fa50dab0e9e33aa8f8aadd3ac32b0f63bd3 [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
asapersson01d70a32016-05-20 06:29:46 -070010#include "webrtc/system_wrappers/include/metrics_default.h"
11
asapersson1731c9c2016-11-30 00:29:09 -080012#include <algorithm>
13
asapersson01d70a32016-05-20 06:29:46 -070014#include "webrtc/base/criticalsection.h"
15#include "webrtc/base/thread_annotations.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010016#include "webrtc/system_wrappers/include/metrics.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
42 rtc::CritScope cs(&crit_);
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.
51 std::unique_ptr<SampleInfo> GetAndReset() {
52 rtc::CritScope cs(&crit_);
53 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() {
68 rtc::CritScope cs(&crit_);
69 info_.samples.clear();
70 }
71
72 int NumEvents(int sample) const {
73 rtc::CritScope cs(&crit_);
74 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;
80 rtc::CritScope cs(&crit_);
81 for (const auto& sample : info_.samples) {
82 num_samples += sample.second;
83 }
84 return num_samples;
85 }
86
87 int MinSample() const {
88 rtc::CritScope cs(&crit_);
89 return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
90 }
91
92 private:
93 rtc::CriticalSection crit_;
94 const int min_;
95 const int max_;
96 SampleInfo info_ GUARDED_BY(crit_);
97
98 RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram);
99};
100
101class RtcHistogramMap {
102 public:
103 RtcHistogramMap() {}
104 ~RtcHistogramMap() {}
105
106 Histogram* GetCountsHistogram(const std::string& name,
107 int min,
108 int max,
109 int bucket_count) {
110 rtc::CritScope cs(&crit_);
111 const auto& it = map_.find(name);
112 if (it != map_.end())
113 return reinterpret_cast<Histogram*>(it->second.get());
114
115 RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count);
116 map_[name].reset(hist);
117 return reinterpret_cast<Histogram*>(hist);
118 }
119
120 Histogram* GetEnumerationHistogram(const std::string& name, int boundary) {
121 rtc::CritScope cs(&crit_);
122 const auto& it = map_.find(name);
123 if (it != map_.end())
124 return reinterpret_cast<Histogram*>(it->second.get());
125
126 RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1);
127 map_[name].reset(hist);
128 return reinterpret_cast<Histogram*>(hist);
129 }
130
131 void GetAndReset(
132 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
133 rtc::CritScope cs(&crit_);
134 for (const auto& kv : map_) {
135 std::unique_ptr<SampleInfo> info = kv.second->GetAndReset();
136 if (info)
137 histograms->insert(std::make_pair(kv.first, std::move(info)));
138 }
139 }
140
141 // Functions only for testing.
142 void Reset() {
143 rtc::CritScope cs(&crit_);
144 for (const auto& kv : map_)
145 kv.second->Reset();
146 }
147
148 int NumEvents(const std::string& name, int sample) const {
149 rtc::CritScope cs(&crit_);
150 const auto& it = map_.find(name);
151 return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
152 }
153
154 int NumSamples(const std::string& name) const {
155 rtc::CritScope cs(&crit_);
156 const auto& it = map_.find(name);
157 return (it == map_.end()) ? 0 : it->second->NumSamples();
158 }
159
160 int MinSample(const std::string& name) const {
161 rtc::CritScope cs(&crit_);
162 const auto& it = map_.find(name);
163 return (it == map_.end()) ? -1 : it->second->MinSample();
164 }
165
166 private:
167 rtc::CriticalSection crit_;
168 std::map<std::string, std::unique_ptr<RtcHistogram>> map_ GUARDED_BY(crit_);
169
170 RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogramMap);
171};
172
173// RtcHistogramMap is allocated upon call to Enable().
174// The histogram getter functions, which return pointer values to the histograms
175// in the map, are cached in WebRTC. Therefore, this memory is not freed by the
176// application (the memory will be reclaimed by the OS).
177static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr;
178
179void CreateMap() {
180 RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map);
181 if (map == nullptr) {
182 RtcHistogramMap* new_map = new RtcHistogramMap();
183 RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr(
184 &g_rtc_histogram_map, static_cast<RtcHistogramMap*>(nullptr), new_map);
185 if (old_map != nullptr)
186 delete new_map;
187 }
188}
189
190// Set the first time we start using histograms. Used to make sure Enable() is
191// not called thereafter.
192#if RTC_DCHECK_IS_ON
193static volatile int g_rtc_histogram_called = 0;
194#endif
195
196// Gets the map (or nullptr).
197RtcHistogramMap* GetMap() {
198#if RTC_DCHECK_IS_ON
199 rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1);
200#endif
201 return g_rtc_histogram_map;
202}
203} // namespace
204
205// Implementation of histogram methods in
206// webrtc/system_wrappers/interface/metrics.h.
207
208// Histogram with exponentially spaced buckets.
209// Creates (or finds) histogram.
210// The returned histogram pointer is cached (and used for adding samples in
211// subsequent calls).
212Histogram* HistogramFactoryGetCounts(const std::string& name,
213 int min,
214 int max,
215 int bucket_count) {
216 RtcHistogramMap* map = GetMap();
217 if (!map)
218 return nullptr;
219
220 return map->GetCountsHistogram(name, min, max, bucket_count);
221}
222
223// Histogram with linearly spaced buckets.
224// Creates (or finds) histogram.
225// The returned histogram pointer is cached (and used for adding samples in
226// subsequent calls).
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000227Histogram* HistogramFactoryGetEnumeration(const std::string& name,
asapersson01d70a32016-05-20 06:29:46 -0700228 int boundary) {
229 RtcHistogramMap* map = GetMap();
230 if (!map)
231 return nullptr;
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000232
asapersson01d70a32016-05-20 06:29:46 -0700233 return map->GetEnumerationHistogram(name, boundary);
234}
235
sakal71b83932016-09-16 06:56:15 -0700236const std::string& GetHistogramName(Histogram* histogram_pointer) {
asapersson01d70a32016-05-20 06:29:46 -0700237 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
sakal71b83932016-09-16 06:56:15 -0700238 return ptr->name();
asapersson01d70a32016-05-20 06:29:46 -0700239}
240
sakal2a5f3712016-09-09 00:11:48 -0700241// Fast path. Adds |sample| to cached |histogram_pointer|.
242void HistogramAdd(Histogram* histogram_pointer, int sample) {
sakal2a5f3712016-09-09 00:11:48 -0700243 RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
244 ptr->Add(sample);
245}
246
asapersson01d70a32016-05-20 06:29:46 -0700247SampleInfo::SampleInfo(const std::string& name,
248 int min,
249 int max,
250 size_t bucket_count)
251 : name(name), min(min), max(max), bucket_count(bucket_count) {}
252
253SampleInfo::~SampleInfo() {}
254
255// Implementation of global functions in metrics_default.h.
256void Enable() {
257 RTC_DCHECK(g_rtc_histogram_map == nullptr);
258#if RTC_DCHECK_IS_ON
259 RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called));
260#endif
261 CreateMap();
262}
263
264void GetAndReset(
265 std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) {
266 histograms->clear();
267 RtcHistogramMap* map = GetMap();
268 if (map)
269 map->GetAndReset(histograms);
270}
271
272void Reset() {
273 RtcHistogramMap* map = GetMap();
274 if (map)
275 map->Reset();
276}
277
278int NumEvents(const std::string& name, int sample) {
279 RtcHistogramMap* map = GetMap();
280 return map ? map->NumEvents(name, sample) : 0;
281}
282
283int NumSamples(const std::string& name) {
284 RtcHistogramMap* map = GetMap();
285 return map ? map->NumSamples(name) : 0;
286}
287
288int MinSample(const std::string& name) {
289 RtcHistogramMap* map = GetMap();
290 return map ? map->MinSample(name) : -1;
291}
asapersson@webrtc.org580d3672014-10-23 12:57:56 +0000292
293} // namespace metrics
294} // namespace webrtc