1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/ds/TimeStamp_windows.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,557 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with 1.11 +// values of GetTickCount(). 1.12 + 1.13 +#include "mozilla/MathAlgorithms.h" 1.14 +#include "mozilla/Mutex.h" 1.15 +#include "mozilla/TimeStamp.h" 1.16 +#include "nsWindowsHelpers.h" 1.17 +#include <windows.h> 1.18 + 1.19 +#include "nsCRT.h" 1.20 +#include "prlog.h" 1.21 +#include "prprf.h" 1.22 +#include <stdio.h> 1.23 + 1.24 +#include <intrin.h> 1.25 + 1.26 +#if defined(PR_LOGGING) 1.27 +// Log module for mozilla::TimeStamp for Windows logging... 1.28 +// 1.29 +// To enable logging (see prlog.h for full details): 1.30 +// 1.31 +// set NSPR_LOG_MODULES=TimeStampWindows:5 1.32 +// set NSPR_LOG_FILE=nspr.log 1.33 +// 1.34 +// this enables PR_LOG_DEBUG level information and places all output in 1.35 +// the file nspr.log 1.36 +static PRLogModuleInfo* 1.37 +GetTimeStampLog() 1.38 +{ 1.39 + static PRLogModuleInfo *sLog; 1.40 + if (!sLog) 1.41 + sLog = PR_NewLogModule("TimeStampWindows"); 1.42 + return sLog; 1.43 +} 1.44 + #define LOG(x) PR_LOG(GetTimeStampLog(), PR_LOG_DEBUG, x) 1.45 +#else 1.46 + #define LOG(x) 1.47 +#endif /* PR_LOGGING */ 1.48 + 1.49 +// Estimate of the smallest duration of time we can measure. 1.50 +static volatile ULONGLONG sResolution; 1.51 +static volatile ULONGLONG sResolutionSigDigs; 1.52 +static const double kNsPerSecd = 1000000000.0; 1.53 +static const LONGLONG kNsPerSec = 1000000000; 1.54 +static const LONGLONG kNsPerMillisec = 1000000; 1.55 + 1.56 +// ---------------------------------------------------------------------------- 1.57 +// Global constants 1.58 +// ---------------------------------------------------------------------------- 1.59 + 1.60 +// Tolerance to failures settings. 1.61 +// 1.62 +// What is the interval we want to have failure free. 1.63 +// in [ms] 1.64 +static const uint32_t kFailureFreeInterval = 5000; 1.65 +// How many failures we are willing to tolerate in the interval. 1.66 +static const uint32_t kMaxFailuresPerInterval = 4; 1.67 +// What is the threshold to treat fluctuations as actual failures. 1.68 +// in [ms] 1.69 +static const uint32_t kFailureThreshold = 50; 1.70 + 1.71 +// If we are not able to get the value of GTC time increment, use this value 1.72 +// which is the most usual increment. 1.73 +static const DWORD kDefaultTimeIncrement = 156001; 1.74 + 1.75 +// ---------------------------------------------------------------------------- 1.76 +// Global variables, not changing at runtime 1.77 +// ---------------------------------------------------------------------------- 1.78 + 1.79 +/** 1.80 + * The [mt] unit: 1.81 + * 1.82 + * Many values are kept in ticks of the Performance Coutner x 1000, 1.83 + * further just referred as [mt], meaning milli-ticks. 1.84 + * 1.85 + * This is needed to preserve maximum precision of the performance frequency 1.86 + * representation. GetTickCount values in milliseconds are multiplied with 1.87 + * frequency per second. Therefor we need to multiply QPC value by 1000 to 1.88 + * have the same units to allow simple arithmentic with both QPC and GTC. 1.89 + */ 1.90 + 1.91 +#define ms2mt(x) ((x) * sFrequencyPerSec) 1.92 +#define mt2ms(x) ((x) / sFrequencyPerSec) 1.93 +#define mt2ms_f(x) (double(x) / sFrequencyPerSec) 1.94 + 1.95 +// Result of QueryPerformanceFrequency 1.96 +static LONGLONG sFrequencyPerSec = 0; 1.97 + 1.98 +// How much we are tolerant to GTC occasional loose of resoltion. 1.99 +// This number says how many multiples of the minimal GTC resolution 1.100 +// detected on the system are acceptable. This number is empirical. 1.101 +static const LONGLONG kGTCTickLeapTolerance = 4; 1.102 + 1.103 +// Base tolerance (more: "inability of detection" range) threshold is calculated 1.104 +// dynamically, and kept in sGTCResulutionThreshold. 1.105 +// 1.106 +// Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) - 1.107 +// (QPC_now - QPC_epoch)) was in [-sGTCResulutionThreshold, sGTCResulutionThreshold] 1.108 +// interval every time we'd compared two time stamps. 1.109 +// If not, then we check the overflow behind this basic threshold 1.110 +// is in kFailureThreshold. If not, we condider it as a QPC failure. If too many 1.111 +// failures in short time are detected, QPC is considered faulty and disabled. 1.112 +// 1.113 +// Kept in [mt] 1.114 +static LONGLONG sGTCResulutionThreshold; 1.115 + 1.116 +// If QPC is found faulty for two stamps in this interval, we engage 1.117 +// the fault detection algorithm. For duration larger then this limit 1.118 +// we bypass using durations calculated from QPC when jitter is detected, 1.119 +// but don't touch the sUseQPC flag. 1.120 +// 1.121 +// Value is in [ms]. 1.122 +static const uint32_t kHardFailureLimit = 2000; 1.123 +// Conversion to [mt] 1.124 +static LONGLONG sHardFailureLimit; 1.125 + 1.126 +// Conversion of kFailureFreeInterval and kFailureThreshold to [mt] 1.127 +static LONGLONG sFailureFreeInterval; 1.128 +static LONGLONG sFailureThreshold; 1.129 + 1.130 +// ---------------------------------------------------------------------------- 1.131 +// Systemm status flags 1.132 +// ---------------------------------------------------------------------------- 1.133 + 1.134 +// Flag for stable TSC that indicates platform where QPC is stable. 1.135 +static bool sHasStableTSC = false; 1.136 + 1.137 +// ---------------------------------------------------------------------------- 1.138 +// Global state variables, changing at runtime 1.139 +// ---------------------------------------------------------------------------- 1.140 + 1.141 +// Initially true, set to false when QPC is found unstable and never 1.142 +// returns back to true since that time. 1.143 +static bool volatile sUseQPC = true; 1.144 + 1.145 +// ---------------------------------------------------------------------------- 1.146 +// Global lock 1.147 +// ---------------------------------------------------------------------------- 1.148 + 1.149 +// Thread spin count before entering the full wait state for sTimeStampLock. 1.150 +// Inspired by Rob Arnold's work on PRMJ_Now(). 1.151 +static const DWORD kLockSpinCount = 4096; 1.152 + 1.153 +// Common mutex (thanks the relative complexity of the logic, this is better 1.154 +// then using CMPXCHG8B.) 1.155 +// It is protecting the globals bellow. 1.156 +static CRITICAL_SECTION sTimeStampLock; 1.157 + 1.158 +// ---------------------------------------------------------------------------- 1.159 +// Global lock protected variables 1.160 +// ---------------------------------------------------------------------------- 1.161 + 1.162 +// Timestamp in future until QPC must behave correctly. 1.163 +// Set to now + kFailureFreeInterval on first QPC failure detection. 1.164 +// Set to now + E * kFailureFreeInterval on following errors, 1.165 +// where E is number of errors detected during last kFailureFreeInterval 1.166 +// milliseconds, calculated simply as: 1.167 +// E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1. 1.168 +// When E > kMaxFailuresPerInterval -> disable QPC. 1.169 +// 1.170 +// Kept in [mt] 1.171 +static ULONGLONG sFaultIntoleranceCheckpoint = 0; 1.172 + 1.173 +// Used only when GetTickCount64 is not available on the platform. 1.174 +// Last result of GetTickCount call. 1.175 +// 1.176 +// Kept in [ms] 1.177 +static DWORD sLastGTCResult = 0; 1.178 + 1.179 +// Higher part of the 64-bit value of MozGetTickCount64, 1.180 +// incremented atomically. 1.181 +static DWORD sLastGTCRollover = 0; 1.182 + 1.183 +namespace mozilla { 1.184 + 1.185 +typedef ULONGLONG (WINAPI* GetTickCount64_t)(); 1.186 +static GetTickCount64_t sGetTickCount64 = nullptr; 1.187 + 1.188 +// Function protecting GetTickCount result from rolling over, 1.189 +// result is in [ms] 1.190 +static ULONGLONG WINAPI 1.191 +MozGetTickCount64() 1.192 +{ 1.193 + DWORD GTC = ::GetTickCount(); 1.194 + 1.195 + // Cheaper then CMPXCHG8B 1.196 + AutoCriticalSection lock(&sTimeStampLock); 1.197 + 1.198 + // Pull the rollover counter forward only if new value of GTC goes way 1.199 + // down under the last saved result 1.200 + if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30))) 1.201 + ++sLastGTCRollover; 1.202 + 1.203 + sLastGTCResult = GTC; 1.204 + return ULONGLONG(sLastGTCRollover) << 32 | sLastGTCResult; 1.205 +} 1.206 + 1.207 +// Result is in [mt] 1.208 +static inline ULONGLONG 1.209 +PerformanceCounter() 1.210 +{ 1.211 + LARGE_INTEGER pc; 1.212 + ::QueryPerformanceCounter(&pc); 1.213 + return pc.QuadPart * 1000ULL; 1.214 +} 1.215 + 1.216 +static void 1.217 +InitThresholds() 1.218 +{ 1.219 + DWORD timeAdjustment = 0, timeIncrement = 0; 1.220 + BOOL timeAdjustmentDisabled; 1.221 + GetSystemTimeAdjustment(&timeAdjustment, 1.222 + &timeIncrement, 1.223 + &timeAdjustmentDisabled); 1.224 + 1.225 + LOG(("TimeStamp: timeIncrement=%d [100ns]", timeIncrement)); 1.226 + 1.227 + if (!timeIncrement) 1.228 + timeIncrement = kDefaultTimeIncrement; 1.229 + 1.230 + // Ceiling to a millisecond 1.231 + // Example values: 156001, 210000 1.232 + DWORD timeIncrementCeil = timeIncrement; 1.233 + // Don't want to round up if already rounded, values will be: 156000, 209999 1.234 + timeIncrementCeil -= 1; 1.235 + // Convert to ms, values will be: 15, 20 1.236 + timeIncrementCeil /= 10000; 1.237 + // Round up, values will be: 16, 21 1.238 + timeIncrementCeil += 1; 1.239 + // Convert back to 100ns, values will be: 160000, 210000 1.240 + timeIncrementCeil *= 10000; 1.241 + 1.242 + // How many milli-ticks has the interval rounded up 1.243 + LONGLONG ticksPerGetTickCountResolutionCeiling = 1.244 + (int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL; 1.245 + 1.246 + // GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value. 1.247 + sGTCResulutionThreshold = 1.248 + LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling); 1.249 + 1.250 + sHardFailureLimit = ms2mt(kHardFailureLimit); 1.251 + sFailureFreeInterval = ms2mt(kFailureFreeInterval); 1.252 + sFailureThreshold = ms2mt(kFailureThreshold); 1.253 +} 1.254 + 1.255 +static void 1.256 +InitResolution() 1.257 +{ 1.258 + // 10 total trials is arbitrary: what we're trying to avoid by 1.259 + // looping is getting unlucky and being interrupted by a context 1.260 + // switch or signal, or being bitten by paging/cache effects 1.261 + 1.262 + ULONGLONG minres = ~0ULL; 1.263 + int loops = 10; 1.264 + do { 1.265 + ULONGLONG start = PerformanceCounter(); 1.266 + ULONGLONG end = PerformanceCounter(); 1.267 + 1.268 + ULONGLONG candidate = (end - start); 1.269 + if (candidate < minres) 1.270 + minres = candidate; 1.271 + } while (--loops && minres); 1.272 + 1.273 + if (0 == minres) { 1.274 + minres = 1; 1.275 + } 1.276 + 1.277 + // Converting minres that is in [mt] to nanosecods, multiplicating 1.278 + // the argument to preserve resolution. 1.279 + ULONGLONG result = mt2ms(minres * kNsPerMillisec); 1.280 + if (0 == result) { 1.281 + result = 1; 1.282 + } 1.283 + 1.284 + sResolution = result; 1.285 + 1.286 + // find the number of significant digits in mResolution, for the 1.287 + // sake of ToSecondsSigDigits() 1.288 + ULONGLONG sigDigs; 1.289 + for (sigDigs = 1; 1.290 + !(sigDigs == result 1.291 + || 10*sigDigs > result); 1.292 + sigDigs *= 10); 1.293 + 1.294 + sResolutionSigDigs = sigDigs; 1.295 +} 1.296 + 1.297 +// ---------------------------------------------------------------------------- 1.298 +// TimeStampValue implementation 1.299 +// ---------------------------------------------------------------------------- 1.300 + 1.301 +TimeStampValue::TimeStampValue(ULONGLONG aGTC, ULONGLONG aQPC, bool aHasQPC) 1.302 + : mGTC(aGTC) 1.303 + , mQPC(aQPC) 1.304 + , mHasQPC(aHasQPC) 1.305 + , mIsNull(false) 1.306 +{ 1.307 +} 1.308 + 1.309 +TimeStampValue& 1.310 +TimeStampValue::operator+=(const int64_t aOther) 1.311 +{ 1.312 + mGTC += aOther; 1.313 + mQPC += aOther; 1.314 + return *this; 1.315 +} 1.316 + 1.317 +TimeStampValue& 1.318 +TimeStampValue::operator-=(const int64_t aOther) 1.319 +{ 1.320 + mGTC -= aOther; 1.321 + mQPC -= aOther; 1.322 + return *this; 1.323 +} 1.324 + 1.325 +// If the duration is less then two seconds, perform check of QPC stability 1.326 +// by comparing both GTC and QPC calculated durations of this and aOther. 1.327 +uint64_t 1.328 +TimeStampValue::CheckQPC(const TimeStampValue &aOther) const 1.329 +{ 1.330 + uint64_t deltaGTC = mGTC - aOther.mGTC; 1.331 + 1.332 + if (!mHasQPC || !aOther.mHasQPC) // Both not holding QPC 1.333 + return deltaGTC; 1.334 + 1.335 + uint64_t deltaQPC = mQPC - aOther.mQPC; 1.336 + 1.337 + if (sHasStableTSC) // For stable TSC there is no need to check 1.338 + return deltaQPC; 1.339 + 1.340 + if (!sUseQPC) // QPC globally disabled 1.341 + return deltaGTC; 1.342 + 1.343 + // Check QPC is sane before using it. 1.344 + int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC)); 1.345 + if (diff <= sGTCResulutionThreshold) 1.346 + return deltaQPC; 1.347 + 1.348 + // Treat absolutely for calibration purposes 1.349 + int64_t duration = DeprecatedAbs(int64_t(deltaGTC)); 1.350 + int64_t overflow = diff - sGTCResulutionThreshold; 1.351 + 1.352 + LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms", 1.353 + mt2ms(duration), mt2ms_f(overflow))); 1.354 + 1.355 + if (overflow <= sFailureThreshold) // We are in the limit, let go. 1.356 + return deltaQPC; // XXX Should we return GTC here? 1.357 + 1.358 + // QPC deviates, don't use it, since now this method may only return deltaGTC. 1.359 + LOG(("TimeStamp: QPC jittered over failure threshold")); 1.360 + 1.361 + if (duration < sHardFailureLimit) { 1.362 + // Interval between the two time stamps is very short, consider 1.363 + // QPC as unstable and record a failure. 1.364 + uint64_t now = ms2mt(sGetTickCount64()); 1.365 + 1.366 + AutoCriticalSection lock(&sTimeStampLock); 1.367 + 1.368 + if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) { 1.369 + // There's already been an error in the last fault intollerant interval. 1.370 + // Time since now to the checkpoint actually holds information on how many 1.371 + // failures there were in the failure free interval we have defined. 1.372 + uint64_t failureCount = (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) / 1.373 + sFailureFreeInterval; 1.374 + if (failureCount > kMaxFailuresPerInterval) { 1.375 + sUseQPC = false; 1.376 + LOG(("TimeStamp: QPC disabled")); 1.377 + } 1.378 + else { 1.379 + // Move the fault intolerance checkpoint more to the future, prolong it 1.380 + // to reflect the number of detected failures. 1.381 + ++failureCount; 1.382 + sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval; 1.383 + LOG(("TimeStamp: recording %dth QPC failure", failureCount)); 1.384 + } 1.385 + } 1.386 + else { 1.387 + // Setup fault intolerance checkpoint in the future for first detected error. 1.388 + sFaultIntoleranceCheckpoint = now + sFailureFreeInterval; 1.389 + LOG(("TimeStamp: recording 1st QPC failure")); 1.390 + } 1.391 + } 1.392 + 1.393 + return deltaGTC; 1.394 +} 1.395 + 1.396 +uint64_t 1.397 +TimeStampValue::operator-(const TimeStampValue &aOther) const 1.398 +{ 1.399 + if (mIsNull && aOther.mIsNull) 1.400 + return uint64_t(0); 1.401 + 1.402 + return CheckQPC(aOther); 1.403 +} 1.404 + 1.405 +// ---------------------------------------------------------------------------- 1.406 +// TimeDuration and TimeStamp implementation 1.407 +// ---------------------------------------------------------------------------- 1.408 + 1.409 +double 1.410 +TimeDuration::ToSeconds() const 1.411 +{ 1.412 + // Converting before arithmetic avoids blocked store forward 1.413 + return double(mValue) / (double(sFrequencyPerSec) * 1000.0); 1.414 +} 1.415 + 1.416 +double 1.417 +TimeDuration::ToSecondsSigDigits() const 1.418 +{ 1.419 + // don't report a value < mResolution ... 1.420 + LONGLONG resolution = sResolution; 1.421 + LONGLONG resolutionSigDigs = sResolutionSigDigs; 1.422 + LONGLONG valueSigDigs = resolution * (mValue / resolution); 1.423 + // and chop off insignificant digits 1.424 + valueSigDigs = resolutionSigDigs * (valueSigDigs / resolutionSigDigs); 1.425 + return double(valueSigDigs) / kNsPerSecd; 1.426 +} 1.427 + 1.428 +TimeDuration 1.429 +TimeDuration::FromMilliseconds(double aMilliseconds) 1.430 +{ 1.431 + return TimeDuration::FromTicks(int64_t(ms2mt(aMilliseconds))); 1.432 +} 1.433 + 1.434 +TimeDuration 1.435 +TimeDuration::Resolution() 1.436 +{ 1.437 + return TimeDuration::FromTicks(int64_t(sResolution)); 1.438 +} 1.439 + 1.440 +static bool 1.441 +HasStableTSC() 1.442 +{ 1.443 + union { 1.444 + int regs[4]; 1.445 + struct { 1.446 + int nIds; 1.447 + char cpuString[12]; 1.448 + }; 1.449 + } cpuInfo; 1.450 + 1.451 + __cpuid(cpuInfo.regs, 0); 1.452 + // Only allow Intel CPUs for now 1.453 + // The order of the registers is reg[1], reg[3], reg[2]. We just adjust the 1.454 + // string so that we can compare in one go. 1.455 + if (_strnicmp(cpuInfo.cpuString, "GenuntelineI", sizeof(cpuInfo.cpuString))) 1.456 + return false; 1.457 + 1.458 + int regs[4]; 1.459 + 1.460 + // detect if the Advanced Power Management feature is supported 1.461 + __cpuid(regs, 0x80000000); 1.462 + if (regs[0] < 0x80000007) 1.463 + return false; 1.464 + 1.465 + __cpuid(regs, 0x80000007); 1.466 + // if bit 8 is set than TSC will run at a constant rate 1.467 + // in all ACPI P-state, C-states and T-states 1.468 + return regs[3] & (1 << 8); 1.469 +} 1.470 + 1.471 +nsresult 1.472 +TimeStamp::Startup() 1.473 +{ 1.474 + // Decide which implementation to use for the high-performance timer. 1.475 + 1.476 + HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll"); 1.477 + sGetTickCount64 = reinterpret_cast<GetTickCount64_t> 1.478 + (GetProcAddress(kernelDLL, "GetTickCount64")); 1.479 + if (!sGetTickCount64) { 1.480 + // If the platform does not support the GetTickCount64 (Windows XP doesn't), 1.481 + // then use our fallback implementation based on GetTickCount. 1.482 + sGetTickCount64 = MozGetTickCount64; 1.483 + } 1.484 + 1.485 + InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount); 1.486 + 1.487 + sHasStableTSC = HasStableTSC(); 1.488 + LOG(("TimeStamp: HasStableTSC=%d", sHasStableTSC)); 1.489 + 1.490 + LARGE_INTEGER freq; 1.491 + sUseQPC = ::QueryPerformanceFrequency(&freq); 1.492 + if (!sUseQPC) { 1.493 + // No Performance Counter. Fall back to use GetTickCount. 1.494 + InitResolution(); 1.495 + 1.496 + LOG(("TimeStamp: using GetTickCount")); 1.497 + return NS_OK; 1.498 + } 1.499 + 1.500 + sFrequencyPerSec = freq.QuadPart; 1.501 + LOG(("TimeStamp: QPC frequency=%llu", sFrequencyPerSec)); 1.502 + 1.503 + InitThresholds(); 1.504 + InitResolution(); 1.505 + 1.506 + return NS_OK; 1.507 +} 1.508 + 1.509 +void 1.510 +TimeStamp::Shutdown() 1.511 +{ 1.512 + DeleteCriticalSection(&sTimeStampLock); 1.513 +} 1.514 + 1.515 +TimeStamp 1.516 +TimeStamp::Now(bool aHighResolution) 1.517 +{ 1.518 + // sUseQPC is volatile 1.519 + bool useQPC = (aHighResolution && sUseQPC); 1.520 + 1.521 + // Both values are in [mt] units. 1.522 + ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0); 1.523 + ULONGLONG GTC = ms2mt(sGetTickCount64()); 1.524 + return TimeStamp(TimeStampValue(GTC, QPC, useQPC)); 1.525 +} 1.526 + 1.527 +// Computes and returns the process uptime in microseconds. 1.528 +// Returns 0 if an error was encountered. 1.529 + 1.530 +uint64_t 1.531 +TimeStamp::ComputeProcessUptime() 1.532 +{ 1.533 + SYSTEMTIME nowSys; 1.534 + GetSystemTime(&nowSys); 1.535 + 1.536 + FILETIME now; 1.537 + bool success = SystemTimeToFileTime(&nowSys, &now); 1.538 + 1.539 + if (!success) 1.540 + return 0; 1.541 + 1.542 + FILETIME start, foo, bar, baz; 1.543 + success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz); 1.544 + 1.545 + if (!success) 1.546 + return 0; 1.547 + 1.548 + ULARGE_INTEGER startUsec = { 1.549 + start.dwLowDateTime, 1.550 + start.dwHighDateTime 1.551 + }; 1.552 + ULARGE_INTEGER nowUsec = { 1.553 + now.dwLowDateTime, 1.554 + now.dwHighDateTime 1.555 + }; 1.556 + 1.557 + return (nowUsec.QuadPart - startUsec.QuadPart) / 10ULL; 1.558 +} 1.559 + 1.560 +} // namespace mozilla