Bug 1243241 - Make RDTSC monotonic. r=jandem

We assume that the total number of cycles spent executing JS code
during an event is equal to the number of cycles in the "top group",
i.e. a group to which everything belongs. While this is true in
theory, RDTSC is actually non-monotonic, so we can end up with fewer
cycles reported for the top group than for some groups whose execution
was actually shorter. When we end up in this situation, groups with
more cycles than the top group will be reported as using more CPU than
was actually used.

This patch fixes the situation by proxying RDTSC behind a trivial API
that ensures that values are monotonic during each tick.

--HG--
extra : transplant_source : %3E%8Aj%12e%B9%A7%08i%8Ef%28%F5%5D69q%15%8E%99
This commit is contained in:
David Rajchenbach-Teller 2016-01-28 14:33:30 +01:00
parent 8402fc035a
commit 68a3dd828b
2 changed files with 41 additions and 10 deletions

View File

@ -40,6 +40,14 @@ PerformanceMonitoring::reset()
// be overwritten progressively during the execution.
++iteration_;
recentGroups_.clear();
// Every so often, we will be rescheduled to another CPU. If this
// happens, we may end up with an entirely unsynchronized
// timestamp counter. If we do not reset
// `highestTimestampCounter_`, we could end up ignoring entirely
// valid sets of measures just because we are on a CPU that has a
// lower RDTSC.
highestTimestampCounter_ = 0;
}
void
@ -158,6 +166,19 @@ PerformanceMonitoring::commit()
return success;
}
uint64_t
PerformanceMonitoring::monotonicReadTimestampCounter()
{
#if defined(MOZ_HAVE_RDTSC)
const uint64_t hardware = ReadTimestampCounter();
if (highestTimestampCounter_ < hardware)
highestTimestampCounter_ = hardware;
return highestTimestampCounter_;
#else
return 0;
#endif // defined(MOZ_HAVE_RDTSC)
}
void
PerformanceMonitoring::dispose(JSRuntime* rt)
{
@ -272,7 +293,7 @@ AutoStopwatch::enter()
}
if (runtime->performanceMonitoring.isMonitoringJank()) {
cyclesStart_ = this->getCycles();
cyclesStart_ = this->getCycles(runtime);
cpuStart_ = this->getCPU();
isMonitoringJank_ = true;
}
@ -295,8 +316,8 @@ AutoStopwatch::exit()
// limited.
const cpuid_t cpuEnd = this->getCPU();
if (isSameCPU(cpuStart_, cpuEnd)) {
const uint64_t cyclesEnd = getCycles();
cyclesDelta = getDelta(cyclesEnd, cyclesStart_);
const uint64_t cyclesEnd = getCycles(runtime);
cyclesDelta = cyclesEnd - cyclesStart_; // Always >= 0 by definition of `getCycles`.
}
#if WINVER >= 0x600
updateTelemetry(cpuStart_, cpuEnd);
@ -382,13 +403,9 @@ AutoStopwatch::getDelta(const uint64_t end, const uint64_t start) const
}
uint64_t
AutoStopwatch::getCycles() const
AutoStopwatch::getCycles(JSRuntime* runtime) const
{
#if defined(MOZ_HAVE_RDTSC)
return ReadTimestampCounter();
#else
return 0;
#endif // defined(MOZ_HAVE_RDTSC)
return runtime->performanceMonitoring.monotonicReadTimestampCounter();
}
cpuid_t inline

View File

@ -85,6 +85,7 @@ struct PerformanceMonitoring {
, isMonitoringCPOW_(false)
, iteration_(0)
, startedAtIteration_(0)
, highestTimestampCounter_(0)
{ }
/**
@ -211,6 +212,13 @@ struct PerformanceMonitoring {
*/
uint64_t totalCPOWTime;
/**
* A variant of RDTSC artificially made monotonic.
*
* Always return 0 on platforms that do not support RDTSC.
*/
uint64_t monotonicReadTimestampCounter();
/**
* Data extracted by the AutoStopwatch to determine how often
* we reschedule the process to a different CPU during the
@ -285,6 +293,12 @@ struct PerformanceMonitoring {
* Groups used in the current iteration.
*/
GroupVector recentGroups_;
/**
* The highest value of the timestamp counter encountered
* during this iteration.
*/
uint64_t highestTimestampCounter_;
};
#if WINVER >= 0x0600
@ -377,7 +391,7 @@ class AutoStopwatch final {
// Return the value of the Timestamp Counter, as provided by the CPU.
// 0 on platforms for which we do not have access to a Timestamp Counter.
uint64_t inline getCycles() const;
uint64_t inline getCycles(JSRuntime*) const;
// Return the identifier of the current CPU, on platforms for which we have