1 /*
2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3  * Copyright (c) 2020, Datadog, Inc. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  *
24  */
25 
26 #include "precompiled.hpp"
27 
28 // This test performs mocking of certain JVM functionality. This works by
29 // including the source file under test inside an anonymous namespace (which
30 // prevents linking conflicts) with the mocked symbols redefined.
31 
32 // The include list should mirror the one found in the included source file -
33 // with the ones that should pick up the mocks removed. Those should be included
34 // later after the mocks have been defined.
35 
36 #include <cmath>
37 
38 #include "jfr/utilities/jfrAllocation.hpp"
39 #include "jfr/utilities/jfrRandom.inline.hpp"
40 #include "jfr/utilities/jfrSpinlockHelper.hpp"
41 #include "jfr/utilities/jfrTime.hpp"
42 #include "jfr/utilities/jfrTimeConverter.hpp"
43 #include "jfr/utilities/jfrTryLock.hpp"
44 #include "logging/log.hpp"
45 #include "runtime/atomic.hpp"
46 #include "utilities/globalDefinitions.hpp"
47 
48 #include "unittest.hpp"
49 
50 // #undef SHARE_JFR_SUPPORT_JFRADAPTIVESAMPLER_HPP
51 
52 namespace {
53   class MockJfrTimeConverter : public ::JfrTimeConverter {
54   public:
nano_to_counter_multiplier(bool is_os_time=false)55     static double nano_to_counter_multiplier(bool is_os_time = false) {
56       return 1.0;
57     }
counter_to_nanos(jlong c,bool is_os_time=false)58     static jlong counter_to_nanos(jlong c, bool is_os_time = false) {
59       return c;
60     }
counter_to_millis(jlong c,bool is_os_time=false)61     static jlong counter_to_millis(jlong c, bool is_os_time = false) {
62       return c * NANOS_PER_MILLISEC;
63     }
nanos_to_countertime(jlong c,bool as_os_time=false)64     static jlong nanos_to_countertime(jlong c, bool as_os_time = false) {
65       return c;
66     }
67   };
68 
69   class MockJfrTickValue {
70   private:
71     jlong _ticks;
72   public:
MockJfrTickValue(jlong ticks)73     MockJfrTickValue(jlong ticks) : _ticks(ticks) {};
value()74     jlong value() {
75       return _ticks;
76     }
77   };
78   class MockJfrTicks {
79   public:
80     static jlong tick;
now()81     static MockJfrTickValue now() {
82       return MockJfrTickValue(tick);
83     }
84   };
85 
86   jlong MockJfrTicks::tick = 0;
87 
88   // Reincluding source files in the anonymous namespace unfortunately seems to
89   // behave strangely with precompiled headers (only when using gcc though)
90 #ifndef DONT_USE_PRECOMPILED_HEADER
91 #define DONT_USE_PRECOMPILED_HEADER
92 #endif
93 
94 #define JfrTicks MockJfrTicks
95 #define JfrTimeConverter MockJfrTimeConverter
96 
97 #include "jfr/support/jfrAdaptiveSampler.hpp"
98 #include "jfr/support/jfrAdaptiveSampler.cpp"
99 
100 #undef JfrTimeConverter
101 #undef JfrTicks
102 } // anonymous namespace
103 
104 class JfrGTestAdaptiveSampling : public ::testing::Test {
105  protected:
106   const int max_population_per_window = 2000;
107   const int min_population_per_window = 2;
108   const int window_count = 10000;
109   const clock_t window_duration_ms = 100;
110   const size_t expected_sample_points_per_window = 50;
111   const size_t expected_sample_points = expected_sample_points_per_window * (size_t)window_count;
112   const size_t window_lookback_count = 50; // 50 windows == 5 seconds (for a window duration of 100 ms)
113   const double max_sample_bias = 0.11;
114 
SetUp()115   void SetUp() {
116     // Ensure that tests are separated in time by spreading them by 24hrs apart
117     MockJfrTicks::tick += (24 * 60 * 60) * NANOSECS_PER_SEC;
118   }
119 
TearDown()120   void TearDown() {
121     // nothing
122   }
123 
assertDistributionProperties(int distr_slots,jlong * population,jlong * sample,size_t population_size,size_t sample_size,const char * msg)124   void assertDistributionProperties(int distr_slots, jlong* population, jlong* sample, size_t population_size, size_t sample_size, const char* msg) {
125     size_t population_sum = 0;
126     size_t sample_sum = 0;
127     for (int i = 0; i < distr_slots; i++) {
128       population_sum += i * population[i];
129       sample_sum += i * sample[i];
130     }
131 
132     double population_mean = population_sum / (double)population_size;
133     double sample_mean = sample_sum / (double)sample_size;
134 
135     double population_variance = 0;
136     double sample_variance = 0;
137     for (int i = 0; i < distr_slots; i++) {
138       double population_diff = i - population_mean;
139       population_variance = population[i] * population_diff * population_diff;
140 
141       double sample_diff = i - sample_mean;
142       sample_variance = sample[i] * sample_diff * sample_diff;
143     }
144     population_variance = population_variance / (population_size - 1);
145     sample_variance = sample_variance / (sample_size - 1);
146     double population_stdev = sqrt(population_variance);
147     double sample_stdev = sqrt(sample_variance);
148 
149     // make sure the standard deviation is ok
150     EXPECT_NEAR(population_stdev, sample_stdev, 0.5) << msg;
151     // make sure that the subsampled set mean is within 2-sigma of the original set mean
152     EXPECT_NEAR(population_mean, sample_mean, population_stdev) << msg;
153     // make sure that the original set mean is within 2-sigma of the subsampled set mean
154     EXPECT_NEAR(sample_mean, population_mean, sample_stdev) << msg;
155   }
156 
157   typedef size_t(JfrGTestAdaptiveSampling::* incoming)() const;
158   void test(incoming inc, size_t events_per_window, double expectation, const char* description);
159 
160  public:
incoming_uniform() const161   size_t incoming_uniform() const {
162     return os::random() % max_population_per_window + min_population_per_window;
163   }
164 
incoming_bursty_10_percent() const165   size_t incoming_bursty_10_percent() const {
166     bool is_burst = (os::random() % 100) < 10; // 10% burst chance
167     return is_burst ? max_population_per_window : min_population_per_window;
168   }
169 
incoming_bursty_90_percent() const170   size_t incoming_bursty_90_percent() const {
171     bool is_burst = (os::random() % 100) < 90; // 90% burst chance
172     return is_burst ? max_population_per_window : min_population_per_window;
173   }
174 
incoming_low_rate() const175   size_t incoming_low_rate() const {
176     return min_population_per_window;
177   }
178 
incoming_high_rate() const179   size_t incoming_high_rate() const {
180     return max_population_per_window;
181   }
182 
incoming_burst_eval(size_t & count,size_t mod_value) const183   size_t incoming_burst_eval(size_t& count, size_t mod_value) const {
184     return count++ % 10 == mod_value ? max_population_per_window : 0;
185   }
186 
incoming_early_burst() const187   size_t incoming_early_burst() const {
188     static size_t count = 1;
189     return incoming_burst_eval(count, 1);
190   }
191 
incoming_mid_burst() const192   size_t incoming_mid_burst() const {
193     static size_t count = 1;
194     return incoming_burst_eval(count, 5);
195   }
196 
incoming_late_burst() const197   size_t incoming_late_burst() const {
198     static size_t count = 1;
199     return incoming_burst_eval(count, 0);
200   }
201 };
202 
test(JfrGTestAdaptiveSampling::incoming inc,size_t sample_points_per_window,double error_factor,const char * const description)203 void JfrGTestAdaptiveSampling::test(JfrGTestAdaptiveSampling::incoming inc, size_t sample_points_per_window, double error_factor, const char* const description) {
204   assert(description != NULL, "invariant");
205   char output[1024] = "Adaptive sampling: ";
206   strcat(output, description);
207   fprintf(stdout, "=== %s\n", output);
208   jlong population[100] = { 0 };
209   jlong sample[100] = { 0 };
210   ::JfrGTestFixedRateSampler sampler = ::JfrGTestFixedRateSampler(expected_sample_points_per_window, window_duration_ms, window_lookback_count);
211   EXPECT_TRUE(sampler.initialize());
212 
213   size_t population_size = 0;
214   size_t sample_size = 0;
215   for (int t = 0; t < window_count; t++) {
216     const size_t incoming_events = (this->*inc)();
217     for (size_t i = 0; i < incoming_events; i++) {
218       ++population_size;
219       size_t index = os::random() % 100;
220       population[index] += 1;
221       if (sampler.sample()) {
222         ++sample_size;
223         sample[index] += 1;
224       }
225     }
226     MockJfrTicks::tick += window_duration_ms * NANOSECS_PER_MILLISEC + 1;
227     sampler.sample(); // window rotation
228   }
229 
230   const size_t target_sample_size = sample_points_per_window * window_count;
231   EXPECT_NEAR(target_sample_size, sample_size, expected_sample_points * error_factor) << output;
232   strcat(output, ", hit distribution");
233   assertDistributionProperties(100, population, sample, population_size, sample_size, output);
234 }
235 
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_uniform_rate)236 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_uniform_rate) {
237   test(&JfrGTestAdaptiveSampling::incoming_uniform, expected_sample_points_per_window, 0.05, "random uniform, all samples");
238 }
239 
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_low_rate)240 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_low_rate) {
241   test(&JfrGTestAdaptiveSampling::incoming_low_rate, min_population_per_window, 0.05, "low rate");
242 }
243 
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_high_rate)244 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_high_rate) {
245   test(&JfrGTestAdaptiveSampling::incoming_high_rate, expected_sample_points_per_window, 0.02, "high rate");
246 }
247 
248 // We can think of the windows as splitting up a time period, for example a second (window_duration_ms = 100)
249 // The burst tests for early, mid and late apply a burst rate at a selected window, with other windows having no incoming input.
250 //
251 // - early during the first window of a new time period
252 // - mid   during the middle window of a new time period
253 // - late  during the last window of a new time period
254 //
255 // The tests verify the total sample size correspond to the selected bursts:
256 //
257 // - early start of a second -> each second will have sampled the window set point for a single window only since no debt has accumulated into the new time period.
258 // - mid   middle of the second -> each second will have sampled the window set point + accumulated debt for the first 4 windows.
259 // - late end of the second -> each second will have sampled the window set point + accumulated debt for the first 9 windows (i.e. it will have sampled all)
260 //
261 
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_early_burst)262 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_early_burst) {
263   test(&JfrGTestAdaptiveSampling::incoming_early_burst, expected_sample_points_per_window, 0.9, "early burst");
264 }
265 
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_mid_burst)266 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_mid_burst) {
267   test(&JfrGTestAdaptiveSampling::incoming_mid_burst, expected_sample_points_per_window, 0.5, "mid burst");
268 }
269 
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_late_burst)270 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_late_burst) {
271   test(&JfrGTestAdaptiveSampling::incoming_late_burst, expected_sample_points_per_window, 0.0, "late burst");
272 }
273 
274 // These are randomized burst tests
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_bursty_rate_10_percent)275 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_bursty_rate_10_percent) {
276   test(&JfrGTestAdaptiveSampling::incoming_bursty_10_percent, expected_sample_points_per_window, 0.96, "bursty 10%");
277 }
278 
TEST_VM_F(JfrGTestAdaptiveSampling,DISABLED_bursty_rate_90_percent)279 TEST_VM_F(JfrGTestAdaptiveSampling, DISABLED_bursty_rate_90_percent) {
280   test(&JfrGTestAdaptiveSampling::incoming_bursty_10_percent, expected_sample_points_per_window, 0.96, "bursty 90%");
281 }
282