123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <windows.h>
- #include <mmsystem.h>
- #include <process.h>
- #include <stdint.h>
- #include <windows.foundation.h>
- #include <algorithm>
- #include <cmath>
- #include <limits>
- #include <vector>
- #include "base/strings/string_piece.h"
- #include "base/threading/platform_thread.h"
- #include "base/time/time.h"
- #include "base/win/registry.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace base {
- namespace {
- // For TimeDelta::ConstexprInitialization
- constexpr int kExpectedDeltaInMilliseconds = 10;
- constexpr TimeDelta kConstexprTimeDelta =
- Milliseconds(kExpectedDeltaInMilliseconds);
- class MockTimeTicks : public TimeTicks {
- public:
- static DWORD Ticker() {
- return static_cast<int>(InterlockedIncrement(&ticker_));
- }
- static void InstallTicker() {
- old_tick_function_ = SetMockTickFunction(&Ticker);
- ticker_ = -5;
- }
- static void UninstallTicker() { SetMockTickFunction(old_tick_function_); }
- private:
- static volatile LONG ticker_;
- static TickFunctionType old_tick_function_;
- };
- volatile LONG MockTimeTicks::ticker_;
- MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_;
- HANDLE g_rollover_test_start;
- unsigned __stdcall RolloverTestThreadMain(void* param) {
- int64_t counter = reinterpret_cast<int64_t>(param);
- DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
- EXPECT_EQ(rv, WAIT_OBJECT_0);
- TimeTicks last = TimeTicks::Now();
- for (int index = 0; index < counter; index++) {
- TimeTicks now = TimeTicks::Now();
- int64_t milliseconds = (now - last).InMilliseconds();
- // This is a tight loop; we could have looped faster than our
- // measurements, so the time might be 0 millis.
- EXPECT_GE(milliseconds, 0);
- EXPECT_LT(milliseconds, 250);
- last = now;
- }
- return 0;
- }
- #if defined(_M_ARM64) && defined(__clang__)
- #define ReadCycleCounter() _ReadStatusReg(ARM64_PMCCNTR_EL0)
- #else
- #define ReadCycleCounter() __rdtsc()
- #endif
- // Measure the performance of the CPU cycle counter so that we can compare it to
- // the overhead of QueryPerformanceCounter. A hard-coded frequency is used
- // because we don't care about the accuracy of the results, we just need to do
- // the work. The amount of work is not exactly the same as in TimeTicks::Now
- // (some steps are skipped) but that doesn't seem to materially affect the
- // results.
- TimeTicks GetTSC() {
- // Using a fake cycle counter frequency for test purposes.
- return TimeTicks() + Microseconds(ReadCycleCounter() *
- Time::kMicrosecondsPerSecond / 10000000);
- }
- } // namespace
- // This test spawns many threads, and can occasionally fail due to resource
- // exhaustion in the presence of ASan.
- #if defined(ADDRESS_SANITIZER)
- #define MAYBE_WinRollover DISABLED_WinRollover
- #else
- #define MAYBE_WinRollover WinRollover
- #endif
- TEST(TimeTicks, MAYBE_WinRollover) {
- // The internal counter rolls over at ~49days. We'll use a mock
- // timer to test this case.
- // Basic test algorithm:
- // 1) Set clock to rollover - N
- // 2) Create N threads
- // 3) Start the threads
- // 4) Each thread loops through TimeTicks() N times
- // 5) Each thread verifies integrity of result.
- const int kThreads = 8;
- // Use int64_t so we can cast into a void* without a compiler warning.
- const int64_t kChecks = 10;
- // It takes a lot of iterations to reproduce the bug!
- // (See bug 1081395)
- for (int loop = 0; loop < 4096; loop++) {
- // Setup
- MockTimeTicks::InstallTicker();
- g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
- HANDLE threads[kThreads];
- for (int index = 0; index < kThreads; index++) {
- void* argument = reinterpret_cast<void*>(kChecks);
- unsigned thread_id;
- threads[index] = reinterpret_cast<HANDLE>(_beginthreadex(
- NULL, 0, RolloverTestThreadMain, argument, 0, &thread_id));
- EXPECT_NE((HANDLE)NULL, threads[index]);
- }
- // Start!
- SetEvent(g_rollover_test_start);
- // Wait for threads to finish
- for (int index = 0; index < kThreads; index++) {
- DWORD rv = WaitForSingleObject(threads[index], INFINITE);
- EXPECT_EQ(rv, WAIT_OBJECT_0);
- // Since using _beginthreadex() (as opposed to _beginthread),
- // an explicit CloseHandle() is supposed to be called.
- CloseHandle(threads[index]);
- }
- CloseHandle(g_rollover_test_start);
- // Teardown
- MockTimeTicks::UninstallTicker();
- }
- }
- TEST(TimeTicks, SubMillisecondTimers) {
- // IsHighResolution() is false on some systems. Since the product still works
- // even if it's false, it makes this entire test questionable.
- if (!TimeTicks::IsHighResolution())
- return;
- // Run kRetries attempts to see a sub-millisecond timer.
- constexpr int kRetries = 1000;
- for (int index = 0; index < kRetries; index++) {
- const TimeTicks start_time = TimeTicks::Now();
- TimeDelta delta;
- // Spin until the clock has detected a change.
- do {
- delta = TimeTicks::Now() - start_time;
- } while (delta.is_zero());
- if (!delta.InMilliseconds())
- return;
- }
- ADD_FAILURE() << "Never saw a sub-millisecond timer.";
- }
- TEST(TimeTicks, TimeGetTimeCaps) {
- // Test some basic assumptions that we expect about how timeGetDevCaps works.
- TIMECAPS caps;
- MMRESULT status = timeGetDevCaps(&caps, sizeof(caps));
- ASSERT_EQ(static_cast<MMRESULT>(MMSYSERR_NOERROR), status);
- EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
- EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
- EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
- EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
- printf("timeGetTime range is %d to %dms\n", caps.wPeriodMin, caps.wPeriodMax);
- }
- TEST(TimeTicks, QueryPerformanceFrequency) {
- // Test some basic assumptions that we expect about QPC.
- LARGE_INTEGER frequency;
- BOOL rv = QueryPerformanceFrequency(&frequency);
- EXPECT_EQ(TRUE, rv);
- EXPECT_GT(frequency.QuadPart, 1000000); // Expect at least 1MHz
- printf("QueryPerformanceFrequency is %5.2fMHz\n",
- frequency.QuadPart / 1000000.0);
- }
- TEST(TimeTicks, TimerPerformance) {
- // Verify that various timer mechanisms can always complete quickly.
- // Note: This is a somewhat arbitrary test.
- const int kLoops = 500000;
- typedef TimeTicks (*TestFunc)();
- struct TestCase {
- TestFunc func;
- const char* description;
- };
- // Cheating a bit here: assumes sizeof(TimeTicks) == sizeof(Time)
- // in order to create a single test case list.
- static_assert(sizeof(TimeTicks) == sizeof(Time),
- "TimeTicks and Time must be the same size");
- std::vector<TestCase> cases;
- cases.push_back({reinterpret_cast<TestFunc>(&Time::Now), "Time::Now"});
- cases.push_back({&TimeTicks::Now, "TimeTicks::Now"});
- cases.push_back({&GetTSC, "CPUCycleCounter"});
- if (ThreadTicks::IsSupported()) {
- ThreadTicks::WaitUntilInitialized();
- cases.push_back(
- {reinterpret_cast<TestFunc>(&ThreadTicks::Now), "ThreadTicks::Now"});
- }
- // Warm up the CPU to its full clock rate so that we get accurate timing
- // information.
- DWORD start_tick = GetTickCount();
- const DWORD kWarmupMs = 50;
- for (;;) {
- DWORD elapsed = GetTickCount() - start_tick;
- if (elapsed > kWarmupMs)
- break;
- }
- for (const auto& test_case : cases) {
- TimeTicks start = TimeTicks::Now();
- for (int index = 0; index < kLoops; index++)
- test_case.func();
- TimeTicks stop = TimeTicks::Now();
- // Turning off the check for acceptible delays. Without this check,
- // the test really doesn't do much other than measure. But the
- // measurements are still useful for testing timers on various platforms.
- // The reason to remove the check is because the tests run on many
- // buildbots, some of which are VMs. These machines can run horribly
- // slow, and there is really no value for checking against a max timer.
- // const int kMaxTime = 35; // Maximum acceptible milliseconds for test.
- // EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
- printf("%s: %1.2fus per call\n", test_case.description,
- (stop - start).InMillisecondsF() * 1000 / kLoops);
- }
- }
- #if !defined(ARCH_CPU_ARM64)
- // This test is disabled on Windows ARM64 systems because TSCTicksPerSecond is
- // only used in Chromium for QueryThreadCycleTime, and QueryThreadCycleTime
- // doesn't use a constant-rate timer on ARM64.
- TEST(TimeTicks, TSCTicksPerSecond) {
- if (time_internal::HasConstantRateTSC()) {
- ThreadTicks::WaitUntilInitialized();
- // Read the CPU frequency from the registry.
- base::win::RegKey processor_key(
- HKEY_LOCAL_MACHINE,
- L"Hardware\\Description\\System\\CentralProcessor\\0", KEY_QUERY_VALUE);
- ASSERT_TRUE(processor_key.Valid());
- DWORD processor_mhz_from_registry;
- ASSERT_EQ(ERROR_SUCCESS,
- processor_key.ReadValueDW(L"~MHz", &processor_mhz_from_registry));
- // Expect the measured TSC frequency to be similar to the processor
- // frequency from the registry (0.5% error).
- double tsc_mhz_measured = time_internal::TSCTicksPerSecond() / 1e6;
- EXPECT_NEAR(tsc_mhz_measured, processor_mhz_from_registry,
- 0.005 * processor_mhz_from_registry);
- }
- }
- #endif
- TEST(TimeTicks, FromQPCValue) {
- if (!TimeTicks::IsHighResolution())
- return;
- LARGE_INTEGER frequency;
- ASSERT_TRUE(QueryPerformanceFrequency(&frequency));
- const int64_t ticks_per_second = frequency.QuadPart;
- ASSERT_GT(ticks_per_second, 0);
- // Generate the tick values to convert, advancing the tick count by varying
- // amounts. These values will ensure that both the fast and overflow-safe
- // conversion logic in FromQPCValue() is tested, and across the entire range
- // of possible QPC tick values.
- std::vector<int64_t> test_cases;
- test_cases.push_back(0);
- // Build the test cases.
- {
- const int kNumAdvancements = 100;
- int64_t ticks = 0;
- int64_t ticks_increment = 10;
- for (int i = 0; i < kNumAdvancements; ++i) {
- test_cases.push_back(ticks);
- ticks += ticks_increment;
- ticks_increment = ticks_increment * 6 / 5;
- }
- test_cases.push_back(Time::kQPCOverflowThreshold - 1);
- test_cases.push_back(Time::kQPCOverflowThreshold);
- test_cases.push_back(Time::kQPCOverflowThreshold + 1);
- ticks = Time::kQPCOverflowThreshold + 10;
- ticks_increment = 10;
- for (int i = 0; i < kNumAdvancements; ++i) {
- test_cases.push_back(ticks);
- ticks += ticks_increment;
- ticks_increment = ticks_increment * 6 / 5;
- }
- test_cases.push_back(std::numeric_limits<int64_t>::max());
- }
- // Test that the conversions using FromQPCValue() match those computed here
- // using simple floating-point arithmetic. The floating-point math provides
- // enough precision for all reasonable values to confirm that the
- // implementation is correct to the microsecond, and for "very large" values
- // it confirms that the answer is very close to correct.
- for (int64_t ticks : test_cases) {
- const double expected_microseconds_since_origin =
- (static_cast<double>(ticks) * Time::kMicrosecondsPerSecond) /
- ticks_per_second;
- const TimeTicks converted_value = TimeTicks::FromQPCValue(ticks);
- const double converted_microseconds_since_origin =
- (converted_value - TimeTicks()).InMicrosecondsF();
- // When we test with very large numbers we end up in a range where adjacent
- // double values are far apart - 512.0 apart in one test failure. In that
- // situation it makes no sense for our epsilon to be 1.0 - it should be
- // the difference between adjacent doubles.
- double epsilon = nextafter(expected_microseconds_since_origin, INFINITY) -
- expected_microseconds_since_origin;
- // Epsilon must be at least 1.0 because converted_microseconds_since_origin
- // comes from an integral value, and expected_microseconds_since_origin is
- // a double that is expected to be up to 0.999 larger. In addition, due to
- // multiple roundings in the double calculation the actual error can be
- // slightly larger than 1.0, even when the converted value is perfect. This
- // epsilon value was chosen because it is slightly larger than the error
- // seen in a test failure caused by the double rounding.
- epsilon = std::max(epsilon, 1.002);
- EXPECT_NEAR(expected_microseconds_since_origin,
- converted_microseconds_since_origin, epsilon)
- << "ticks=" << ticks << ", to be converted via logic path: "
- << (ticks < Time::kQPCOverflowThreshold ? "FAST" : "SAFE");
- }
- }
- TEST(TimeDelta, ConstexprInitialization) {
- // Make sure that TimeDelta works around crbug.com/635974
- EXPECT_EQ(kExpectedDeltaInMilliseconds, kConstexprTimeDelta.InMilliseconds());
- }
- TEST(TimeDelta, FromFileTime) {
- FILETIME ft;
- ft.dwLowDateTime = 1001;
- ft.dwHighDateTime = 0;
- // 100100 ns ~= 100 us.
- EXPECT_EQ(Microseconds(100), TimeDelta::FromFileTime(ft));
- ft.dwLowDateTime = 0;
- ft.dwHighDateTime = 1;
- // 2^32 * 100 ns ~= 2^32 * 10 us.
- EXPECT_EQ(Microseconds((1ull << 32) / 10), TimeDelta::FromFileTime(ft));
- }
- TEST(TimeDelta, FromWinrtDateTime) {
- ABI::Windows::Foundation::DateTime dt;
- dt.UniversalTime = 0;
- // 0 UniversalTime = no delta since epoch.
- EXPECT_EQ(TimeDelta(), TimeDelta::FromWinrtDateTime(dt));
- dt.UniversalTime = 101;
- // 101 * 100 ns ~= 10.1 microseconds.
- EXPECT_EQ(Microseconds(10.1), TimeDelta::FromWinrtDateTime(dt));
- }
- TEST(TimeDelta, ToWinrtDateTime) {
- auto time_delta = Seconds(0);
- // No delta since epoch = 0 DateTime.
- EXPECT_EQ(0, time_delta.ToWinrtDateTime().UniversalTime);
- time_delta = Microseconds(10);
- // 10 microseconds = 100 * 100 ns.
- EXPECT_EQ(100, time_delta.ToWinrtDateTime().UniversalTime);
- }
- TEST(HighResolutionTimer, GetUsage) {
- Time::ResetHighResolutionTimerUsage();
- // 0% usage since the timer isn't activated regardless of how much time has
- // elapsed.
- EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
- Sleep(10);
- EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
- Time::ActivateHighResolutionTimer(true);
- Time::ResetHighResolutionTimerUsage();
- Sleep(20);
- // 100% usage since the timer has been activated entire time.
- EXPECT_EQ(100.0, Time::GetHighResolutionTimerUsage());
- Time::ActivateHighResolutionTimer(false);
- Sleep(20);
- double usage1 = Time::GetHighResolutionTimerUsage();
- // usage1 should be about 50%.
- EXPECT_LT(usage1, 100.0);
- EXPECT_GT(usage1, 0.0);
- Time::ActivateHighResolutionTimer(true);
- Sleep(10);
- Time::ActivateHighResolutionTimer(false);
- double usage2 = Time::GetHighResolutionTimerUsage();
- // usage2 should be about 60%.
- EXPECT_LT(usage2, 100.0);
- EXPECT_GT(usage2, usage1);
- Time::ResetHighResolutionTimerUsage();
- EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
- }
- } // namespace base
|