diff --git a/xpcom/threads/CPUUsageWatcher.cpp b/xpcom/threads/CPUUsageWatcher.cpp index ee9731b0bf54..e00a4761d289 100644 --- a/xpcom/threads/CPUUsageWatcher.cpp +++ b/xpcom/threads/CPUUsageWatcher.cpp @@ -9,18 +9,15 @@ #include "prsystem.h" #ifdef XP_MACOSX +#include +#include #include #endif -// We only support OSX and Windows, because on Linux we're forced to read -// from /proc/stat in order to get global CPU values. We would prefer to not -// eat that cost for this. -#if defined(NIGHTLY_BUILD) && (defined(XP_WIN) || defined(XP_MACOSX)) -#define CPU_USAGE_WATCHER_ACTIVE -#endif - namespace mozilla { +#ifdef CPU_USAGE_WATCHER_ACTIVE + // Even if the machine only has one processor, tolerate up to 50% // external CPU usage. static const float kTolerableExternalCPUUsageFloor = 0.5f; @@ -37,26 +34,44 @@ struct CPUStats { #ifdef XP_MACOSX -static const uint64_t kNanosecondsPerSecond = 1000000000LL; -static const uint64_t kCPUCheckInterval = kNanosecondsPerSecond / 2LL; +static const uint64_t kMicrosecondsPerSecond = 1000000LL; +static const uint64_t kNanosecondsPerMicrosecond = 1000LL; +static const uint64_t kCPUCheckInterval = kMicrosecondsPerSecond / 2LL; -Result -GetClockTime(clockid_t clockId) { - timespec clockResult; - bool success = !clock_gettime(clockId, &clockResult); - if (!success) { - return Err(ClockGetTimeError); - } - return ((uint64_t)clockResult.tv_sec) * kNanosecondsPerSecond + - (uint64_t)clockResult.tv_nsec; +uint64_t GetMicroseconds(timeval time) { + return ((uint64_t)time.tv_sec) * kMicrosecondsPerSecond + + (uint64_t)time.tv_usec; +} + +uint64_t GetMicroseconds(mach_timespec_t time) { + return ((uint64_t)time.tv_sec) * kMicrosecondsPerSecond + + ((uint64_t)time.tv_nsec) / kNanosecondsPerMicrosecond; } Result GetProcessCPUStats(int32_t numCPUs) { CPUStats result = {}; - MOZ_TRY_VAR(result.usageTime, GetClockTime(CLOCK_PROCESS_CPUTIME_ID)); - MOZ_TRY_VAR(result.updateTime, GetClockTime(CLOCK_MONOTONIC)); - // CLOCK_PROCESS_CPUTIME_ID will give us the sum of the values across all + rusage usage; + int32_t rusageResult = getrusage(RUSAGE_SELF, &usage); + if (rusageResult == -1) { + return Err(GetProcessTimesError); + } + result.usageTime = GetMicroseconds(usage.ru_utime) + GetMicroseconds(usage.ru_stime); + + clock_serv_t realtimeClock; + kern_return_t errorResult = + host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &realtimeClock); + if (errorResult != KERN_SUCCESS) { + return Err(GetProcessTimesError); + } + mach_timespec_t time; + errorResult = clock_get_time(realtimeClock, &time); + if (errorResult != KERN_SUCCESS) { + return Err(GetProcessTimesError); + } + result.updateTime = GetMicroseconds(time); + + // getrusage will give us the sum of the values across all // of our cores. Divide by the number of CPUs to get an average. result.usageTime /= numCPUs; return result; @@ -157,8 +172,6 @@ CPUUsageWatcher::Init() mExternalUsageThreshold = std::max(1.0f - 1.0f / (float)mNumCPUs, kTolerableExternalCPUUsageFloor); -#ifdef CPU_USAGE_WATCHER_ACTIVE - CPUStats processTimes; MOZ_TRY_VAR(processTimes, GetProcessCPUStats(mNumCPUs)); mProcessUpdateTime = processTimes.updateTime; @@ -176,18 +189,16 @@ CPUUsageWatcher::Init() NS_NewRunnableFunction("CPUUsageWatcher::Init", [=]() { HangMonitor::RegisterAnnotator(*self); })); -#endif // CPU_USAGE_WATCHER_ACTIVE return Ok(); } void CPUUsageWatcher::Uninit() { + if (mInitialized) { + HangMonitor::UnregisterAnnotator(*this); + } mInitialized = false; - -#ifdef CPU_USAGE_WATCHER_ACTIVE - HangMonitor::UnregisterAnnotator(*this); -#endif // CPU_USAGE_WATCHER_ACTIVE } Result @@ -197,7 +208,6 @@ CPUUsageWatcher::CollectCPUUsage() return Ok(); } -#ifdef CPU_USAGE_WATCHER_ACTIVE mExternalUsageRatio = 0.0f; CPUStats processTimes; @@ -224,7 +234,6 @@ CPUUsageWatcher::CollectCPUUsage() mExternalUsageRatio = std::max(0.0f, globalUsageNormalized - processUsageNormalized); -#endif // CPU_USAGE_WATCHER_ACTIVE return Ok(); } @@ -240,4 +249,24 @@ CPUUsageWatcher::AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) { } } +#else // !CPU_USAGE_WATCHER_ACTIVE + +Result +CPUUsageWatcher::Init() +{ + return Ok(); +} + +void CPUUsageWatcher::Uninit() {} + +Result +CPUUsageWatcher::CollectCPUUsage() +{ + return Ok(); +} + +void CPUUsageWatcher::AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) {} + +#endif // CPU_USAGE_WATCHER_ACTIVE + } // namespace mozilla diff --git a/xpcom/threads/CPUUsageWatcher.h b/xpcom/threads/CPUUsageWatcher.h index 343bf8547364..5e8e58981237 100644 --- a/xpcom/threads/CPUUsageWatcher.h +++ b/xpcom/threads/CPUUsageWatcher.h @@ -11,6 +11,13 @@ #include "mozilla/HangAnnotations.h" +// We only support OSX and Windows, because on Linux we're forced to read +// from /proc/stat in order to get global CPU values. We would prefer to not +// eat that cost for this. +#if defined(NIGHTLY_BUILD) && (defined(XP_WIN) || defined(XP_MACOSX)) +#define CPU_USAGE_WATCHER_ACTIVE +#endif + namespace mozilla { enum CPUUsageWatcherError : uint8_t @@ -33,6 +40,7 @@ class CPUUsageWatcher : public HangMonitor::Annotator { public: +#ifdef CPU_USAGE_WATCHER_ACTIVE CPUUsageWatcher() : mInitialized(false) , mExternalUsageThreshold(0) @@ -42,6 +50,7 @@ public: , mGlobalUsageTime(0) , mGlobalUpdateTime(0) {} +#endif Result Init(); @@ -54,6 +63,7 @@ public: void AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) final; private: +#ifdef CPU_USAGE_WATCHER_ACTIVE bool mInitialized; // The threshold above which we will mark a hang as occurring under high // external CPU usage conditions @@ -75,6 +85,7 @@ private: uint64_t mGlobalUpdateTime; // The number of virtual cores on our machine uint64_t mNumCPUs; +#endif }; } // namespace mozilla