xref: /aosp_15_r20/external/cronet/base/time/time_win_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/time/time.h"
6 
7 #include <windows.h>
8 
9 #include <mmsystem.h>
10 #include <process.h>
11 #include <stdint.h>
12 #include <windows.foundation.h>
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <limits>
17 #include <vector>
18 
19 #include "base/strings/string_piece.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/win/registry.h"
22 #include "build/build_config.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 
25 namespace base {
26 namespace {
27 
28 // For TimeDelta::ConstexprInitialization
29 constexpr int kExpectedDeltaInMilliseconds = 10;
30 constexpr TimeDelta kConstexprTimeDelta =
31     Milliseconds(kExpectedDeltaInMilliseconds);
32 
33 class MockTimeTicks : public TimeTicks {
34  public:
Ticker()35   static DWORD Ticker() {
36     return static_cast<int>(InterlockedIncrement(&ticker_));
37   }
38 
InstallTicker()39   static void InstallTicker() {
40     old_tick_function_ = SetMockTickFunction(&Ticker);
41     ticker_ = -5;
42   }
43 
UninstallTicker()44   static void UninstallTicker() { SetMockTickFunction(old_tick_function_); }
45 
46  private:
47   static volatile LONG ticker_;
48   static TickFunctionType old_tick_function_;
49 };
50 
51 volatile LONG MockTimeTicks::ticker_;
52 MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_;
53 
54 HANDLE g_rollover_test_start;
55 
RolloverTestThreadMain(void * param)56 unsigned __stdcall RolloverTestThreadMain(void* param) {
57   int64_t counter = reinterpret_cast<int64_t>(param);
58   DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
59   EXPECT_EQ(rv, WAIT_OBJECT_0);
60 
61   TimeTicks last = TimeTicks::Now();
62   for (int index = 0; index < counter; index++) {
63     TimeTicks now = TimeTicks::Now();
64     int64_t milliseconds = (now - last).InMilliseconds();
65     // This is a tight loop; we could have looped faster than our
66     // measurements, so the time might be 0 millis.
67     EXPECT_GE(milliseconds, 0);
68     EXPECT_LT(milliseconds, 250);
69     last = now;
70   }
71   return 0;
72 }
73 
74 #if defined(_M_ARM64) && defined(__clang__)
75 #define ReadCycleCounter() _ReadStatusReg(ARM64_PMCCNTR_EL0)
76 #else
77 #define ReadCycleCounter() __rdtsc()
78 #endif
79 
80 // Measure the performance of the CPU cycle counter so that we can compare it to
81 // the overhead of QueryPerformanceCounter. A hard-coded frequency is used
82 // because we don't care about the accuracy of the results, we just need to do
83 // the work. The amount of work is not exactly the same as in TimeTicks::Now
84 // (some steps are skipped) but that doesn't seem to materially affect the
85 // results.
GetTSC()86 TimeTicks GetTSC() {
87   // Using a fake cycle counter frequency for test purposes.
88   return TimeTicks() + Microseconds(ReadCycleCounter() *
89                                     Time::kMicrosecondsPerSecond / 10000000);
90 }
91 
92 }  // namespace
93 
94 // This test spawns many threads, and can occasionally fail due to resource
95 // exhaustion in the presence of ASan.
96 #if defined(ADDRESS_SANITIZER)
97 #define MAYBE_WinRollover DISABLED_WinRollover
98 #else
99 #define MAYBE_WinRollover WinRollover
100 #endif
TEST(TimeTicks,MAYBE_WinRollover)101 TEST(TimeTicks, MAYBE_WinRollover) {
102   // The internal counter rolls over at ~49days.  We'll use a mock
103   // timer to test this case.
104   // Basic test algorithm:
105   //   1) Set clock to rollover - N
106   //   2) Create N threads
107   //   3) Start the threads
108   //   4) Each thread loops through TimeTicks() N times
109   //   5) Each thread verifies integrity of result.
110 
111   const int kThreads = 8;
112   // Use int64_t so we can cast into a void* without a compiler warning.
113   const int64_t kChecks = 10;
114 
115   // It takes a lot of iterations to reproduce the bug!
116   // (See bug 1081395)
117   for (int loop = 0; loop < 4096; loop++) {
118     // Setup
119     MockTimeTicks::InstallTicker();
120     g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
121     HANDLE threads[kThreads];
122 
123     for (int index = 0; index < kThreads; index++) {
124       void* argument = reinterpret_cast<void*>(kChecks);
125       unsigned thread_id;
126       threads[index] = reinterpret_cast<HANDLE>(_beginthreadex(
127           NULL, 0, RolloverTestThreadMain, argument, 0, &thread_id));
128       EXPECT_NE((HANDLE)NULL, threads[index]);
129     }
130 
131     // Start!
132     SetEvent(g_rollover_test_start);
133 
134     // Wait for threads to finish
135     for (int index = 0; index < kThreads; index++) {
136       DWORD rv = WaitForSingleObject(threads[index], INFINITE);
137       EXPECT_EQ(rv, WAIT_OBJECT_0);
138       // Since using _beginthreadex() (as opposed to _beginthread),
139       // an explicit CloseHandle() is supposed to be called.
140       CloseHandle(threads[index]);
141     }
142 
143     CloseHandle(g_rollover_test_start);
144 
145     // Teardown
146     MockTimeTicks::UninstallTicker();
147   }
148 }
149 
TEST(TimeTicks,SubMillisecondTimers)150 TEST(TimeTicks, SubMillisecondTimers) {
151   // IsHighResolution() is false on some systems.  Since the product still works
152   // even if it's false, it makes this entire test questionable.
153   if (!TimeTicks::IsHighResolution())
154     return;
155 
156   // Run kRetries attempts to see a sub-millisecond timer.
157   constexpr int kRetries = 1000;
158   for (int index = 0; index < kRetries; index++) {
159     const TimeTicks start_time = TimeTicks::Now();
160     TimeDelta delta;
161     // Spin until the clock has detected a change.
162     do {
163       delta = TimeTicks::Now() - start_time;
164     } while (delta.is_zero());
165     if (!delta.InMilliseconds())
166       return;
167   }
168   ADD_FAILURE() << "Never saw a sub-millisecond timer.";
169 }
170 
TEST(TimeTicks,TimeGetTimeCaps)171 TEST(TimeTicks, TimeGetTimeCaps) {
172   // Test some basic assumptions that we expect about how timeGetDevCaps works.
173 
174   TIMECAPS caps;
175   MMRESULT status = timeGetDevCaps(&caps, sizeof(caps));
176   ASSERT_EQ(static_cast<MMRESULT>(MMSYSERR_NOERROR), status);
177 
178   EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
179   EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
180   EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
181   EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
182   printf("timeGetTime range is %d to %dms\n", caps.wPeriodMin, caps.wPeriodMax);
183 }
184 
TEST(TimeTicks,QueryPerformanceFrequency)185 TEST(TimeTicks, QueryPerformanceFrequency) {
186   // Test some basic assumptions that we expect about QPC.
187 
188   LARGE_INTEGER frequency;
189   BOOL rv = QueryPerformanceFrequency(&frequency);
190   EXPECT_EQ(TRUE, rv);
191   EXPECT_GT(frequency.QuadPart, 1000000);  // Expect at least 1MHz
192   printf("QueryPerformanceFrequency is %5.2fMHz\n",
193          frequency.QuadPart / 1000000.0);
194 }
195 
TEST(TimeTicks,TimerPerformance)196 TEST(TimeTicks, TimerPerformance) {
197   // Verify that various timer mechanisms can always complete quickly.
198   // Note:  This is a somewhat arbitrary test.
199   const int kLoops = 500000;
200 
201   typedef TimeTicks (*TestFunc)();
202   struct TestCase {
203     TestFunc func;
204     const char* description;
205   };
206   // Cheating a bit here:  assumes sizeof(TimeTicks) == sizeof(Time)
207   // in order to create a single test case list.
208   static_assert(sizeof(TimeTicks) == sizeof(Time),
209                 "TimeTicks and Time must be the same size");
210   std::vector<TestCase> cases;
211   cases.push_back({reinterpret_cast<TestFunc>(&Time::Now), "Time::Now"});
212   cases.push_back({&TimeTicks::Now, "TimeTicks::Now"});
213   cases.push_back({&GetTSC, "CPUCycleCounter"});
214 
215   if (ThreadTicks::IsSupported()) {
216     ThreadTicks::WaitUntilInitialized();
217     cases.push_back(
218         {reinterpret_cast<TestFunc>(&ThreadTicks::Now), "ThreadTicks::Now"});
219   }
220 
221   // Warm up the CPU to its full clock rate so that we get accurate timing
222   // information.
223   DWORD start_tick = GetTickCount();
224   const DWORD kWarmupMs = 50;
225   for (;;) {
226     DWORD elapsed = GetTickCount() - start_tick;
227     if (elapsed > kWarmupMs)
228       break;
229   }
230 
231   for (const auto& test_case : cases) {
232     TimeTicks start = TimeTicks::Now();
233     for (int index = 0; index < kLoops; index++)
234       test_case.func();
235     TimeTicks stop = TimeTicks::Now();
236     // Turning off the check for acceptible delays.  Without this check,
237     // the test really doesn't do much other than measure.  But the
238     // measurements are still useful for testing timers on various platforms.
239     // The reason to remove the check is because the tests run on many
240     // buildbots, some of which are VMs.  These machines can run horribly
241     // slow, and there is really no value for checking against a max timer.
242     // const int kMaxTime = 35;  // Maximum acceptible milliseconds for test.
243     // EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
244     printf("%s: %1.2fus per call\n", test_case.description,
245            (stop - start).InMillisecondsF() * 1000 / kLoops);
246   }
247 }
248 
249 #if !defined(ARCH_CPU_ARM64)
250 // This test is disabled on Windows ARM64 systems because TSCTicksPerSecond is
251 // only used in Chromium for QueryThreadCycleTime, and QueryThreadCycleTime
252 // doesn't use a constant-rate timer on ARM64.
TEST(TimeTicks,TSCTicksPerSecond)253 TEST(TimeTicks, TSCTicksPerSecond) {
254   if (time_internal::HasConstantRateTSC()) {
255     ThreadTicks::WaitUntilInitialized();
256 
257     // Read the CPU frequency from the registry.
258     base::win::RegKey processor_key(
259         HKEY_LOCAL_MACHINE,
260         L"Hardware\\Description\\System\\CentralProcessor\\0", KEY_QUERY_VALUE);
261     ASSERT_TRUE(processor_key.Valid());
262     DWORD processor_mhz_from_registry;
263     ASSERT_EQ(ERROR_SUCCESS,
264               processor_key.ReadValueDW(L"~MHz", &processor_mhz_from_registry));
265 
266     // Expect the measured TSC frequency to be similar to the processor
267     // frequency from the registry (0.5% error).
268     double tsc_mhz_measured = time_internal::TSCTicksPerSecond() / 1e6;
269     EXPECT_NEAR(tsc_mhz_measured, processor_mhz_from_registry,
270                 0.005 * processor_mhz_from_registry);
271   }
272 }
273 #endif
274 
TEST(TimeTicks,FromQPCValue)275 TEST(TimeTicks, FromQPCValue) {
276   if (!TimeTicks::IsHighResolution())
277     return;
278 
279   LARGE_INTEGER frequency;
280   ASSERT_TRUE(QueryPerformanceFrequency(&frequency));
281   const int64_t ticks_per_second = frequency.QuadPart;
282   ASSERT_GT(ticks_per_second, 0);
283 
284   // Generate the tick values to convert, advancing the tick count by varying
285   // amounts.  These values will ensure that both the fast and overflow-safe
286   // conversion logic in FromQPCValue() is tested, and across the entire range
287   // of possible QPC tick values.
288   std::vector<int64_t> test_cases;
289   test_cases.push_back(0);
290 
291   // Build the test cases.
292   {
293     const int kNumAdvancements = 100;
294     int64_t ticks = 0;
295     int64_t ticks_increment = 10;
296     for (int i = 0; i < kNumAdvancements; ++i) {
297       test_cases.push_back(ticks);
298       ticks += ticks_increment;
299       ticks_increment = ticks_increment * 6 / 5;
300     }
301     test_cases.push_back(Time::kQPCOverflowThreshold - 1);
302     test_cases.push_back(Time::kQPCOverflowThreshold);
303     test_cases.push_back(Time::kQPCOverflowThreshold + 1);
304     ticks = Time::kQPCOverflowThreshold + 10;
305     ticks_increment = 10;
306     for (int i = 0; i < kNumAdvancements; ++i) {
307       test_cases.push_back(ticks);
308       ticks += ticks_increment;
309       ticks_increment = ticks_increment * 6 / 5;
310     }
311     test_cases.push_back(std::numeric_limits<int64_t>::max());
312   }
313 
314   // Test that the conversions using FromQPCValue() match those computed here
315   // using simple floating-point arithmetic.  The floating-point math provides
316   // enough precision for all reasonable values to confirm that the
317   // implementation is correct to the microsecond, and for "very large" values
318   // it confirms that the answer is very close to correct.
319   for (int64_t ticks : test_cases) {
320     const double expected_microseconds_since_origin =
321         (static_cast<double>(ticks) * Time::kMicrosecondsPerSecond) /
322         ticks_per_second;
323     const TimeTicks converted_value = TimeTicks::FromQPCValue(ticks);
324     const double converted_microseconds_since_origin =
325         (converted_value - TimeTicks()).InMicrosecondsF();
326     // When we test with very large numbers we end up in a range where adjacent
327     // double values are far apart - 512.0 apart in one test failure. In that
328     // situation it makes no sense for our epsilon to be 1.0 - it should be
329     // the difference between adjacent doubles.
330     double epsilon = nextafter(expected_microseconds_since_origin, INFINITY) -
331                      expected_microseconds_since_origin;
332     // Epsilon must be at least 1.0 because converted_microseconds_since_origin
333     // comes from an integral value, and expected_microseconds_since_origin is
334     // a double that is expected to be up to 0.999 larger. In addition, due to
335     // multiple roundings in the double calculation the actual error can be
336     // slightly larger than 1.0, even when the converted value is perfect. This
337     // epsilon value was chosen because it is slightly larger than the error
338     // seen in a test failure caused by the double rounding.
339     epsilon = std::max(epsilon, 1.002);
340     EXPECT_NEAR(expected_microseconds_since_origin,
341                 converted_microseconds_since_origin, epsilon)
342         << "ticks=" << ticks << ", to be converted via logic path: "
343         << (ticks < Time::kQPCOverflowThreshold ? "FAST" : "SAFE");
344   }
345 }
346 
TEST(TimeDelta,ConstexprInitialization)347 TEST(TimeDelta, ConstexprInitialization) {
348   // Make sure that TimeDelta works around crbug.com/635974
349   EXPECT_EQ(kExpectedDeltaInMilliseconds, kConstexprTimeDelta.InMilliseconds());
350 }
351 
TEST(TimeDelta,FromFileTime)352 TEST(TimeDelta, FromFileTime) {
353   FILETIME ft;
354   ft.dwLowDateTime = 1001;
355   ft.dwHighDateTime = 0;
356 
357   // 100100 ns ~= 100 us.
358   EXPECT_EQ(Microseconds(100), TimeDelta::FromFileTime(ft));
359 
360   ft.dwLowDateTime = 0;
361   ft.dwHighDateTime = 1;
362 
363   // 2^32 * 100 ns ~= 2^32 * 10 us.
364   EXPECT_EQ(Microseconds((1ull << 32) / 10), TimeDelta::FromFileTime(ft));
365 }
366 
TEST(TimeDelta,FromWinrtDateTime)367 TEST(TimeDelta, FromWinrtDateTime) {
368   ABI::Windows::Foundation::DateTime dt;
369   dt.UniversalTime = 0;
370 
371   // 0 UniversalTime = no delta since epoch.
372   EXPECT_EQ(TimeDelta(), TimeDelta::FromWinrtDateTime(dt));
373 
374   dt.UniversalTime = 101;
375 
376   // 101 * 100 ns ~= 10.1 microseconds.
377   EXPECT_EQ(Microseconds(10.1), TimeDelta::FromWinrtDateTime(dt));
378 }
379 
TEST(TimeDelta,ToWinrtDateTime)380 TEST(TimeDelta, ToWinrtDateTime) {
381   auto time_delta = Seconds(0);
382 
383   // No delta since epoch = 0 DateTime.
384   EXPECT_EQ(0, time_delta.ToWinrtDateTime().UniversalTime);
385 
386   time_delta = Microseconds(10);
387 
388   // 10 microseconds = 100 * 100 ns.
389   EXPECT_EQ(100, time_delta.ToWinrtDateTime().UniversalTime);
390 }
391 
TEST(TimeDelta,FromWinrtTimeSpan)392 TEST(TimeDelta, FromWinrtTimeSpan) {
393   ABI::Windows::Foundation::TimeSpan ts;
394   ts.Duration = 0;
395 
396   // 0.
397   EXPECT_EQ(TimeDelta(), TimeDelta::FromWinrtTimeSpan(ts));
398 
399   ts.Duration = 101;
400 
401   // 101 * 100 ns ~= 10.1 microseconds.
402   EXPECT_EQ(Microseconds(10.1), TimeDelta::FromWinrtTimeSpan(ts));
403 }
404 
TEST(TimeDelta,ToWinrtTimeSpan)405 TEST(TimeDelta, ToWinrtTimeSpan) {
406   auto time_delta = Seconds(0);
407 
408   // 0.
409   EXPECT_EQ(0, time_delta.ToWinrtTimeSpan().Duration);
410 
411   time_delta = Microseconds(10);
412 
413   // 10 microseconds = 100 * 100 ns.
414   EXPECT_EQ(100, time_delta.ToWinrtTimeSpan().Duration);
415 }
416 
TEST(HighResolutionTimer,GetUsage)417 TEST(HighResolutionTimer, GetUsage) {
418   Time::ResetHighResolutionTimerUsage();
419 
420   // 0% usage since the timer isn't activated regardless of how much time has
421   // elapsed.
422   EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
423   Sleep(10);
424   EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
425 
426   Time::ActivateHighResolutionTimer(true);
427   Time::ResetHighResolutionTimerUsage();
428 
429   Sleep(20);
430   // 100% usage since the timer has been activated entire time.
431   EXPECT_EQ(100.0, Time::GetHighResolutionTimerUsage());
432 
433   Time::ActivateHighResolutionTimer(false);
434   Sleep(20);
435   double usage1 = Time::GetHighResolutionTimerUsage();
436   // usage1 should be about 50%.
437   EXPECT_LT(usage1, 100.0);
438   EXPECT_GT(usage1, 0.0);
439 
440   Time::ActivateHighResolutionTimer(true);
441   Sleep(10);
442   Time::ActivateHighResolutionTimer(false);
443   double usage2 = Time::GetHighResolutionTimerUsage();
444   // usage2 should be about 60%.
445   EXPECT_LT(usage2, 100.0);
446   EXPECT_GT(usage2, usage1);
447 
448   Time::ResetHighResolutionTimerUsage();
449   EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
450 }
451 
452 }  // namespace base
453