Global Metrics
path: .metrics.loc.lloc
old: 3.0
new: 28.0
path: .metrics.loc.blank
old: 7.0
new: 21.0
path: .metrics.loc.sloc
old: 27.0
new: 235.0
path: .metrics.loc.cloc
old: 6.0
new: 118.0
path: .metrics.loc.ploc
old: 14.0
new: 96.0
path: .metrics.cognitive.average
old: 2.0
new: 1.8333333333333333
path: .metrics.cognitive.sum
old: 2.0
new: 11.0
path: .metrics.nom.functions
old: 1.0
new: 6.0
path: .metrics.nom.total
old: 1.0
new: 6.0
path: .metrics.mi.mi_visual_studio
old: 51.60071540982331
new: 22.013139579190376
path: .metrics.mi.mi_sei
old: 85.36036855463864
new: 24.948266530208237
path: .metrics.mi.mi_original
old: 88.23722335079788
new: 37.64246868041555
path: .metrics.halstead.N2
old: 19.0
new: 180.0
path: .metrics.halstead.purity_ratio
old: 2.032458178855633
new: 1.1067451937823205
path: .metrics.halstead.N1
old: 31.0
new: 221.0
path: .metrics.halstead.length
old: 50.0
new: 401.0
path: .metrics.halstead.n2
old: 12.0
new: 58.0
path: .metrics.halstead.level
old: 0.08421052631578947
new: 0.028019323671497585
path: .metrics.halstead.n1
old: 15.0
new: 23.0
path: .metrics.halstead.estimated_program_length
old: 101.62290894278166
new: 443.8048227067104
path: .metrics.halstead.difficulty
old: 11.875
new: 35.689655172413794
path: .metrics.halstead.bugs
old: 0.06658473227477106
new: 0.6730632581837971
path: .metrics.halstead.vocabulary
old: 27.0
new: 81.0
path: .metrics.halstead.volume
old: 237.7443751081734
new: 2542.2798511567344
path: .metrics.halstead.time
old: 156.84524746719774
new: 5040.727291086629
path: .metrics.halstead.effort
old: 2823.2144544095595
new: 90733.09123955932
path: .metrics.cyclomatic.average
old: 1.3333333333333333
new: 2.0
path: .metrics.cyclomatic.sum
old: 4.0
new: 18.0
path: .metrics.nexits.sum
old: 2.0
new: 5.0
path: .metrics.nexits.average
old: 2.0
new: 0.8333333333333334
path: .metrics.nargs.sum
old: 1.0
new: 11.0
path: .metrics.nargs.average
old: 1.0
new: 1.8333333333333333
Spaces Data
Minimal test - lines (13, 233)
path: .spaces[0].metrics.nom.total
old: 0.0
new: 6.0
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 6.0
path: .spaces[0].metrics.nargs.average
old: null
new: 1.8333333333333333
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 11.0
path: .spaces[0].metrics.mi.mi_sei
old: null
new: 26.828141924939793
path: .spaces[0].metrics.mi.mi_original
old: null
new: 38.93623603271952
path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 22.769728674104982
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 5.0
path: .spaces[0].metrics.nexits.average
old: null
new: 0.8333333333333334
path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 56.0
path: .spaces[0].metrics.halstead.time
old: 0.0
new: 5066.344835059492
path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 429.2538006245371
path: .spaces[0].metrics.halstead.length
old: 1.0
new: 398.0
path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 36.348214285714285
path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.0785271372475806
path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 79.0
path: .spaces[0].metrics.halstead.level
old: null
new: 0.02751166789486613
path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 91194.20703107084
path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 221.0
path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 0.6753417198709843
path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 177.0
path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 23.0
path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 2508.904737774487
path: .spaces[0].metrics.cognitive.average
old: null
new: 1.8333333333333333
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 11.0
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 17.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.125
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 19.0
path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 90.0
path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 221.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 28.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 112.0
Code
namespace mozilla {
// Utility class that converts time values represented as an unsigned integral
// number of milliseconds from one time source (e.g. a native event time) to
// corresponding mozilla::TimeStamp objects.
//
// This class handles wrapping of integer values and skew between the time
// source and mozilla::TimeStamp values.
//
// It does this by using an historical reference time recorded in both time
// scales (i.e. both as a numerical time value and as a TimeStamp).
//
// For performance reasons, this class is careful to minimize calls to the
// native "current time" function (e.g. gdk_x11_server_get_time) since this can
// be slow.
template
class SystemTimeConverter {
public:
SystemTimeConverter()
: mReferenceTime(Time(0)),
mReferenceTimeStamp() // Initializes to the null timestamp
,
mLastBackwardsSkewCheck(Time(0)),
kTimeRange(std::numeric_limits::max()),
kTimeHalfRange(kTimeRange / 2),
kBackwardsSkewCheckInterval(Time(2000)) {
static_assert(!std::is_signed_v, "Expected Time to be unsigned");
}
template
mozilla::TimeStamp GetTimeStampFromSystemTime(
Time aTime, CurrentTimeGetter& aCurrentTimeGetter) {
TimeStamp roughlyNow = TimeStampNowProvider::Now();
// If the reference time is not set, use the current time value to fill
// it in.
if (mReferenceTimeStamp.IsNull()) {
// This sometimes happens when ::GetMessageTime returns 0 for the first
// message on Windows.
if (!aTime) return roughlyNow;
UpdateReferenceTime(aTime, aCurrentTimeGetter);
}
// Check for skew between the source of Time values and TimeStamp values.
// We do this by comparing two durations (both in ms):
//
// i. The duration from the reference time to the passed-in time.
// (timeDelta in the diagram below)
// ii. The duration from the reference timestamp to the current time
// based on TimeStamp::Now.
// (timeStampDelta in the diagram below)
//
// Normally, we'd expect (ii) to be slightly larger than (i) to account
// for the time taken between generating the event and processing it.
//
// If (ii) - (i) is negative then the source of Time values is getting
// "ahead" of TimeStamp. We call this "forwards" skew below.
//
// For the reverse case, if (ii) - (i) is positive (and greater than some
// tolerance factor), then we may have "backwards" skew. This is often
// the case when we have a backlog of events and by the time we process
// them, the time given by the system is comparatively "old".
//
// The IsNewerThanTimestamp function computes the equivalent of |aTime| in
// the TimeStamp scale and returns that in |timeAsTimeStamp|.
//
// Graphically:
//
// mReferenceTime aTime
// Time scale: ........+.......................*........
// |--------timeDelta------|
//
// mReferenceTimeStamp roughlyNow
// TimeStamp scale: ........+...........................*....
// |------timeStampDelta-------|
//
// |---|
// roughlyNow-timeAsTimeStamp
//
TimeStamp timeAsTimeStamp;
bool newer = IsTimeNewerThanTimestamp(aTime, roughlyNow, &timeAsTimeStamp);
// Tolerance when detecting clock skew.
static const TimeDuration kTolerance = TimeDuration::FromMilliseconds(30.0);
// Check for forwards skew
if (newer) {
// Make aTime correspond to roughlyNow
UpdateReferenceTime(aTime, roughlyNow);
// We didn't have backwards skew so don't bother checking for
// backwards skew again for a little while.
mLastBackwardsSkewCheck = aTime;
return roughlyNow;
}
if (roughlyNow - timeAsTimeStamp <= kTolerance) {
// If the time between event times and TimeStamp values is within
// the tolerance then assume we don't have clock skew so we can
// avoid checking for backwards skew for a while.
mLastBackwardsSkewCheck = aTime;
} else if (aTime - mLastBackwardsSkewCheck > kBackwardsSkewCheckInterval) {
aCurrentTimeGetter.GetTimeAsyncForPossibleBackwardsSkew(roughlyNow);
mLastBackwardsSkewCheck = aTime;
}
// Finally, calculate the timestamp
return timeAsTimeStamp;
}
void CompensateForBackwardsSkew(Time aReferenceTime,
const TimeStamp& aLowerBound) {
// Check if we actually have backwards skew. Backwards skew looks like
// the following:
//
// mReferenceTime
// Time: ..+...a...b...c..........................
//
// mReferenceTimeStamp
// TimeStamp: ..+.....a.....b.....c....................
//
// Converted
// time: ......a'..b'..c'.........................
//
// What we need to do is bring mReferenceTime "forwards".
//
// Suppose when we get (c), we detect possible backwards skew and trigger
// an async request for the current time (which is passed in here as
// aReferenceTime).
//
// We end up with something like the following:
//
// mReferenceTime aReferenceTime
// Time: ..+...a...b...c...v......................
//
// mReferenceTimeStamp
// TimeStamp: ..+.....a.....b.....c..........x.........
// ^ ^
// aLowerBound TimeStamp::Now()
//
// If the duration (aLowerBound - mReferenceTimeStamp) is greater than
// (aReferenceTime - mReferenceTime) then we know we have backwards skew.
//
// If that's not the case, then we probably just got caught behind
// temporarily.
if (IsTimeNewerThanTimestamp(aReferenceTime, aLowerBound, nullptr)) {
return;
}
// We have backwards skew; the equivalent TimeStamp for aReferenceTime lies
// somewhere between aLowerBound (which was the TimeStamp when we triggered
// the async request for the current time) and TimeStamp::Now().
//
// If aReferenceTime was waiting in the event queue for a long time, the
// equivalent TimeStamp might be much closer to aLowerBound than
// TimeStamp::Now() so for now we just set it to aLowerBound. That's
// guaranteed to be at least somewhat of an improvement.
UpdateReferenceTime(aReferenceTime, aLowerBound);
}
private:
template
void UpdateReferenceTime(Time aReferenceTime,
const CurrentTimeGetter& aCurrentTimeGetter) {
Time currentTime = aCurrentTimeGetter.GetCurrentTime();
TimeStamp currentTimeStamp = TimeStampNowProvider::Now();
Time timeSinceReference = currentTime - aReferenceTime;
TimeStamp referenceTimeStamp =
currentTimeStamp - TimeDuration::FromMilliseconds(timeSinceReference);
UpdateReferenceTime(aReferenceTime, referenceTimeStamp);
}
void UpdateReferenceTime(Time aReferenceTime,
const TimeStamp& aReferenceTimeStamp) {
mReferenceTime = aReferenceTime;
mReferenceTimeStamp = aReferenceTimeStamp;
}
bool IsTimeNewerThanTimestamp(Time aTime, TimeStamp aTimeStamp,
TimeStamp* aTimeAsTimeStamp) {
Time timeDelta = aTime - mReferenceTime;
// Cast the result to signed 64-bit integer first since that should be
// enough to hold the range of values returned by ToMilliseconds() and
// the result of converting from double to an integer-type when the value
// is outside the integer range is undefined.
// Then we do an implicit cast to Time (typically an unsigned 32-bit
// integer) which wraps times outside that range.
TimeDuration timeStampDelta = (aTimeStamp - mReferenceTimeStamp);
int64_t wholeMillis = static_cast(timeStampDelta.ToMilliseconds());
Time wrappedTimeStampDelta = wholeMillis; // truncate to unsigned
Time timeToTimeStamp = wrappedTimeStampDelta - timeDelta;
bool isNewer = false;
if (timeToTimeStamp == 0) {
// wholeMillis needs no adjustment
} else if (timeToTimeStamp < kTimeHalfRange) {
wholeMillis -= timeToTimeStamp;
} else {
isNewer = true;
wholeMillis += (-timeToTimeStamp);
}
if (aTimeAsTimeStamp) {
*aTimeAsTimeStamp =
mReferenceTimeStamp + TimeDuration::FromMilliseconds(wholeMillis);
}
return isNewer;
}
Time mReferenceTime;
TimeStamp mReferenceTimeStamp;
Time mLastBackwardsSkewCheck;
const Time kTimeRange;
const Time kTimeHalfRange;
const Time kBackwardsSkewCheckInterval;
};
} // namespace mozilla