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