xpcom/ds/TimeStamp_windows.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with
     8 // values of GetTickCount().
    10 #include "mozilla/MathAlgorithms.h"
    11 #include "mozilla/Mutex.h"
    12 #include "mozilla/TimeStamp.h"
    13 #include "nsWindowsHelpers.h"
    14 #include <windows.h>
    16 #include "nsCRT.h"
    17 #include "prlog.h"
    18 #include "prprf.h"
    19 #include <stdio.h>
    21 #include <intrin.h>
    23 #if defined(PR_LOGGING)
    24 // Log module for mozilla::TimeStamp for Windows logging...
    25 //
    26 // To enable logging (see prlog.h for full details):
    27 //
    28 //    set NSPR_LOG_MODULES=TimeStampWindows:5
    29 //    set NSPR_LOG_FILE=nspr.log
    30 //
    31 // this enables PR_LOG_DEBUG level information and places all output in
    32 // the file nspr.log
    33 static PRLogModuleInfo*
    34 GetTimeStampLog()
    35 {
    36   static PRLogModuleInfo *sLog;
    37   if (!sLog)
    38     sLog = PR_NewLogModule("TimeStampWindows");
    39   return sLog;
    40 }
    41   #define LOG(x)  PR_LOG(GetTimeStampLog(), PR_LOG_DEBUG, x)
    42 #else
    43   #define LOG(x)
    44 #endif /* PR_LOGGING */
    46 // Estimate of the smallest duration of time we can measure.
    47 static volatile ULONGLONG sResolution;
    48 static volatile ULONGLONG sResolutionSigDigs;
    49 static const double   kNsPerSecd  = 1000000000.0;
    50 static const LONGLONG kNsPerSec   = 1000000000;
    51 static const LONGLONG kNsPerMillisec = 1000000;
    53 // ----------------------------------------------------------------------------
    54 // Global constants
    55 // ----------------------------------------------------------------------------
    57 // Tolerance to failures settings.
    58 //
    59 // What is the interval we want to have failure free.
    60 // in [ms]
    61 static const uint32_t kFailureFreeInterval = 5000;
    62 // How many failures we are willing to tolerate in the interval.
    63 static const uint32_t kMaxFailuresPerInterval = 4;
    64 // What is the threshold to treat fluctuations as actual failures.
    65 // in [ms]
    66 static const uint32_t kFailureThreshold = 50;
    68 // If we are not able to get the value of GTC time increment, use this value
    69 // which is the most usual increment.
    70 static const DWORD kDefaultTimeIncrement = 156001;
    72 // ----------------------------------------------------------------------------
    73 // Global variables, not changing at runtime
    74 // ----------------------------------------------------------------------------
    76 /**
    77  * The [mt] unit:
    78  *
    79  * Many values are kept in ticks of the Performance Coutner x 1000,
    80  * further just referred as [mt], meaning milli-ticks.
    81  *
    82  * This is needed to preserve maximum precision of the performance frequency
    83  * representation.  GetTickCount values in milliseconds are multiplied with
    84  * frequency per second.  Therefor we need to multiply QPC value by 1000 to
    85  * have the same units to allow simple arithmentic with both QPC and GTC.
    86  */
    88 #define ms2mt(x) ((x) * sFrequencyPerSec)
    89 #define mt2ms(x) ((x) / sFrequencyPerSec)
    90 #define mt2ms_f(x) (double(x) / sFrequencyPerSec)
    92 // Result of QueryPerformanceFrequency
    93 static LONGLONG sFrequencyPerSec = 0;
    95 // How much we are tolerant to GTC occasional loose of resoltion.
    96 // This number says how many multiples of the minimal GTC resolution
    97 // detected on the system are acceptable.  This number is empirical.
    98 static const LONGLONG kGTCTickLeapTolerance = 4;
   100 // Base tolerance (more: "inability of detection" range) threshold is calculated
   101 // dynamically, and kept in sGTCResulutionThreshold.
   102 //
   103 // Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) -
   104 // (QPC_now - QPC_epoch)) was in  [-sGTCResulutionThreshold, sGTCResulutionThreshold]
   105 // interval every time we'd compared two time stamps.
   106 // If not, then we check the overflow behind this basic threshold
   107 // is in kFailureThreshold.  If not, we condider it as a QPC failure.  If too many
   108 // failures in short time are detected, QPC is considered faulty and disabled.
   109 //
   110 // Kept in [mt]
   111 static LONGLONG sGTCResulutionThreshold;
   113 // If QPC is found faulty for two stamps in this interval, we engage
   114 // the fault detection algorithm.  For duration larger then this limit
   115 // we bypass using durations calculated from QPC when jitter is detected,
   116 // but don't touch the sUseQPC flag.
   117 //
   118 // Value is in [ms].
   119 static const uint32_t kHardFailureLimit = 2000;
   120 // Conversion to [mt]
   121 static LONGLONG sHardFailureLimit;
   123 // Conversion of kFailureFreeInterval and kFailureThreshold to [mt]
   124 static LONGLONG sFailureFreeInterval;
   125 static LONGLONG sFailureThreshold;
   127 // ----------------------------------------------------------------------------
   128 // Systemm status flags
   129 // ----------------------------------------------------------------------------
   131 // Flag for stable TSC that indicates platform where QPC is stable.
   132 static bool sHasStableTSC = false;
   134 // ----------------------------------------------------------------------------
   135 // Global state variables, changing at runtime
   136 // ----------------------------------------------------------------------------
   138 // Initially true, set to false when QPC is found unstable and never
   139 // returns back to true since that time.
   140 static bool volatile sUseQPC = true;
   142 // ----------------------------------------------------------------------------
   143 // Global lock
   144 // ----------------------------------------------------------------------------
   146 // Thread spin count before entering the full wait state for sTimeStampLock.
   147 // Inspired by Rob Arnold's work on PRMJ_Now().
   148 static const DWORD kLockSpinCount = 4096;
   150 // Common mutex (thanks the relative complexity of the logic, this is better
   151 // then using CMPXCHG8B.)
   152 // It is protecting the globals bellow.
   153 static CRITICAL_SECTION sTimeStampLock;
   155 // ----------------------------------------------------------------------------
   156 // Global lock protected variables
   157 // ----------------------------------------------------------------------------
   159 // Timestamp in future until QPC must behave correctly.
   160 // Set to now + kFailureFreeInterval on first QPC failure detection.
   161 // Set to now + E * kFailureFreeInterval on following errors,
   162 //   where E is number of errors detected during last kFailureFreeInterval
   163 //   milliseconds, calculated simply as:
   164 //   E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1.
   165 // When E > kMaxFailuresPerInterval -> disable QPC.
   166 //
   167 // Kept in [mt]
   168 static ULONGLONG sFaultIntoleranceCheckpoint = 0;
   170 // Used only when GetTickCount64 is not available on the platform.
   171 // Last result of GetTickCount call.
   172 //
   173 // Kept in [ms]
   174 static DWORD sLastGTCResult = 0;
   176 // Higher part of the 64-bit value of MozGetTickCount64,
   177 // incremented atomically.
   178 static DWORD sLastGTCRollover = 0;
   180 namespace mozilla {
   182 typedef ULONGLONG (WINAPI* GetTickCount64_t)();
   183 static GetTickCount64_t sGetTickCount64 = nullptr;
   185 // Function protecting GetTickCount result from rolling over,
   186 // result is in [ms]
   187 static ULONGLONG WINAPI
   188 MozGetTickCount64()
   189 {
   190   DWORD GTC = ::GetTickCount();
   192   // Cheaper then CMPXCHG8B
   193   AutoCriticalSection lock(&sTimeStampLock);
   195   // Pull the rollover counter forward only if new value of GTC goes way
   196   // down under the last saved result
   197   if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30)))
   198     ++sLastGTCRollover;
   200   sLastGTCResult = GTC;
   201   return ULONGLONG(sLastGTCRollover) << 32 | sLastGTCResult;
   202 }
   204 // Result is in [mt]
   205 static inline ULONGLONG
   206 PerformanceCounter()
   207 {
   208   LARGE_INTEGER pc;
   209   ::QueryPerformanceCounter(&pc);
   210   return pc.QuadPart * 1000ULL;
   211 }
   213 static void
   214 InitThresholds()
   215 {
   216   DWORD timeAdjustment = 0, timeIncrement = 0;
   217   BOOL timeAdjustmentDisabled;
   218   GetSystemTimeAdjustment(&timeAdjustment,
   219                           &timeIncrement,
   220                           &timeAdjustmentDisabled);
   222   LOG(("TimeStamp: timeIncrement=%d [100ns]", timeIncrement));
   224   if (!timeIncrement)
   225     timeIncrement = kDefaultTimeIncrement;
   227   // Ceiling to a millisecond
   228   // Example values: 156001, 210000
   229   DWORD timeIncrementCeil = timeIncrement;
   230   // Don't want to round up if already rounded, values will be: 156000, 209999
   231   timeIncrementCeil -= 1;
   232   // Convert to ms, values will be: 15, 20
   233   timeIncrementCeil /= 10000;
   234   // Round up, values will be: 16, 21
   235   timeIncrementCeil += 1;
   236   // Convert back to 100ns, values will be: 160000, 210000
   237   timeIncrementCeil *= 10000;
   239   // How many milli-ticks has the interval rounded up
   240   LONGLONG ticksPerGetTickCountResolutionCeiling =
   241     (int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL;
   243   // GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value.
   244   sGTCResulutionThreshold =
   245     LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling);
   247   sHardFailureLimit = ms2mt(kHardFailureLimit);
   248   sFailureFreeInterval = ms2mt(kFailureFreeInterval);
   249   sFailureThreshold = ms2mt(kFailureThreshold);
   250 }
   252 static void
   253 InitResolution()
   254 {
   255   // 10 total trials is arbitrary: what we're trying to avoid by
   256   // looping is getting unlucky and being interrupted by a context
   257   // switch or signal, or being bitten by paging/cache effects
   259   ULONGLONG minres = ~0ULL;
   260   int loops = 10;
   261   do {
   262     ULONGLONG start = PerformanceCounter();
   263     ULONGLONG end = PerformanceCounter();
   265     ULONGLONG candidate = (end - start);
   266     if (candidate < minres)
   267       minres = candidate;
   268   } while (--loops && minres);
   270   if (0 == minres) {
   271     minres = 1;
   272   }
   274   // Converting minres that is in [mt] to nanosecods, multiplicating
   275   // the argument to preserve resolution.
   276   ULONGLONG result = mt2ms(minres * kNsPerMillisec);
   277   if (0 == result) {
   278     result = 1;
   279   }
   281   sResolution = result;
   283   // find the number of significant digits in mResolution, for the
   284   // sake of ToSecondsSigDigits()
   285   ULONGLONG sigDigs;
   286   for (sigDigs = 1;
   287        !(sigDigs == result
   288          || 10*sigDigs > result);
   289        sigDigs *= 10);
   291   sResolutionSigDigs = sigDigs;
   292 }
   294 // ----------------------------------------------------------------------------
   295 // TimeStampValue implementation
   296 // ----------------------------------------------------------------------------
   298 TimeStampValue::TimeStampValue(ULONGLONG aGTC, ULONGLONG aQPC, bool aHasQPC)
   299   : mGTC(aGTC)
   300   , mQPC(aQPC)
   301   , mHasQPC(aHasQPC)
   302   , mIsNull(false)
   303 {
   304 }
   306 TimeStampValue&
   307 TimeStampValue::operator+=(const int64_t aOther)
   308 {
   309   mGTC += aOther;
   310   mQPC += aOther;
   311   return *this;
   312 }
   314 TimeStampValue&
   315 TimeStampValue::operator-=(const int64_t aOther)
   316 {
   317   mGTC -= aOther;
   318   mQPC -= aOther;
   319   return *this;
   320 }
   322 // If the duration is less then two seconds, perform check of QPC stability
   323 // by comparing both GTC and QPC calculated durations of this and aOther.
   324 uint64_t
   325 TimeStampValue::CheckQPC(const TimeStampValue &aOther) const
   326 {
   327   uint64_t deltaGTC = mGTC - aOther.mGTC;
   329   if (!mHasQPC || !aOther.mHasQPC) // Both not holding QPC
   330     return deltaGTC;
   332   uint64_t deltaQPC = mQPC - aOther.mQPC;
   334   if (sHasStableTSC) // For stable TSC there is no need to check
   335     return deltaQPC;
   337   if (!sUseQPC) // QPC globally disabled
   338     return deltaGTC;
   340   // Check QPC is sane before using it.
   341   int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC));
   342   if (diff <= sGTCResulutionThreshold)
   343     return deltaQPC;
   345   // Treat absolutely for calibration purposes
   346   int64_t duration = DeprecatedAbs(int64_t(deltaGTC));
   347   int64_t overflow = diff - sGTCResulutionThreshold;
   349   LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms",
   350        mt2ms(duration), mt2ms_f(overflow)));
   352   if (overflow <= sFailureThreshold) // We are in the limit, let go.
   353     return deltaQPC; // XXX Should we return GTC here?
   355   // QPC deviates, don't use it, since now this method may only return deltaGTC.
   356   LOG(("TimeStamp: QPC jittered over failure threshold"));
   358   if (duration < sHardFailureLimit) {
   359     // Interval between the two time stamps is very short, consider
   360     // QPC as unstable and record a failure.
   361     uint64_t now = ms2mt(sGetTickCount64());
   363     AutoCriticalSection lock(&sTimeStampLock);
   365     if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) {
   366       // There's already been an error in the last fault intollerant interval.
   367       // Time since now to the checkpoint actually holds information on how many
   368       // failures there were in the failure free interval we have defined.
   369       uint64_t failureCount = (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) /
   370                                sFailureFreeInterval;
   371       if (failureCount > kMaxFailuresPerInterval) {
   372         sUseQPC = false;
   373         LOG(("TimeStamp: QPC disabled"));
   374       }
   375       else {
   376         // Move the fault intolerance checkpoint more to the future, prolong it
   377         // to reflect the number of detected failures.
   378         ++failureCount;
   379         sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval;
   380         LOG(("TimeStamp: recording %dth QPC failure", failureCount));
   381       }
   382     }
   383     else {
   384       // Setup fault intolerance checkpoint in the future for first detected error.
   385       sFaultIntoleranceCheckpoint = now + sFailureFreeInterval;
   386       LOG(("TimeStamp: recording 1st QPC failure"));
   387     }
   388   }
   390   return deltaGTC;
   391 }
   393 uint64_t
   394 TimeStampValue::operator-(const TimeStampValue &aOther) const
   395 {
   396   if (mIsNull && aOther.mIsNull)
   397     return uint64_t(0);
   399   return CheckQPC(aOther);
   400 }
   402 // ----------------------------------------------------------------------------
   403 // TimeDuration and TimeStamp implementation
   404 // ----------------------------------------------------------------------------
   406 double
   407 TimeDuration::ToSeconds() const
   408 {
   409   // Converting before arithmetic avoids blocked store forward
   410   return double(mValue) / (double(sFrequencyPerSec) * 1000.0);
   411 }
   413 double
   414 TimeDuration::ToSecondsSigDigits() const
   415 {
   416   // don't report a value < mResolution ...
   417   LONGLONG resolution = sResolution;
   418   LONGLONG resolutionSigDigs = sResolutionSigDigs;
   419   LONGLONG valueSigDigs = resolution * (mValue / resolution);
   420   // and chop off insignificant digits
   421   valueSigDigs = resolutionSigDigs * (valueSigDigs / resolutionSigDigs);
   422   return double(valueSigDigs) / kNsPerSecd;
   423 }
   425 TimeDuration
   426 TimeDuration::FromMilliseconds(double aMilliseconds)
   427 {
   428   return TimeDuration::FromTicks(int64_t(ms2mt(aMilliseconds)));
   429 }
   431 TimeDuration
   432 TimeDuration::Resolution()
   433 {
   434   return TimeDuration::FromTicks(int64_t(sResolution));
   435 }
   437 static bool
   438 HasStableTSC()
   439 {
   440   union {
   441     int regs[4];
   442     struct {
   443       int nIds;
   444       char cpuString[12];
   445     };
   446   } cpuInfo;
   448   __cpuid(cpuInfo.regs, 0);
   449   // Only allow Intel CPUs for now
   450   // The order of the registers is reg[1], reg[3], reg[2].  We just adjust the
   451   // string so that we can compare in one go.
   452   if (_strnicmp(cpuInfo.cpuString, "GenuntelineI", sizeof(cpuInfo.cpuString)))
   453     return false;
   455   int regs[4];
   457   // detect if the Advanced Power Management feature is supported
   458   __cpuid(regs, 0x80000000);
   459   if (regs[0] < 0x80000007)
   460     return false;
   462   __cpuid(regs, 0x80000007);
   463   // if bit 8 is set than TSC will run at a constant rate
   464   // in all ACPI P-state, C-states and T-states
   465   return regs[3] & (1 << 8);
   466 }
   468 nsresult
   469 TimeStamp::Startup()
   470 {
   471   // Decide which implementation to use for the high-performance timer.
   473   HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
   474   sGetTickCount64 = reinterpret_cast<GetTickCount64_t>
   475     (GetProcAddress(kernelDLL, "GetTickCount64"));
   476   if (!sGetTickCount64) {
   477     // If the platform does not support the GetTickCount64 (Windows XP doesn't),
   478     // then use our fallback implementation based on GetTickCount.
   479     sGetTickCount64 = MozGetTickCount64;
   480   }
   482   InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount);
   484   sHasStableTSC = HasStableTSC();
   485   LOG(("TimeStamp: HasStableTSC=%d", sHasStableTSC));
   487   LARGE_INTEGER freq;
   488   sUseQPC = ::QueryPerformanceFrequency(&freq);
   489   if (!sUseQPC) {
   490     // No Performance Counter.  Fall back to use GetTickCount.
   491     InitResolution();
   493     LOG(("TimeStamp: using GetTickCount"));
   494     return NS_OK;
   495   }
   497   sFrequencyPerSec = freq.QuadPart;
   498   LOG(("TimeStamp: QPC frequency=%llu", sFrequencyPerSec));
   500   InitThresholds();
   501   InitResolution();
   503   return NS_OK;
   504 }
   506 void
   507 TimeStamp::Shutdown()
   508 {
   509   DeleteCriticalSection(&sTimeStampLock);
   510 }
   512 TimeStamp
   513 TimeStamp::Now(bool aHighResolution)
   514 {
   515   // sUseQPC is volatile
   516   bool useQPC = (aHighResolution && sUseQPC);
   518   // Both values are in [mt] units.
   519   ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0);
   520   ULONGLONG GTC = ms2mt(sGetTickCount64());
   521   return TimeStamp(TimeStampValue(GTC, QPC, useQPC));
   522 }
   524 // Computes and returns the process uptime in microseconds.
   525 // Returns 0 if an error was encountered.
   527 uint64_t
   528 TimeStamp::ComputeProcessUptime()
   529 {
   530   SYSTEMTIME nowSys;
   531   GetSystemTime(&nowSys);
   533   FILETIME now;
   534   bool success = SystemTimeToFileTime(&nowSys, &now);
   536   if (!success)
   537     return 0;
   539   FILETIME start, foo, bar, baz;
   540   success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
   542   if (!success)
   543     return 0;
   545   ULARGE_INTEGER startUsec = {
   546     start.dwLowDateTime,
   547     start.dwHighDateTime
   548   };
   549   ULARGE_INTEGER nowUsec = {
   550     now.dwLowDateTime,
   551     now.dwHighDateTime
   552   };
   554   return (nowUsec.QuadPart - startUsec.QuadPart) / 10ULL;
   555 }
   557 } // namespace mozilla

mercurial