Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2011 The Chromium Authors |
mark@chromium.org | 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> |
mark@chromium.org | 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> |
mark@chromium.org | 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" |
mark@chromium.org | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 19 | |
mark@chromium.org | 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_localMetricsSubSampler 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_tRandUint64(){ |
| 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 | |
mark@chromium.org | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 44 | intRandInt(int min,int max){ |
kushi.p@gmail.com | d7a93ad | 2011-04-22 13:13:07 | [diff] [blame] | 45 | DCHECK_LE(min, max); |
mark@chromium.org | 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))); |
kushi.p@gmail.com | e1be56d | 2011-05-04 01:29:38 | [diff] [blame] | 52 | DCHECK_GE(result, min); |
| 53 | DCHECK_LE(result, max); |
mark@chromium.org | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 54 | return result; |
| 55 | } |
| 56 | |
| 57 | doubleRandDouble(){ |
joi@chromium.org | edafd4c | 2011-05-10 17:18:53 | [diff] [blame] | 58 | returnBitsToOpenEndedUnitInterval(base::RandUint64()); |
| 59 | } |
| 60 | |
Avery Musbach | eff342b | 2022-10-06 18:36:07 | [diff] [blame] | 61 | floatRandFloat(){ |
| 62 | returnBitsToOpenEndedUnitIntervalF(base::RandUint64()); |
| 63 | } |
| 64 | |
Peter Kasting | b2dc5504 | 2025-01-16 16:30:54 | [diff] [blame] | 65 | boolRandBool(){ |
| 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 | TimeDeltaRandTimeDelta(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 | constint64_t range=(limit- start).InMicroseconds(); |
| 78 | // Because of the `CHECK_LT()` above, range > 0, so this cast is safe. |
| 79 | constuint64_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 | TimeDeltaRandTimeDeltaUpTo(TimeDelta limit){ |
| 85 | returnRandTimeDelta(TimeDelta(), limit); |
| 86 | } |
| 87 | |
Nico Weber | 0a3852a7 | 2015-10-29 20:42:58 | [diff] [blame] | 88 | doubleBitsToOpenEndedUnitInterval(uint64_t bits){ |
mark@chromium.org | 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 | constexprint kBits= std::numeric_limits<double>::digits; |
| 96 | return ldexp(bits&((UINT64_C(1)<< kBits)-1u),-kBits); |
mark@chromium.org | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 97 | } |
| 98 | |
Avery Musbach | eff342b | 2022-10-06 18:36:07 | [diff] [blame] | 99 | floatBitsToOpenEndedUnitIntervalF(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 | constexprint 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_tRandGenerator(uint64_t range){ |
dilmah@chromium.org | 0173b96 | 2011-08-24 19:58:36 | [diff] [blame] | 110 | DCHECK_GT(range,0u); |
joi@chromium.org | 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 |
dilmah@chromium.org | 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; |
joi@chromium.org | 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; |
joi@chromium.org | af2e192b | 2011-05-30 17:39:09 | [diff] [blame] | 119 | do{ |
| 120 | value= base::RandUint64(); |
dilmah@chromium.org | 0173b96 | 2011-08-24 19:58:36 | [diff] [blame] | 121 | }while(value> max_acceptable_value); |
joi@chromium.org | af2e192b | 2011-05-30 17:39:09 | [diff] [blame] | 122 | |
dilmah@chromium.org | 0173b96 | 2011-08-24 19:58:36 | [diff] [blame] | 123 | return value% range; |
isherman@chromium.org | a74dcae | 2010-08-30 21:07:05 | [diff] [blame] | 124 | } |
| 125 | |
qsr@google.com | 51a0181 | 2011-05-05 08:46:11 | [diff] [blame] | 126 | std::stringRandBytesAsString(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)); |
abarth@chromium.org | 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 | voidInsecureRandomGenerator::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_tInsecureRandomGenerator::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 | constuint64_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_tInsecureRandomGenerator::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 | returnthis->RandUint64()>>32; |
| 170 | } |
| 171 | |
Anthony Vallée-Dubois | 13bd8f6 | 2025-01-21 17:10:32 | [diff] [blame] | 172 | doubleInsecureRandomGenerator::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 | boolMetricsSubSampler::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 | returntrue; |
| 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 | returnfalse; |
| 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 | voidMetricsSubSampler::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 | boolShouldRecordSubsampledMetric(double probability){ |
| 223 | returnGetSharedSubsampler()->ShouldSample(probability); |
| 224 | } |
| 225 | |
| 226 | voidReseedSharedMetricsSubsampler(){ |
| 227 | GetSharedSubsampler()->Reseed(); |
| 228 | } |
| 229 | |
mark@chromium.org | 05f9b68 | 2008-09-29 22:18:01 | [diff] [blame] | 230 | }// namespace base |