// If the duration is less then one second, perform check of QPC stability // by comparing both 'epoch' and 'now' skew (=GTC - QPC) values. bool TimeStampValue::CheckQPC(int64_t aDuration, const TimeStampValue &aOther) const { if (!mHasQPC || !aOther.mHasQPC) // Not both holding QPC return false; if (sHasStableTSC) // For stable TSC there is no need to check return true; if (!sUseQPC) // QPC globally disabled return false; // Treat absolutely for calibration purposes aDuration = Abs(aDuration); // Check QPC is sane before using it. LONGLONG skew1 = mGTC - mQPC; LONGLONG skew2 = aOther.mGTC - aOther.mQPC; LONGLONG diff = skew1 - skew2; LONGLONG overflow; if (diff < sUnderrunThreshold) overflow = sUnderrunThreshold - diff; else if (diff > sOverrunThreshold) overflow = diff - sOverrunThreshold; else return true; ULONGLONG trend; if (aDuration) trend = LONGLONG(overflow * (double(sQPCHardFailureDetectionInterval) / aDuration)); else trend = overflow; LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms" ", adjusted trend per interval is %1.4fms", mt2ms(aDuration), mt2ms_f(overflow), mt2ms_f(trend))); if (trend <= ms2mt(kOverflowLimit)) { // We are in the limit, let go. return true; } // QPC deviates, don't use it. LOG(("TimeStamp: QPC found highly jittering")); if (aDuration < sQPCHardFailureDetectionInterval) { // Interval between the two time stamps is very short, consider // QPC as unstable and disable it completely. sUseQPC = false; LOG(("TimeStamp: QPC disabled")); } return false; }
// If the duration is less then two seconds, perform check of QPC stability // by comparing both GTC and QPC calculated durations of this and aOther. MFBT_API uint64_t TimeStampValue::CheckQPC(const TimeStampValue& aOther) const { uint64_t deltaGTC = mGTC - aOther.mGTC; if (!mHasQPC || !aOther.mHasQPC) { // Both not holding QPC return deltaGTC; } uint64_t deltaQPC = mQPC - aOther.mQPC; if (sHasStableTSC) { // For stable TSC there is no need to check return deltaQPC; } // Check QPC is sane before using it. int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC)); if (diff <= sGTCResolutionThreshold) { return deltaQPC; } // Treat absolutely for calibration purposes int64_t duration = DeprecatedAbs(int64_t(deltaGTC)); int64_t overflow = diff - sGTCResolutionThreshold; LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms", mt2ms(duration), mt2ms_f(overflow))); if (overflow <= sFailureThreshold) { // We are in the limit, let go. return deltaQPC; } // QPC deviates, don't use it, since now this method may only return deltaGTC. if (!sUseQPC) { // QPC already disabled, no need to run the fault tolerance algorithm. return deltaGTC; } LOG(("TimeStamp: QPC jittered over failure threshold")); if (duration < sHardFailureLimit) { // Interval between the two time stamps is very short, consider // QPC as unstable and record a failure. uint64_t now = ms2mt(sGetTickCount64()); AutoCriticalSection lock(&sTimeStampLock); if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) { // There's already been an error in the last fault intollerant interval. // Time since now to the checkpoint actually holds information on how many // failures there were in the failure free interval we have defined. uint64_t failureCount = (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) / sFailureFreeInterval; if (failureCount > kMaxFailuresPerInterval) { sUseQPC = false; LOG(("TimeStamp: QPC disabled")); } else { // Move the fault intolerance checkpoint more to the future, prolong it // to reflect the number of detected failures. ++failureCount; sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval; LOG(("TimeStamp: recording %dth QPC failure", failureCount)); } } else { // Setup fault intolerance checkpoint in the future for first detected error. sFaultIntoleranceCheckpoint = now + sFailureFreeInterval; LOG(("TimeStamp: recording 1st QPC failure")); } } return deltaGTC; }