Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2011 The Chromium Authors |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 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/rand_util.h" |
| 6 | |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 7 | #include <limits.h> |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 8 | #include <math.h> |
tfarina | 3208992 | 2015-05-18 22:14:09 | [diff] [blame] | 9 | #include <stdint.h> |
[email protected] | 94a0f31 | 2008-09-30 14:26:33 | [diff] [blame] | 10 | |
John Chen | 87cc25c | 2018-04-20 22:49:49 | [diff] [blame] | 11 | #include <algorithm> |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 12 | #include <atomic> |
John Chen | 87cc25c | 2018-04-20 22:49:49 | [diff] [blame] | 13 | #include <limits> |
| 14 | |
Anand Ravi | b793ffb3 | 2025-02-25 22:56:54 | [diff] [blame] | 15 | #include "base/check.h" |
Hans Wennborg | c3cffa6 | 2020-04-27 10:09:12 | [diff] [blame] | 16 | #include "base/check_op.h" |
Peter Kasting | a253f75 | 2025-01-31 18:57:26 | [diff] [blame] | 17 | #include "base/containers/span.h" |
Peter Kasting | f18c8ca | 2023-10-04 16:31:51 | [diff] [blame] | 18 | #include "base/time/time.h" |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 19 | |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 20 | namespace base { |
| 21 | |
François Doray | 87e35a8 | 2023-05-05 14:44:28 | [diff] [blame] | 22 | namespace { |
| 23 | |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 24 | // A MetricSubsampler instance is not thread-safe. However, the global |
| 25 | // sampling state may be read concurrently with writing it via testing |
| 26 | // scopers, hence the need to use atomics. All operations use |
| 27 | // memory_order_relaxed because there are no dependent memory accesses. |
| 28 | std::atomic<bool> g_subsampling_always_sample = false; |
| 29 | std::atomic<bool> g_subsampling_never_sample = false; |
François Doray | 87e35a8 | 2023-05-05 14:44:28 | [diff] [blame] | 30 | |
Anthony Vallée-Dubois | ecfdb38 | 2025-02-04 20:07:40 | [diff] [blame] | 31 | MetricsSubSampler* GetSharedSubsampler() { |
| 32 | static thread_local MetricsSubSampler g_shared_subsampler; |
| 33 | return &g_shared_subsampler; |
| 34 | } |
| 35 | |
François Doray | 87e35a8 | 2023-05-05 14:44:28 | [diff] [blame] | 36 | } // namespace |
| 37 | |
John Mellor | afab972d | 2017-09-26 16:28:19 | [diff] [blame] | 38 | uint64_t RandUint64() { |
| 39 | uint64_t number; |
danakj | 95305d27 | 2024-05-09 20:38:44 | [diff] [blame] | 40 | RandBytes(base::byte_span_from_ref(number)); |
John Mellor | afab972d | 2017-09-26 16:28:19 | [diff] [blame] | 41 | return number; |
| 42 | } |
| 43 | |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 44 | int RandInt(int min, int max) { |
[email protected] | d7a93ad | 2011-04-22 13:13:07 | [diff] [blame] | 45 | DCHECK_LE(min, max); |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 46 | |
Peter Kasting | 28b51cf | 2022-06-28 15:02:43 | [diff] [blame] | 47 | uint64_t range = static_cast<uint64_t>(max) - static_cast<uint64_t>(min) + 1; |
John Chen | 87cc25c | 2018-04-20 22:49:49 | [diff] [blame] | 48 | // |range| is at most UINT_MAX + 1, so the result of RandGenerator(range) |
| 49 | // is at most UINT_MAX. Hence it's safe to cast it from uint64_t to int64_t. |
| 50 | int result = |
| 51 | static_cast<int>(min + static_cast<int64_t>(base::RandGenerator(range))); |
[email protected] | e1be56d | 2011-05-04 01:29:38 | [diff] [blame] | 52 | DCHECK_GE(result, min); |
| 53 | DCHECK_LE(result, max); |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 54 | return result; |
| 55 | } |
| 56 | |
| 57 | double RandDouble() { |
[email protected] | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 58 | return BitsToOpenEndedUnitInterval(base::RandUint64()); |
| 59 | } |
| 60 | |
Avery Musbach | eff342b | 2022-10-06 18:36:07 | [diff] [blame] | 61 | float RandFloat() { |
| 62 | return BitsToOpenEndedUnitIntervalF(base::RandUint64()); |
| 63 | } |
| 64 | |
Peter Kasting | b2dc5504 | 2025-01-16 16:30:54 | [diff] [blame] | 65 | bool RandBool() { |
| 66 | uint8_t number; |
| 67 | RandBytes(span_from_ref(number)); |
| 68 | return number & 1; |
| 69 | } |
| 70 | |
Peter Kasting | f18c8ca | 2023-10-04 16:31:51 | [diff] [blame] | 71 | TimeDelta RandTimeDelta(TimeDelta start, TimeDelta limit) { |
| 72 | // We must have a finite, non-empty, non-reversed interval. |
| 73 | CHECK_LT(start, limit); |
| 74 | CHECK(!start.is_min()); |
| 75 | CHECK(!limit.is_max()); |
| 76 | |
| 77 | const int64_t range = (limit - start).InMicroseconds(); |
| 78 | // Because of the `CHECK_LT()` above, range > 0, so this cast is safe. |
| 79 | const uint64_t delta_us = base::RandGenerator(static_cast<uint64_t>(range)); |
| 80 | // ...and because `range` fit in an `int64_t`, so will `delta_us`. |
| 81 | return start + Microseconds(static_cast<int64_t>(delta_us)); |
| 82 | } |
| 83 | |
| 84 | TimeDelta RandTimeDeltaUpTo(TimeDelta limit) { |
| 85 | return RandTimeDelta(TimeDelta(), limit); |
| 86 | } |
| 87 | |
Nico Weber | 0a3852a7 | 2015-10-29 20:42:58 | [diff] [blame] | 88 | double BitsToOpenEndedUnitInterval(uint64_t bits) { |
[email protected] | 94a0f31 | 2008-09-30 14:26:33 | [diff] [blame] | 89 | // We try to get maximum precision by masking out as many bits as will fit |
| 90 | // in the target type's mantissa, and raising it to an appropriate power to |
| 91 | // produce output in the range [0, 1). For IEEE 754 doubles, the mantissa |
Avery Musbach | 92a30e38 | 2022-09-08 23:30:41 | [diff] [blame] | 92 | // is expected to accommodate 53 bits (including the implied bit). |
avi | 4ec0dff | 2015-11-24 14:26:24 | [diff] [blame] | 93 | static_assert(std::numeric_limits<double>::radix == 2, |
| 94 | "otherwise use scalbn"); |
Avery Musbach | 92a30e38 | 2022-09-08 23:30:41 | [diff] [blame] | 95 | constexpr int kBits = std::numeric_limits<double>::digits; |
| 96 | return ldexp(bits & ((UINT64_C(1) << kBits) - 1u), -kBits); |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 97 | } |
| 98 | |
Avery Musbach | eff342b | 2022-10-06 18:36:07 | [diff] [blame] | 99 | float BitsToOpenEndedUnitIntervalF(uint64_t bits) { |
| 100 | // We try to get maximum precision by masking out as many bits as will fit |
| 101 | // in the target type's mantissa, and raising it to an appropriate power to |
| 102 | // produce output in the range [0, 1). For IEEE 754 floats, the mantissa is |
| 103 | // expected to accommodate 12 bits (including the implied bit). |
| 104 | static_assert(std::numeric_limits<float>::radix == 2, "otherwise use scalbn"); |
| 105 | constexpr int kBits = std::numeric_limits<float>::digits; |
| 106 | return ldexpf(bits & ((UINT64_C(1) << kBits) - 1u), -kBits); |
| 107 | } |
| 108 | |
Nico Weber | 0a3852a7 | 2015-10-29 20:42:58 | [diff] [blame] | 109 | uint64_t RandGenerator(uint64_t range) { |
[email protected] | 0173b96 | 2011-08-24 19:58:36 | [diff] [blame] | 110 | DCHECK_GT(range, 0u); |
[email protected] | af2e192b | 2011-05-30 17:39:09 | [diff] [blame] | 111 | // We must discard random results above this number, as they would |
| 112 | // make the random generator non-uniform (consider e.g. if |
[email protected] | 0173b96 | 2011-08-24 19:58:36 | [diff] [blame] | 113 | // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice |
| 114 | // as likely as a result of 3 or 4). |
Nico Weber | 0a3852a7 | 2015-10-29 20:42:58 | [diff] [blame] | 115 | uint64_t max_acceptable_value = |
| 116 | (std::numeric_limits<uint64_t>::max() / range) * range - 1; |
[email protected] | af2e192b | 2011-05-30 17:39:09 | [diff] [blame] | 117 | |
Nico Weber | 0a3852a7 | 2015-10-29 20:42:58 | [diff] [blame] | 118 | uint64_t value; |
[email protected] | af2e192b | 2011-05-30 17:39:09 | [diff] [blame] | 119 | do { |
| 120 | value = base::RandUint64(); |
[email protected] | 0173b96 | 2011-08-24 19:58:36 | [diff] [blame] | 121 | } while (value > max_acceptable_value); |
[email protected] | af2e192b | 2011-05-30 17:39:09 | [diff] [blame] | 122 | |
[email protected] | 0173b96 | 2011-08-24 19:58:36 | [diff] [blame] | 123 | return value % range; |
[email protected] | a74dcae | 2010-08-30 21:07:05 | [diff] [blame] | 124 | } |
| 125 | |
[email protected] | 51a0181 | 2011-05-05 08:46:11 | [diff] [blame] | 126 | std::string RandBytesAsString(size_t length) { |
Daniel Cheng | 58960231 | 2024-02-09 17:50:51 | [diff] [blame] | 127 | std::string result(length, '\0'); |
danakj | 95305d27 | 2024-05-09 20:38:44 | [diff] [blame] | 128 | RandBytes(as_writable_byte_span(result)); |
[email protected] | 29548d8 | 2011-04-29 21:03:54 | [diff] [blame] | 129 | return result; |
| 130 | } |
| 131 | |
Tom Sepez | 230a75c6 | 2023-11-13 23:27:16 | [diff] [blame] | 132 | std::vector<uint8_t> RandBytesAsVector(size_t length) { |
| 133 | std::vector<uint8_t> result(length); |
danakj | 95305d27 | 2024-05-09 20:38:44 | [diff] [blame] | 134 | RandBytes(result); |
Tom Sepez | 230a75c6 | 2023-11-13 23:27:16 | [diff] [blame] | 135 | return result; |
| 136 | } |
| 137 | |
Benoit Lize | 7532d4af | 2021-08-24 11:34:04 | [diff] [blame] | 138 | InsecureRandomGenerator::InsecureRandomGenerator() { |
Benoit Lize | 73de21b | 2021-07-02 08:17:56 | [diff] [blame] | 139 | a_ = base::RandUint64(); |
| 140 | b_ = base::RandUint64(); |
Benoit Lize | 73de21b | 2021-07-02 08:17:56 | [diff] [blame] | 141 | } |
| 142 | |
Benoit Lize | 7532d4af | 2021-08-24 11:34:04 | [diff] [blame] | 143 | void InsecureRandomGenerator::ReseedForTesting(uint64_t seed) { |
Benoit Lize | 73de21b | 2021-07-02 08:17:56 | [diff] [blame] | 144 | a_ = seed; |
| 145 | b_ = seed; |
Benoit Lize | 73de21b | 2021-07-02 08:17:56 | [diff] [blame] | 146 | } |
| 147 | |
Anthony Vallée-Dubois | 13bd8f6 | 2025-01-21 17:10:32 | [diff] [blame] | 148 | uint64_t InsecureRandomGenerator::RandUint64() const { |
Benoit Lize | 73de21b | 2021-07-02 08:17:56 | [diff] [blame] | 149 | // Using XorShift128+, which is simple and widely used. See |
| 150 | // https://en.wikipedia.org/wiki/Xorshift#xorshift+ for details. |
| 151 | uint64_t t = a_; |
| 152 | const uint64_t s = b_; |
| 153 | |
| 154 | a_ = s; |
| 155 | t ^= t << 23; |
| 156 | t ^= t >> 17; |
| 157 | t ^= s ^ (s >> 26); |
| 158 | b_ = t; |
| 159 | |
| 160 | return t + s; |
| 161 | } |
| 162 | |
Anthony Vallée-Dubois | 13bd8f6 | 2025-01-21 17:10:32 | [diff] [blame] | 163 | uint32_t InsecureRandomGenerator::RandUint32() const { |
Benoit Lize | 73de21b | 2021-07-02 08:17:56 | [diff] [blame] | 164 | // The generator usually returns an uint64_t, truncate it. |
| 165 | // |
| 166 | // It is noted in this paper (https://arxiv.org/abs/1810.05313) that the |
| 167 | // lowest 32 bits fail some statistical tests from the Big Crush |
| 168 | // suite. Use the higher ones instead. |
| 169 | return this->RandUint64() >> 32; |
| 170 | } |
| 171 | |
Anthony Vallée-Dubois | 13bd8f6 | 2025-01-21 17:10:32 | [diff] [blame] | 172 | double InsecureRandomGenerator::RandDouble() const { |
Benoit Lize | d637714 | 2021-07-05 10:17:16 | [diff] [blame] | 173 | uint64_t x = RandUint64(); |
| 174 | // From https://vigna.di.unimi.it/xorshift/. |
| 175 | // 53 bits of mantissa, hence the "hexadecimal exponent" 1p-53. |
| 176 | return (x >> 11) * 0x1.0p-53; |
| 177 | } |
| 178 | |
Benoit Lize | ee4fe1e4 | 2022-06-29 19:13:48 | [diff] [blame] | 179 | MetricsSubSampler::MetricsSubSampler() = default; |
Anthony Vallée-Dubois | 13bd8f6 | 2025-01-21 17:10:32 | [diff] [blame] | 180 | bool MetricsSubSampler::ShouldSample(double probability) const { |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 181 | if (g_subsampling_always_sample.load(std::memory_order_relaxed)) { |
Olivier Li | ef2b23c | 2024-01-29 20:58:56 | [diff] [blame] | 182 | return true; |
| 183 | } |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 184 | if (g_subsampling_never_sample.load(std::memory_order_relaxed)) { |
Olivier Li | ef2b23c | 2024-01-29 20:58:56 | [diff] [blame] | 185 | return false; |
| 186 | } |
| 187 | |
Anand Ravi | b793ffb3 | 2025-02-25 22:56:54 | [diff] [blame] | 188 | DCHECK(probability >= 0 && probability <= 1); |
Olivier Li | ef2b23c | 2024-01-29 20:58:56 | [diff] [blame] | 189 | return generator_.RandDouble() < probability; |
François Doray | 87e35a8 | 2023-05-05 14:44:28 | [diff] [blame] | 190 | } |
| 191 | |
Anthony Vallée-Dubois | ecfdb38 | 2025-02-04 20:07:40 | [diff] [blame] | 192 | void MetricsSubSampler::Reseed() { |
| 193 | generator_ = InsecureRandomGenerator(); |
| 194 | } |
| 195 | |
Olivier Li | ef2b23c | 2024-01-29 20:58:56 | [diff] [blame] | 196 | MetricsSubSampler::ScopedAlwaysSampleForTesting:: |
| 197 | ScopedAlwaysSampleForTesting() { |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 198 | DCHECK(!g_subsampling_always_sample.load(std::memory_order_relaxed)); |
| 199 | DCHECK(!g_subsampling_never_sample.load(std::memory_order_relaxed)); |
| 200 | g_subsampling_always_sample.store(true, std::memory_order_relaxed); |
François Doray | 87e35a8 | 2023-05-05 14:44:28 | [diff] [blame] | 201 | } |
| 202 | |
Olivier Li | ef2b23c | 2024-01-29 20:58:56 | [diff] [blame] | 203 | MetricsSubSampler::ScopedAlwaysSampleForTesting:: |
| 204 | ~ScopedAlwaysSampleForTesting() { |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 205 | DCHECK(g_subsampling_always_sample.load(std::memory_order_relaxed)); |
| 206 | DCHECK(!g_subsampling_never_sample.load(std::memory_order_relaxed)); |
| 207 | g_subsampling_always_sample.store(false, std::memory_order_relaxed); |
Olivier Li | ef2b23c | 2024-01-29 20:58:56 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | MetricsSubSampler::ScopedNeverSampleForTesting::ScopedNeverSampleForTesting() { |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 211 | DCHECK(!g_subsampling_always_sample.load(std::memory_order_relaxed)); |
| 212 | DCHECK(!g_subsampling_never_sample.load(std::memory_order_relaxed)); |
| 213 | g_subsampling_never_sample.store(true, std::memory_order_relaxed); |
Olivier Li | ef2b23c | 2024-01-29 20:58:56 | [diff] [blame] | 214 | } |
| 215 | |
| 216 | MetricsSubSampler::ScopedNeverSampleForTesting::~ScopedNeverSampleForTesting() { |
| 217 | DCHECK(!g_subsampling_always_sample); |
| 218 | DCHECK(g_subsampling_never_sample); |
Olivier Li Shing Tat-Dupuis | 9e5e7d9 | 2024-02-13 14:42:10 | [diff] [blame] | 219 | g_subsampling_never_sample.store(false, std::memory_order_relaxed); |
Benoit Lize | ee4fe1e4 | 2022-06-29 19:13:48 | [diff] [blame] | 220 | } |
| 221 | |
Anthony Vallée-Dubois | ecfdb38 | 2025-02-04 20:07:40 | [diff] [blame] | 222 | bool ShouldRecordSubsampledMetric(double probability) { |
| 223 | return GetSharedSubsampler()->ShouldSample(probability); |
| 224 | } |
| 225 | |
| 226 | void ReseedSharedMetricsSubsampler() { |
| 227 | GetSharedSubsampler()->Reseed(); |
| 228 | } |
| 229 | |
[email protected] | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 230 | } // namespace base |