1 //
2 // Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3 //
4 // Use of this source code is governed by a BSD-style license
5 // that can be found in the LICENSE file in the root of the source
6 // tree. An additional intellectual property rights grant can be found
7 // in the file PATENTS.  All contributing project authors may
8 // be found in the AUTHORS file in the root of the source tree.
9 //
10 
11 #ifndef SYSTEM_WRAPPERS_INCLUDE_METRICS_H_
12 #define SYSTEM_WRAPPERS_INCLUDE_METRICS_H_
13 
14 #include <stddef.h>
15 
16 #include <map>
17 #include <memory>
18 #include <string>
19 
20 #include "rtc_base/atomic_ops.h"
21 #include "rtc_base/checks.h"
22 
23 #if defined(RTC_DISABLE_METRICS)
24 #define RTC_METRICS_ENABLED 0
25 #else
26 #define RTC_METRICS_ENABLED 1
27 #endif
28 
29 namespace webrtc {
30 namespace metrics_impl {
31 template <typename... Ts>
NoOp(const Ts &...)32 void NoOp(const Ts&...) {}
33 }
34 }
35 
36 #if RTC_METRICS_ENABLED
37 #define EXPECT_METRIC_EQ(val1, val2) EXPECT_EQ(val1, val2)
38 #define EXPECT_METRIC_EQ_WAIT(val1, val2, timeout) \
39   EXPECT_EQ_WAIT(val1, val2, timeout)
40 #define EXPECT_METRIC_GT(val1, val2) EXPECT_GT(val1, val2)
41 #define EXPECT_METRIC_LE(val1, val2) EXPECT_LE(val1, val2)
42 #define EXPECT_METRIC_TRUE(conditon) EXPECT_TRUE(conditon)
43 #define EXPECT_METRIC_FALSE(conditon) EXPECT_FALSE(conditon)
44 #define EXPECT_METRIC_THAT(value, matcher) EXPECT_THAT(value, matcher)
45 #else
46 #define EXPECT_METRIC_EQ(val1, val2) webrtc::metrics_impl::NoOp(val1, val2)
47 #define EXPECT_METRIC_EQ_WAIT(val1, val2, timeout) webrtc::metrics_impl::NoOp(val1, val2, timeout)
48 #define EXPECT_METRIC_GT(val1, val2) webrtc::metrics_impl::NoOp(val1, val2)
49 #define EXPECT_METRIC_LE(val1, val2) webrtc::metrics_impl::NoOp(val1, val2)
50 #define EXPECT_METRIC_TRUE(condition) webrtc::metrics_impl::NoOp(condition || true)
51 #define EXPECT_METRIC_FALSE(condition) webrtc::metrics_impl::NoOp(condition && false)
52 #define EXPECT_METRIC_THAT(value, matcher) webrtc::metrics_impl::NoOp(value, testing::_)
53 #endif
54 
55 #if RTC_METRICS_ENABLED
56 // Macros for allowing WebRTC clients (e.g. Chrome) to gather and aggregate
57 // statistics.
58 //
59 // Histogram for counters.
60 // RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count);
61 //
62 // Histogram for enumerators.
63 // The boundary should be above the max enumerator sample.
64 // RTC_HISTOGRAM_ENUMERATION(name, sample, boundary);
65 //
66 //
67 // The macros use the methods HistogramFactoryGetCounts,
68 // HistogramFactoryGetEnumeration and HistogramAdd.
69 //
70 // By default WebRTC provides implementations of the aforementioned methods
71 // that can be found in system_wrappers/source/metrics.cc. If clients want to
72 // provide a custom version, they will have to:
73 //
74 // 1. Compile WebRTC defining the preprocessor macro
75 //    WEBRTC_EXCLUDE_METRICS_DEFAULT (if GN is used this can be achieved
76 //    by setting the GN arg rtc_exclude_metrics_default to true).
77 // 2. Provide implementations of:
78 //    Histogram* webrtc::metrics::HistogramFactoryGetCounts(
79 //        const std::string& name, int sample, int min, int max,
80 //        int bucket_count);
81 //    Histogram* webrtc::metrics::HistogramFactoryGetEnumeration(
82 //        const std::string& name, int sample, int boundary);
83 //    void webrtc::metrics::HistogramAdd(
84 //        Histogram* histogram_pointer, const std::string& name, int sample);
85 //
86 // Example usage:
87 //
88 // RTC_HISTOGRAM_COUNTS("WebRTC.Video.NacksSent", nacks_sent, 1, 100000, 100);
89 //
90 // enum Types {
91 //   kTypeX,
92 //   kTypeY,
93 //   kBoundary,
94 // };
95 //
96 // RTC_HISTOGRAM_ENUMERATION("WebRTC.Types", kTypeX, kBoundary);
97 //
98 // NOTE: It is recommended to do the Chromium review for modifications to
99 // histograms.xml before new metrics are committed to WebRTC.
100 
101 // Macros for adding samples to a named histogram.
102 
103 // Histogram for counters (exponentially spaced buckets).
104 #define RTC_HISTOGRAM_COUNTS_100(name, sample) \
105   RTC_HISTOGRAM_COUNTS(name, sample, 1, 100, 50)
106 
107 #define RTC_HISTOGRAM_COUNTS_200(name, sample) \
108   RTC_HISTOGRAM_COUNTS(name, sample, 1, 200, 50)
109 
110 #define RTC_HISTOGRAM_COUNTS_500(name, sample) \
111   RTC_HISTOGRAM_COUNTS(name, sample, 1, 500, 50)
112 
113 #define RTC_HISTOGRAM_COUNTS_1000(name, sample) \
114   RTC_HISTOGRAM_COUNTS(name, sample, 1, 1000, 50)
115 
116 #define RTC_HISTOGRAM_COUNTS_10000(name, sample) \
117   RTC_HISTOGRAM_COUNTS(name, sample, 1, 10000, 50)
118 
119 #define RTC_HISTOGRAM_COUNTS_100000(name, sample) \
120   RTC_HISTOGRAM_COUNTS(name, sample, 1, 100000, 50)
121 
122 #define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count)       \
123   RTC_HISTOGRAM_COMMON_BLOCK(name, sample,                               \
124                              webrtc::metrics::HistogramFactoryGetCounts( \
125                                  name, min, max, bucket_count))
126 
127 #define RTC_HISTOGRAM_COUNTS_LINEAR(name, sample, min, max, bucket_count)      \
128   RTC_HISTOGRAM_COMMON_BLOCK(name, sample,                                     \
129                              webrtc::metrics::HistogramFactoryGetCountsLinear( \
130                                  name, min, max, bucket_count))
131 
132 // Slow metrics: pointer to metric is acquired at each call and is not cached.
133 //
134 #define RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample) \
135   RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100, 50)
136 
137 #define RTC_HISTOGRAM_COUNTS_SPARSE_200(name, sample) \
138   RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 200, 50)
139 
140 #define RTC_HISTOGRAM_COUNTS_SPARSE_500(name, sample) \
141   RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 500, 50)
142 
143 #define RTC_HISTOGRAM_COUNTS_SPARSE_1000(name, sample) \
144   RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 1000, 50)
145 
146 #define RTC_HISTOGRAM_COUNTS_SPARSE_10000(name, sample) \
147   RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 10000, 50)
148 
149 #define RTC_HISTOGRAM_COUNTS_SPARSE_100000(name, sample) \
150   RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100000, 50)
151 
152 #define RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, min, max, bucket_count)     \
153   RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample,                               \
154                                   webrtc::metrics::HistogramFactoryGetCounts( \
155                                       name, min, max, bucket_count))
156 
157 // Histogram for percentage (evenly spaced buckets).
158 #define RTC_HISTOGRAM_PERCENTAGE_SPARSE(name, sample) \
159   RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 101)
160 
161 // Histogram for booleans.
162 #define RTC_HISTOGRAM_BOOLEAN_SPARSE(name, sample) \
163   RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 2)
164 
165 // Histogram for enumerators (evenly spaced buckets).
166 // |boundary| should be above the max enumerator sample.
167 //
168 // TODO(qingsi): Refactor the default implementation given by RtcHistogram,
169 // which is already sparse, and remove the boundary argument from the macro.
170 #define RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, boundary) \
171   RTC_HISTOGRAM_COMMON_BLOCK_SLOW(                               \
172       name, sample,                                              \
173       webrtc::metrics::SparseHistogramFactoryGetEnumeration(name, boundary))
174 
175 // Histogram for percentage (evenly spaced buckets).
176 #define RTC_HISTOGRAM_PERCENTAGE(name, sample) \
177   RTC_HISTOGRAM_ENUMERATION(name, sample, 101)
178 
179 // Histogram for booleans.
180 #define RTC_HISTOGRAM_BOOLEAN(name, sample) \
181   RTC_HISTOGRAM_ENUMERATION(name, sample, 2)
182 
183 // Histogram for enumerators (evenly spaced buckets).
184 // |boundary| should be above the max enumerator sample.
185 #define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \
186   RTC_HISTOGRAM_COMMON_BLOCK_SLOW(                        \
187       name, sample,                                       \
188       webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary))
189 
190 // The name of the histogram should not vary.
191 // TODO(asapersson): Consider changing string to const char*.
192 #define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample,                  \
193                                    factory_get_invocation)                 \
194   do {                                                                     \
195     static webrtc::metrics::Histogram* atomic_histogram_pointer = nullptr; \
196     webrtc::metrics::Histogram* histogram_pointer =                        \
197         rtc::AtomicOps::AcquireLoadPtr(&atomic_histogram_pointer);         \
198     if (!histogram_pointer) {                                              \
199       histogram_pointer = factory_get_invocation;                          \
200       webrtc::metrics::Histogram* prev_pointer =                           \
201           rtc::AtomicOps::CompareAndSwapPtr(                               \
202               &atomic_histogram_pointer,                                   \
203               static_cast<webrtc::metrics::Histogram*>(nullptr),           \
204               histogram_pointer);                                          \
205       RTC_DCHECK(prev_pointer == nullptr ||                                \
206                  prev_pointer == histogram_pointer);                       \
207     }                                                                      \
208     if (histogram_pointer) {                                               \
209       webrtc::metrics::HistogramAdd(histogram_pointer, sample);            \
210     }                                                                      \
211   } while (0)
212 
213 // The histogram is constructed/found for each call.
214 // May be used for histograms with infrequent updates.`
215 #define RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, factory_get_invocation) \
216   do {                                                                        \
217     webrtc::metrics::Histogram* histogram_pointer = factory_get_invocation;   \
218     if (histogram_pointer) {                                                  \
219       webrtc::metrics::HistogramAdd(histogram_pointer, sample);               \
220     }                                                                         \
221   } while (0)
222 
223 // Helper macros.
224 // Macros for calling a histogram with varying name (e.g. when using a metric
225 // in different modes such as real-time vs screenshare). Fast, because pointer
226 // is cached. |index| should be different for different names. Allowed |index|
227 // values are 0, 1, and 2.
228 #define RTC_HISTOGRAMS_COUNTS_100(index, name, sample) \
229   RTC_HISTOGRAMS_COMMON(index, name, sample,           \
230                         RTC_HISTOGRAM_COUNTS(name, sample, 1, 100, 50))
231 
232 #define RTC_HISTOGRAMS_COUNTS_200(index, name, sample) \
233   RTC_HISTOGRAMS_COMMON(index, name, sample,           \
234                         RTC_HISTOGRAM_COUNTS(name, sample, 1, 200, 50))
235 
236 #define RTC_HISTOGRAMS_COUNTS_500(index, name, sample) \
237   RTC_HISTOGRAMS_COMMON(index, name, sample,           \
238                         RTC_HISTOGRAM_COUNTS(name, sample, 1, 500, 50))
239 
240 #define RTC_HISTOGRAMS_COUNTS_1000(index, name, sample) \
241   RTC_HISTOGRAMS_COMMON(index, name, sample,            \
242                         RTC_HISTOGRAM_COUNTS(name, sample, 1, 1000, 50))
243 
244 #define RTC_HISTOGRAMS_COUNTS_10000(index, name, sample) \
245   RTC_HISTOGRAMS_COMMON(index, name, sample,             \
246                         RTC_HISTOGRAM_COUNTS(name, sample, 1, 10000, 50))
247 
248 #define RTC_HISTOGRAMS_COUNTS_100000(index, name, sample) \
249   RTC_HISTOGRAMS_COMMON(index, name, sample,              \
250                         RTC_HISTOGRAM_COUNTS(name, sample, 1, 100000, 50))
251 
252 #define RTC_HISTOGRAMS_ENUMERATION(index, name, sample, boundary) \
253   RTC_HISTOGRAMS_COMMON(index, name, sample,                      \
254                         RTC_HISTOGRAM_ENUMERATION(name, sample, boundary))
255 
256 #define RTC_HISTOGRAMS_PERCENTAGE(index, name, sample) \
257   RTC_HISTOGRAMS_COMMON(index, name, sample,           \
258                         RTC_HISTOGRAM_PERCENTAGE(name, sample))
259 
260 #define RTC_HISTOGRAMS_COMMON(index, name, sample, macro_invocation) \
261   do {                                                               \
262     switch (index) {                                                 \
263       case 0:                                                        \
264         macro_invocation;                                            \
265         break;                                                       \
266       case 1:                                                        \
267         macro_invocation;                                            \
268         break;                                                       \
269       case 2:                                                        \
270         macro_invocation;                                            \
271         break;                                                       \
272       default:                                                       \
273         RTC_NOTREACHED();                                            \
274     }                                                                \
275   } while (0)
276 
277 #else
278 
279 ////////////////////////////////////////////////////////////////////////////////
280 // This section defines no-op alternatives to the metrics macros when
281 // RTC_METRICS_ENABLED is defined.
282 
283 #define RTC_HISTOGRAM_COUNTS_100(name, sample) webrtc::metrics_impl::NoOp(name, sample)
284 
285 #define RTC_HISTOGRAM_COUNTS_200(name, sample) webrtc::metrics_impl::NoOp(name, sample)
286 
287 #define RTC_HISTOGRAM_COUNTS_500(name, sample) webrtc::metrics_impl::NoOp(name, sample)
288 
289 #define RTC_HISTOGRAM_COUNTS_1000(name, sample) webrtc::metrics_impl::NoOp(name, sample)
290 
291 #define RTC_HISTOGRAM_COUNTS_10000(name, sample) webrtc::metrics_impl::NoOp(name, sample)
292 
293 #define RTC_HISTOGRAM_COUNTS_100000(name, sample) webrtc::metrics_impl::NoOp(name, sample)
294 
295 #define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \
296   webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count)
297 
298 #define RTC_HISTOGRAM_COUNTS_LINEAR(name, sample, min, max, bucket_count) \
299   webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count)
300 
301 #define RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample) webrtc::metrics_impl::NoOp(name, sample)
302 
303 #define RTC_HISTOGRAM_COUNTS_SPARSE_200(name, sample) webrtc::metrics_impl::NoOp(name, sample)
304 
305 #define RTC_HISTOGRAM_COUNTS_SPARSE_500(name, sample) webrtc::metrics_impl::NoOp(name, sample)
306 
307 #define RTC_HISTOGRAM_COUNTS_SPARSE_1000(name, sample) webrtc::metrics_impl::NoOp(name, sample)
308 
309 #define RTC_HISTOGRAM_COUNTS_SPARSE_10000(name, sample) webrtc::metrics_impl::NoOp(name, sample)
310 
311 #define RTC_HISTOGRAM_COUNTS_SPARSE_100000(name, sample) webrtc::metrics_impl::NoOp(name, sample)
312 
313 #define RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, min, max, bucket_count) \
314   webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count)
315 
316 #define RTC_HISTOGRAM_PERCENTAGE_SPARSE(name, sample) webrtc::metrics_impl::NoOp(name, sample)
317 
318 #define RTC_HISTOGRAM_BOOLEAN_SPARSE(name, sample) webrtc::metrics_impl::NoOp(name, sample)
319 
320 #define RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, boundary) \
321   webrtc::metrics_impl::NoOp(name, sample, boundary)
322 
323 #define RTC_HISTOGRAM_PERCENTAGE(name, sample) webrtc::metrics_impl::NoOp(name, sample)
324 
325 #define RTC_HISTOGRAM_BOOLEAN(name, sample) webrtc::metrics_impl::NoOp(name, sample)
326 
327 #define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \
328   webrtc::metrics_impl::NoOp(name, sample, boundary)
329 
330 #define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample,  \
331                                    factory_get_invocation) \
332   webrtc::metrics_impl::NoOp(constant_name, sample, factory_get_invocation)
333 
334 #define RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, factory_get_invocation) \
335   webrtc::metrics_impl::NoOp(name, sample, factory_get_invocation)
336 
337 #define RTC_HISTOGRAMS_COUNTS_100(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample)
338 
339 #define RTC_HISTOGRAMS_COUNTS_200(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample)
340 
341 #define RTC_HISTOGRAMS_COUNTS_500(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample)
342 
343 #define RTC_HISTOGRAMS_COUNTS_1000(index, name, sample) \
344   webrtc::metrics_impl::NoOp(index, name, sample)
345 
346 #define RTC_HISTOGRAMS_COUNTS_10000(index, name, sample) \
347   webrtc::metrics_impl::NoOp(index, name, sample)
348 
349 #define RTC_HISTOGRAMS_COUNTS_100000(index, name, sample) \
350   webrtc::metrics_impl::NoOp(index, name, sample)
351 
352 #define RTC_HISTOGRAMS_ENUMERATION(index, name, sample, boundary) \
353   webrtc::metrics_impl::NoOp(index, name, sample, boundary)
354 
355 #define RTC_HISTOGRAMS_PERCENTAGE(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample)
356 
357 #define RTC_HISTOGRAMS_COMMON(index, name, sample, macro_invocation) \
358   webrtc::metrics_impl::NoOp(index, name, sample, macro_invocation)
359 
360 #endif  // RTC_METRICS_ENABLED
361 
362 namespace webrtc {
363 namespace metrics {
364 
365 // Time that should have elapsed for stats that are gathered once per call.
366 enum { kMinRunTimeInSeconds = 10 };
367 
368 class Histogram;
369 
370 // Functions for getting pointer to histogram (constructs or finds the named
371 // histogram).
372 
373 // Get histogram for counters.
374 Histogram* HistogramFactoryGetCounts(const std::string& name,
375                                      int min,
376                                      int max,
377                                      int bucket_count);
378 
379 // Get histogram for counters with linear bucket spacing.
380 Histogram* HistogramFactoryGetCountsLinear(const std::string& name,
381                                            int min,
382                                            int max,
383                                            int bucket_count);
384 
385 // Get histogram for enumerators.
386 // |boundary| should be above the max enumerator sample.
387 Histogram* HistogramFactoryGetEnumeration(const std::string& name,
388                                           int boundary);
389 
390 // Get sparse histogram for enumerators.
391 // |boundary| should be above the max enumerator sample.
392 Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name,
393                                                 int boundary);
394 
395 // Function for adding a |sample| to a histogram.
396 void HistogramAdd(Histogram* histogram_pointer, int sample);
397 
398 struct SampleInfo {
399   SampleInfo(const std::string& name, int min, int max, size_t bucket_count);
400   ~SampleInfo();
401 
402   const std::string name;
403   const int min;
404   const int max;
405   const size_t bucket_count;
406   std::map<int, int> samples;  // <value, # of events>
407 };
408 
409 // Enables collection of samples.
410 // This method should be called before any other call into webrtc.
411 void Enable();
412 
413 // Gets histograms and clears all samples.
414 void GetAndReset(
415     std::map<std::string, std::unique_ptr<SampleInfo>>* histograms);
416 
417 // Functions below are mainly for testing.
418 
419 // Clears all samples.
420 void Reset();
421 
422 // Returns the number of times the |sample| has been added to the histogram.
423 int NumEvents(const std::string& name, int sample);
424 
425 // Returns the total number of added samples to the histogram.
426 int NumSamples(const std::string& name);
427 
428 // Returns the minimum sample value (or -1 if the histogram has no samples).
429 int MinSample(const std::string& name);
430 
431 // Returns a map with keys the samples with at least one event and values the
432 // number of events for that sample.
433 std::map<int, int> Samples(const std::string& name);
434 
435 }  // namespace metrics
436 }  // namespace webrtc
437 
438 #endif  // SYSTEM_WRAPPERS_INCLUDE_METRICS_H_
439