// Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "absl/strings/internal/cordz_functions.h" #include // NOLINT we need real clean new threads #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/config.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { namespace { using ::testing::Eq; using ::testing::Ge; using ::testing::Le; TEST(CordzFunctionsTest, SampleRate) { int32_t orig_sample_rate = get_cordz_mean_interval(); int32_t expected_sample_rate = 123; set_cordz_mean_interval(expected_sample_rate); EXPECT_THAT(get_cordz_mean_interval(), Eq(expected_sample_rate)); set_cordz_mean_interval(orig_sample_rate); } // Cordz is disabled when we don't have thread_local. All calls to // should_profile will return false when cordz is disabled, so we might want to // avoid those tests. #ifdef ABSL_INTERNAL_CORDZ_ENABLED TEST(CordzFunctionsTest, ShouldProfileDisable) { int32_t orig_sample_rate = get_cordz_mean_interval(); set_cordz_mean_interval(0); cordz_set_next_sample_for_testing(0); EXPECT_EQ(cordz_should_profile(), 0); // 1 << 16 is from kIntervalIfDisabled in cordz_functions.cc. EXPECT_THAT(cordz_next_sample.next_sample, Eq(1 << 16)); set_cordz_mean_interval(orig_sample_rate); } TEST(CordzFunctionsTest, ShouldProfileAlways) { int32_t orig_sample_rate = get_cordz_mean_interval(); set_cordz_mean_interval(1); cordz_set_next_sample_for_testing(1); EXPECT_GT(cordz_should_profile(), 0); EXPECT_THAT(cordz_next_sample.next_sample, Le(1)); set_cordz_mean_interval(orig_sample_rate); } TEST(CordzFunctionsTest, DoesNotAlwaysSampleFirstCord) { // Set large enough interval such that the chance of 'tons' of threads // randomly sampling the first call is infinitely small. set_cordz_mean_interval(10000); int tries = 0; bool sampled = false; do { ++tries; ASSERT_THAT(tries, Le(1000)); std::thread thread([&sampled] { sampled = cordz_should_profile() > 0; }); thread.join(); } while (sampled); } TEST(CordzFunctionsTest, ShouldProfileRate) { static constexpr int kDesiredMeanInterval = 1000; static constexpr int kSamples = 10000; int32_t orig_sample_rate = get_cordz_mean_interval(); set_cordz_mean_interval(kDesiredMeanInterval); int64_t sum_of_intervals = 0; for (int i = 0; i < kSamples; i++) { // Setting next_sample to 0 will force cordz_should_profile to generate a // new value for next_sample each iteration. cordz_set_next_sample_for_testing(0); cordz_should_profile(); sum_of_intervals += cordz_next_sample.next_sample; } // The sum of independent exponential variables is an Erlang distribution, // which is a gamma distribution where the shape parameter is equal to the // number of summands. The distribution used for cordz_should_profile is // actually floor(Exponential(1/mean)) which introduces bias. However, we can // apply the squint-really-hard correction factor. That is, when mean is // large, then if we squint really hard the shape of the distribution between // N and N+1 looks like a uniform distribution. On average, each value for // next_sample will be about 0.5 lower than we would expect from an // exponential distribution. This squint-really-hard correction approach won't // work when mean is smaller than about 10 but works fine when mean is 1000. // // We can use R to calculate a confidence interval. This // shows how to generate a confidence interval with a false positive rate of // one in a billion. // // $ R -q // > mean = 1000 // > kSamples = 10000 // > errorRate = 1e-9 // > correction = -kSamples / 2 // > low = qgamma(errorRate/2, kSamples, 1/mean) + correction // > high = qgamma(1 - errorRate/2, kSamples, 1/mean) + correction // > low // [1] 9396115 // > high // [1] 10618100 EXPECT_THAT(sum_of_intervals, Ge(9396115)); EXPECT_THAT(sum_of_intervals, Le(10618100)); set_cordz_mean_interval(orig_sample_rate); } #else // ABSL_INTERNAL_CORDZ_ENABLED TEST(CordzFunctionsTest, ShouldProfileDisabled) { int32_t orig_sample_rate = get_cordz_mean_interval(); set_cordz_mean_interval(1); cordz_set_next_sample_for_testing(0); EXPECT_FALSE(cordz_should_profile()); set_cordz_mean_interval(orig_sample_rate); } #endif // ABSL_INTERNAL_CORDZ_ENABLED } // namespace } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl