• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/metrics/sample_vector.h"
6 
7 #include <limits.h>
8 #include <stddef.h>
9 
10 #include <atomic>
11 #include <memory>
12 #include <vector>
13 
14 #include "base/metrics/bucket_ranges.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/persistent_memory_allocator.h"
17 #include "base/test/gtest_util.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace base {
21 
22 // This framework class has "friend" access to the SampleVector for accessing
23 // non-public methods and fields.
24 class SampleVectorTest : public testing::Test {
25  public:
GetSamplesCounts(const SampleVectorBase & samples)26   const HistogramBase::AtomicCount* GetSamplesCounts(
27       const SampleVectorBase& samples) {
28     return samples.counts();
29   }
30 };
31 
TEST_F(SampleVectorTest,Accumulate)32 TEST_F(SampleVectorTest, Accumulate) {
33   // Custom buckets: [1, 5) [5, 10)
34   BucketRanges ranges(3);
35   ranges.set_range(0, 1);
36   ranges.set_range(1, 5);
37   ranges.set_range(2, 10);
38   SampleVector samples(1, &ranges);
39 
40   samples.Accumulate(1, 200);
41   samples.Accumulate(2, -300);
42   EXPECT_EQ(-100, samples.GetCountAtIndex(0));
43 
44   samples.Accumulate(5, 200);
45   EXPECT_EQ(200, samples.GetCountAtIndex(1));
46 
47   EXPECT_EQ(600, samples.sum());
48   EXPECT_EQ(100, samples.redundant_count());
49   EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
50 
51   samples.Accumulate(5, -100);
52   EXPECT_EQ(100, samples.GetCountAtIndex(1));
53 
54   EXPECT_EQ(100, samples.sum());
55   EXPECT_EQ(0, samples.redundant_count());
56   EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
57 }
58 
TEST_F(SampleVectorTest,Accumulate_LargeValuesDontOverflow)59 TEST_F(SampleVectorTest, Accumulate_LargeValuesDontOverflow) {
60   // Custom buckets: [1, 250000000) [250000000, 500000000)
61   BucketRanges ranges(3);
62   ranges.set_range(0, 1);
63   ranges.set_range(1, 250000000);
64   ranges.set_range(2, 500000000);
65   SampleVector samples(1, &ranges);
66 
67   samples.Accumulate(240000000, 200);
68   samples.Accumulate(249999999, -300);
69   EXPECT_EQ(-100, samples.GetCountAtIndex(0));
70 
71   samples.Accumulate(250000000, 200);
72   EXPECT_EQ(200, samples.GetCountAtIndex(1));
73 
74   EXPECT_EQ(23000000300LL, samples.sum());
75   EXPECT_EQ(100, samples.redundant_count());
76   EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
77 
78   samples.Accumulate(250000000, -100);
79   EXPECT_EQ(100, samples.GetCountAtIndex(1));
80 
81   EXPECT_EQ(-1999999700LL, samples.sum());
82   EXPECT_EQ(0, samples.redundant_count());
83   EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
84 }
85 
TEST_F(SampleVectorTest,AddSubtract)86 TEST_F(SampleVectorTest, AddSubtract) {
87   // Custom buckets: [0, 1) [1, 2) [2, 3) [3, INT_MAX)
88   BucketRanges ranges(5);
89   ranges.set_range(0, 0);
90   ranges.set_range(1, 1);
91   ranges.set_range(2, 2);
92   ranges.set_range(3, 3);
93   ranges.set_range(4, INT_MAX);
94 
95   SampleVector samples1(1, &ranges);
96   samples1.Accumulate(0, 100);
97   samples1.Accumulate(2, 100);
98   samples1.Accumulate(4, 100);
99   EXPECT_EQ(600, samples1.sum());
100   EXPECT_EQ(300, samples1.TotalCount());
101   EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
102 
103   SampleVector samples2(2, &ranges);
104   samples2.Accumulate(1, 200);
105   samples2.Accumulate(2, 200);
106   samples2.Accumulate(4, 200);
107   EXPECT_EQ(1400, samples2.sum());
108   EXPECT_EQ(600, samples2.TotalCount());
109   EXPECT_EQ(samples2.redundant_count(), samples2.TotalCount());
110 
111   samples1.Add(samples2);
112   EXPECT_EQ(100, samples1.GetCountAtIndex(0));
113   EXPECT_EQ(200, samples1.GetCountAtIndex(1));
114   EXPECT_EQ(300, samples1.GetCountAtIndex(2));
115   EXPECT_EQ(300, samples1.GetCountAtIndex(3));
116   EXPECT_EQ(2000, samples1.sum());
117   EXPECT_EQ(900, samples1.TotalCount());
118   EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
119 
120   samples1.Subtract(samples2);
121   EXPECT_EQ(100, samples1.GetCountAtIndex(0));
122   EXPECT_EQ(0, samples1.GetCountAtIndex(1));
123   EXPECT_EQ(100, samples1.GetCountAtIndex(2));
124   EXPECT_EQ(100, samples1.GetCountAtIndex(3));
125   EXPECT_EQ(600, samples1.sum());
126   EXPECT_EQ(300, samples1.TotalCount());
127   EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
128 }
129 
TEST_F(SampleVectorTest,BucketIndexDeath)130 TEST_F(SampleVectorTest, BucketIndexDeath) {
131   // 8 buckets with exponential layout:
132   // [0, 1) [1, 2) [2, 4) [4, 8) [8, 16) [16, 32) [32, 64) [64, INT_MAX)
133   BucketRanges ranges(9);
134   Histogram::InitializeBucketRanges(1, 64, &ranges);
135   SampleVector samples(1, &ranges);
136 
137   // Normal case
138   samples.Accumulate(0, 1);
139   samples.Accumulate(3, 2);
140   samples.Accumulate(64, 3);
141   EXPECT_EQ(1, samples.GetCount(0));
142   EXPECT_EQ(2, samples.GetCount(2));
143   EXPECT_EQ(3, samples.GetCount(65));
144 
145   // Extreme case.
146   EXPECT_DEATH_IF_SUPPORTED(samples.Accumulate(INT_MIN, 100), "");
147   EXPECT_DEATH_IF_SUPPORTED(samples.Accumulate(-1, 100), "");
148   EXPECT_DEATH_IF_SUPPORTED(samples.Accumulate(INT_MAX, 100), "");
149 
150   // Custom buckets: [1, 5) [5, 10)
151   // Note, this is not a valid BucketRanges for Histogram because it does not
152   // have overflow buckets.
153   BucketRanges ranges2(3);
154   ranges2.set_range(0, 1);
155   ranges2.set_range(1, 5);
156   ranges2.set_range(2, 10);
157   SampleVector samples2(2, &ranges2);
158 
159   // Normal case.
160   samples2.Accumulate(1, 1);
161   samples2.Accumulate(4, 1);
162   samples2.Accumulate(5, 2);
163   samples2.Accumulate(9, 2);
164   EXPECT_EQ(2, samples2.GetCount(1));
165   EXPECT_EQ(4, samples2.GetCount(5));
166 
167   // Extreme case.
168   EXPECT_DEATH_IF_SUPPORTED(samples2.Accumulate(0, 100), "");
169   EXPECT_DEATH_IF_SUPPORTED(samples2.Accumulate(10, 100), "");
170 }
171 
TEST_F(SampleVectorTest,AddSubtractBucketNotMatchDeath)172 TEST_F(SampleVectorTest, AddSubtractBucketNotMatchDeath) {
173   // Custom buckets 1: [1, 3) [3, 5)
174   BucketRanges ranges1(3);
175   ranges1.set_range(0, 1);
176   ranges1.set_range(1, 3);
177   ranges1.set_range(2, 5);
178   SampleVector samples1(1, &ranges1);
179 
180   // Custom buckets 2: [0, 1) [1, 3) [3, 6) [6, 7)
181   BucketRanges ranges2(5);
182   ranges2.set_range(0, 0);
183   ranges2.set_range(1, 1);
184   ranges2.set_range(2, 3);
185   ranges2.set_range(3, 6);
186   ranges2.set_range(4, 7);
187   SampleVector samples2(2, &ranges2);
188 
189   samples2.Accumulate(1, 100);
190   samples1.Add(samples2);
191   EXPECT_EQ(100, samples1.GetCountAtIndex(0));
192 
193   // Extra bucket in the beginning. These should CHECK in GetBucketIndex.
194   samples2.Accumulate(0, 100);
195   EXPECT_DEATH_IF_SUPPORTED(samples1.Add(samples2), "");
196   EXPECT_DEATH_IF_SUPPORTED(samples1.Subtract(samples2), "");
197 
198   // Extra bucket in the end. These should cause AddSubtractImpl to fail, and
199   // Add to DCHECK as a result.
200   samples2.Accumulate(0, -100);
201   samples2.Accumulate(6, 100);
202   EXPECT_DCHECK_DEATH(samples1.Add(samples2));
203   EXPECT_DCHECK_DEATH(samples1.Subtract(samples2));
204 
205   // Bucket not match: [3, 5) VS [3, 6). These should cause AddSubtractImpl to
206   // DCHECK.
207   samples2.Accumulate(6, -100);
208   samples2.Accumulate(3, 100);
209   EXPECT_DCHECK_DEATH(samples1.Add(samples2));
210   EXPECT_DCHECK_DEATH(samples1.Subtract(samples2));
211 }
212 
TEST_F(SampleVectorTest,Iterate)213 TEST_F(SampleVectorTest, Iterate) {
214   BucketRanges ranges(5);
215   ranges.set_range(0, 0);
216   ranges.set_range(1, 1);
217   ranges.set_range(2, 2);
218   ranges.set_range(3, 3);
219   ranges.set_range(4, 4);
220 
221   // Create iterator from SampleVector.
222   SampleVector samples(1, &ranges);
223   samples.Accumulate(0, 0);  // Iterator will bypass this empty bucket.
224   samples.Accumulate(1, 1);
225   samples.Accumulate(2, 2);
226   samples.Accumulate(3, 3);
227   std::unique_ptr<SampleCountIterator> it = samples.Iterator();
228 
229   int i;
230   size_t index;
231   HistogramBase::Sample min;
232   int64_t max;
233   HistogramBase::Count count;
234   for (i = 1; !it->Done(); i++, it->Next()) {
235     it->Get(&min, &max, &count);
236     EXPECT_EQ(i, min);
237     EXPECT_EQ(i + 1, max);
238     EXPECT_EQ(i, count);
239 
240     EXPECT_TRUE(it->GetBucketIndex(&index));
241     EXPECT_EQ(static_cast<size_t>(i), index);
242   }
243   EXPECT_EQ(4, i);
244 }
245 
TEST_F(SampleVectorTest,IterateDoneDeath)246 TEST_F(SampleVectorTest, IterateDoneDeath) {
247   BucketRanges ranges(5);
248   ranges.set_range(0, 0);
249   ranges.set_range(1, 1);
250   ranges.set_range(2, 2);
251   ranges.set_range(3, 3);
252   ranges.set_range(4, INT_MAX);
253   SampleVector samples(1, &ranges);
254 
255   std::unique_ptr<SampleCountIterator> it = samples.Iterator();
256 
257   EXPECT_TRUE(it->Done());
258 
259   HistogramBase::Sample min;
260   int64_t max;
261   HistogramBase::Count count;
262   EXPECT_DCHECK_DEATH(it->Get(&min, &max, &count));
263 
264   EXPECT_DCHECK_DEATH(it->Next());
265 
266   samples.Accumulate(2, 100);
267   it = samples.Iterator();
268   EXPECT_FALSE(it->Done());
269 }
270 
TEST_F(SampleVectorTest,SingleSample)271 TEST_F(SampleVectorTest, SingleSample) {
272   // Custom buckets: [1, 5) [5, 10)
273   BucketRanges ranges(3);
274   ranges.set_range(0, 1);
275   ranges.set_range(1, 5);
276   ranges.set_range(2, 10);
277   SampleVector samples(&ranges);
278 
279   // Ensure that a single value accumulates correctly.
280   EXPECT_FALSE(GetSamplesCounts(samples));
281   samples.Accumulate(3, 200);
282   EXPECT_EQ(200, samples.GetCount(3));
283   EXPECT_FALSE(GetSamplesCounts(samples));
284   samples.Accumulate(3, 400);
285   EXPECT_EQ(600, samples.GetCount(3));
286   EXPECT_FALSE(GetSamplesCounts(samples));
287   EXPECT_EQ(3 * 600, samples.sum());
288   EXPECT_EQ(600, samples.TotalCount());
289   EXPECT_EQ(600, samples.redundant_count());
290 
291   // Ensure that the iterator returns only one value.
292   HistogramBase::Sample min;
293   int64_t max;
294   HistogramBase::Count count;
295   std::unique_ptr<SampleCountIterator> it = samples.Iterator();
296   ASSERT_FALSE(it->Done());
297   it->Get(&min, &max, &count);
298   EXPECT_EQ(1, min);
299   EXPECT_EQ(5, max);
300   EXPECT_EQ(600, count);
301   it->Next();
302   EXPECT_TRUE(it->Done());
303 
304   // Ensure that it can be merged to another single-sample vector.
305   SampleVector samples_copy(&ranges);
306   samples_copy.Add(samples);
307   EXPECT_FALSE(GetSamplesCounts(samples_copy));
308   EXPECT_EQ(3 * 600, samples_copy.sum());
309   EXPECT_EQ(600, samples_copy.TotalCount());
310   EXPECT_EQ(600, samples_copy.redundant_count());
311 
312   // A different value should cause creation of the counts array.
313   samples.Accumulate(8, 100);
314   EXPECT_TRUE(GetSamplesCounts(samples));
315   EXPECT_EQ(600, samples.GetCount(3));
316   EXPECT_EQ(100, samples.GetCount(8));
317   EXPECT_EQ(3 * 600 + 8 * 100, samples.sum());
318   EXPECT_EQ(600 + 100, samples.TotalCount());
319   EXPECT_EQ(600 + 100, samples.redundant_count());
320 
321   // The iterator should now return both values.
322   it = samples.Iterator();
323   ASSERT_FALSE(it->Done());
324   it->Get(&min, &max, &count);
325   EXPECT_EQ(1, min);
326   EXPECT_EQ(5, max);
327   EXPECT_EQ(600, count);
328   it->Next();
329   ASSERT_FALSE(it->Done());
330   it->Get(&min, &max, &count);
331   EXPECT_EQ(5, min);
332   EXPECT_EQ(10, max);
333   EXPECT_EQ(100, count);
334   it->Next();
335   EXPECT_TRUE(it->Done());
336 
337   // Ensure that it can merged to a single-sample vector.
338   samples_copy.Add(samples);
339   EXPECT_TRUE(GetSamplesCounts(samples_copy));
340   EXPECT_EQ(3 * 1200 + 8 * 100, samples_copy.sum());
341   EXPECT_EQ(1200 + 100, samples_copy.TotalCount());
342   EXPECT_EQ(1200 + 100, samples_copy.redundant_count());
343 }
344 
TEST_F(SampleVectorTest,PersistentSampleVector)345 TEST_F(SampleVectorTest, PersistentSampleVector) {
346   LocalPersistentMemoryAllocator allocator(64 << 10, 0, "");
347   std::atomic<PersistentMemoryAllocator::Reference> samples_ref;
348   samples_ref.store(0, std::memory_order_relaxed);
349   HistogramSamples::Metadata samples_meta;
350   memset(&samples_meta, 0, sizeof(samples_meta));
351 
352   // Custom buckets: [1, 5) [5, 10)
353   BucketRanges ranges(3);
354   ranges.set_range(0, 1);
355   ranges.set_range(1, 5);
356   ranges.set_range(2, 10);
357 
358   // Persistent allocation.
359   const size_t counts_bytes =
360       sizeof(HistogramBase::AtomicCount) * ranges.bucket_count();
361   const DelayedPersistentAllocation allocation(&allocator, &samples_ref, 1,
362                                                counts_bytes, false);
363 
364   PersistentSampleVector samples1(0, &ranges, &samples_meta, allocation);
365   EXPECT_FALSE(GetSamplesCounts(samples1));
366   samples1.Accumulate(3, 200);
367   EXPECT_EQ(200, samples1.GetCount(3));
368   EXPECT_FALSE(GetSamplesCounts(samples1));
369   EXPECT_EQ(0, samples1.GetCount(8));
370   EXPECT_FALSE(GetSamplesCounts(samples1));
371 
372   PersistentSampleVector samples2(0, &ranges, &samples_meta, allocation);
373   EXPECT_EQ(200, samples2.GetCount(3));
374   EXPECT_FALSE(GetSamplesCounts(samples2));
375 
376   HistogramBase::Sample min;
377   int64_t max;
378   HistogramBase::Count count;
379   std::unique_ptr<SampleCountIterator> it = samples2.Iterator();
380   ASSERT_FALSE(it->Done());
381   it->Get(&min, &max, &count);
382   EXPECT_EQ(1, min);
383   EXPECT_EQ(5, max);
384   EXPECT_EQ(200, count);
385   it->Next();
386   EXPECT_TRUE(it->Done());
387 
388   samples1.Accumulate(8, 100);
389   EXPECT_TRUE(GetSamplesCounts(samples1));
390 
391   EXPECT_FALSE(GetSamplesCounts(samples2));
392   EXPECT_EQ(200, samples2.GetCount(3));
393   EXPECT_EQ(100, samples2.GetCount(8));
394   EXPECT_TRUE(GetSamplesCounts(samples2));
395   EXPECT_EQ(3 * 200 + 8 * 100, samples2.sum());
396   EXPECT_EQ(300, samples2.TotalCount());
397   EXPECT_EQ(300, samples2.redundant_count());
398 
399   it = samples2.Iterator();
400   ASSERT_FALSE(it->Done());
401   it->Get(&min, &max, &count);
402   EXPECT_EQ(1, min);
403   EXPECT_EQ(5, max);
404   EXPECT_EQ(200, count);
405   it->Next();
406   ASSERT_FALSE(it->Done());
407   it->Get(&min, &max, &count);
408   EXPECT_EQ(5, min);
409   EXPECT_EQ(10, max);
410   EXPECT_EQ(100, count);
411   it->Next();
412   EXPECT_TRUE(it->Done());
413 
414   PersistentSampleVector samples3(0, &ranges, &samples_meta, allocation);
415   EXPECT_TRUE(GetSamplesCounts(samples2));
416   EXPECT_EQ(200, samples3.GetCount(3));
417   EXPECT_EQ(100, samples3.GetCount(8));
418   EXPECT_EQ(3 * 200 + 8 * 100, samples3.sum());
419   EXPECT_EQ(300, samples3.TotalCount());
420   EXPECT_EQ(300, samples3.redundant_count());
421 
422   it = samples3.Iterator();
423   ASSERT_FALSE(it->Done());
424   it->Get(&min, &max, &count);
425   EXPECT_EQ(1, min);
426   EXPECT_EQ(5, max);
427   EXPECT_EQ(200, count);
428   it->Next();
429   ASSERT_FALSE(it->Done());
430   it->Get(&min, &max, &count);
431   EXPECT_EQ(5, min);
432   EXPECT_EQ(10, max);
433   EXPECT_EQ(100, count);
434   it->Next();
435   EXPECT_TRUE(it->Done());
436 }
437 
TEST_F(SampleVectorTest,PersistentSampleVectorTestWithOutsideAlloc)438 TEST_F(SampleVectorTest, PersistentSampleVectorTestWithOutsideAlloc) {
439   LocalPersistentMemoryAllocator allocator(64 << 10, 0, "");
440   std::atomic<PersistentMemoryAllocator::Reference> samples_ref;
441   samples_ref.store(0, std::memory_order_relaxed);
442   HistogramSamples::Metadata samples_meta;
443   memset(&samples_meta, 0, sizeof(samples_meta));
444 
445   // Custom buckets: [1, 5) [5, 10)
446   BucketRanges ranges(3);
447   ranges.set_range(0, 1);
448   ranges.set_range(1, 5);
449   ranges.set_range(2, 10);
450 
451   // Persistent allocation.
452   const size_t counts_bytes =
453       sizeof(HistogramBase::AtomicCount) * ranges.bucket_count();
454   const DelayedPersistentAllocation allocation(&allocator, &samples_ref, 1,
455                                                counts_bytes, false);
456 
457   PersistentSampleVector samples1(0, &ranges, &samples_meta, allocation);
458   EXPECT_FALSE(GetSamplesCounts(samples1));
459   samples1.Accumulate(3, 200);
460   EXPECT_EQ(200, samples1.GetCount(3));
461   EXPECT_FALSE(GetSamplesCounts(samples1));
462 
463   // Because the delayed allocation can be shared with other objects (the
464   // |offset| parameter allows concatinating multiple data blocks into the
465   // same allocation), it's possible that the allocation gets realized from
466   // the outside even though the data block being accessed is all zero.
467   allocation.Get();
468   EXPECT_EQ(200, samples1.GetCount(3));
469   EXPECT_FALSE(GetSamplesCounts(samples1));
470 
471   HistogramBase::Sample min;
472   int64_t max;
473   HistogramBase::Count count;
474   std::unique_ptr<SampleCountIterator> it = samples1.Iterator();
475   ASSERT_FALSE(it->Done());
476   it->Get(&min, &max, &count);
477   EXPECT_EQ(1, min);
478   EXPECT_EQ(5, max);
479   EXPECT_EQ(200, count);
480   it->Next();
481   EXPECT_TRUE(it->Done());
482 
483   // A duplicate samples object should still see the single-sample entry even
484   // when storage is available.
485   PersistentSampleVector samples2(0, &ranges, &samples_meta, allocation);
486   EXPECT_EQ(200, samples2.GetCount(3));
487 
488   // New accumulations, in both directions, of the existing value should work.
489   samples1.Accumulate(3, 50);
490   EXPECT_EQ(250, samples1.GetCount(3));
491   EXPECT_EQ(250, samples2.GetCount(3));
492   samples2.Accumulate(3, 50);
493   EXPECT_EQ(300, samples1.GetCount(3));
494   EXPECT_EQ(300, samples2.GetCount(3));
495 
496   it = samples1.Iterator();
497   ASSERT_FALSE(it->Done());
498   it->Get(&min, &max, &count);
499   EXPECT_EQ(1, min);
500   EXPECT_EQ(5, max);
501   EXPECT_EQ(300, count);
502   it->Next();
503   EXPECT_TRUE(it->Done());
504 
505   samples1.Accumulate(8, 100);
506   EXPECT_TRUE(GetSamplesCounts(samples1));
507   EXPECT_EQ(300, samples1.GetCount(3));
508   EXPECT_EQ(300, samples2.GetCount(3));
509   EXPECT_EQ(100, samples1.GetCount(8));
510   EXPECT_EQ(100, samples2.GetCount(8));
511   samples2.Accumulate(8, 100);
512   EXPECT_EQ(300, samples1.GetCount(3));
513   EXPECT_EQ(300, samples2.GetCount(3));
514   EXPECT_EQ(200, samples1.GetCount(8));
515   EXPECT_EQ(200, samples2.GetCount(8));
516 }
517 
518 // Tests GetPeakBucketSize() returns accurate max bucket size.
TEST_F(SampleVectorTest,GetPeakBucketSize)519 TEST_F(SampleVectorTest, GetPeakBucketSize) {
520   // Custom buckets: [1, 5) [5, 10) [10, 20)
521   BucketRanges ranges(4);
522   ranges.set_range(0, 1);
523   ranges.set_range(1, 5);
524   ranges.set_range(2, 10);
525   ranges.set_range(3, 20);
526   SampleVector samples(1, &ranges);
527   samples.Accumulate(3, 1);
528   samples.Accumulate(6, 2);
529   samples.Accumulate(12, 3);
530   EXPECT_EQ(3, samples.GetPeakBucketSize());
531 }
532 
533 }  // namespace base
534