зеркало из https://github.com/mozilla/gecko-dev.git
244 строки
7.4 KiB
C++
244 строки
7.4 KiB
C++
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
|
||
|
#include "mozilla/CPUUsageWatcher.h"
|
||
|
|
||
|
#include "prsystem.h"
|
||
|
|
||
|
#ifdef XP_MACOSX
|
||
|
#include <mach/mach_host.h>
|
||
|
#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 {
|
||
|
|
||
|
// Even if the machine only has one processor, tolerate up to 50%
|
||
|
// external CPU usage.
|
||
|
static const float kTolerableExternalCPUUsageFloor = 0.5f;
|
||
|
|
||
|
struct CPUStats {
|
||
|
// The average CPU usage time, which can be summed across all cores in the
|
||
|
// system, or averaged between them. Whichever it is, it needs to be in the
|
||
|
// same units as updateTime.
|
||
|
uint64_t usageTime;
|
||
|
// A monotonically increasing value in the same units as usageTime, which can
|
||
|
// be used to determine the percentage of active vs idle time
|
||
|
uint64_t updateTime;
|
||
|
};
|
||
|
|
||
|
#ifdef XP_MACOSX
|
||
|
|
||
|
static const uint64_t kNanosecondsPerSecond = 1000000000LL;
|
||
|
static const uint64_t kCPUCheckInterval = kNanosecondsPerSecond / 2LL;
|
||
|
|
||
|
Result<uint64_t, CPUUsageWatcherError>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
Result<CPUStats, CPUUsageWatcherError>
|
||
|
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
|
||
|
// of our cores. Divide by the number of CPUs to get an average.
|
||
|
result.usageTime /= numCPUs;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Result<CPUStats, CPUUsageWatcherError>
|
||
|
GetGlobalCPUStats() {
|
||
|
CPUStats result = {};
|
||
|
host_cpu_load_info_data_t loadInfo;
|
||
|
mach_msg_type_number_t loadInfoCount = HOST_CPU_LOAD_INFO_COUNT;
|
||
|
kern_return_t statsResult = host_statistics(mach_host_self(),
|
||
|
HOST_CPU_LOAD_INFO,
|
||
|
(host_info_t)&loadInfo,
|
||
|
&loadInfoCount);
|
||
|
if (statsResult != KERN_SUCCESS) {
|
||
|
return Err(HostStatisticsError);
|
||
|
}
|
||
|
|
||
|
result.usageTime = loadInfo.cpu_ticks[CPU_STATE_USER] +
|
||
|
loadInfo.cpu_ticks[CPU_STATE_NICE] +
|
||
|
loadInfo.cpu_ticks[CPU_STATE_SYSTEM];
|
||
|
result.updateTime = result.usageTime + loadInfo.cpu_ticks[CPU_STATE_IDLE];
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#endif // XP_MACOSX
|
||
|
|
||
|
#ifdef XP_WIN
|
||
|
|
||
|
// A FILETIME represents the number of 100-nanosecond ticks since 1/1/1601 UTC
|
||
|
static const uint64_t kFILETIMETicksPerSecond = 10000000;
|
||
|
static const uint64_t kCPUCheckInterval = kFILETIMETicksPerSecond / 2;
|
||
|
|
||
|
uint64_t
|
||
|
FiletimeToInteger(FILETIME filetime) {
|
||
|
return ((uint64_t)filetime.dwLowDateTime) |
|
||
|
(uint64_t)filetime.dwHighDateTime << 32;
|
||
|
}
|
||
|
|
||
|
Result<CPUStats, CPUUsageWatcherError> GetProcessCPUStats(int32_t numCPUs) {
|
||
|
CPUStats result = {};
|
||
|
FILETIME creationFiletime;
|
||
|
FILETIME exitFiletime;
|
||
|
FILETIME kernelFiletime;
|
||
|
FILETIME userFiletime;
|
||
|
bool success = GetProcessTimes(GetCurrentProcess(),
|
||
|
&creationFiletime,
|
||
|
&exitFiletime,
|
||
|
&kernelFiletime,
|
||
|
&userFiletime);
|
||
|
if (!success) {
|
||
|
return Err(GetProcessTimesError);
|
||
|
}
|
||
|
|
||
|
result.usageTime = FiletimeToInteger(kernelFiletime) +
|
||
|
FiletimeToInteger(userFiletime);
|
||
|
|
||
|
FILETIME nowFiletime;
|
||
|
GetSystemTimeAsFileTime(&nowFiletime);
|
||
|
result.updateTime = FiletimeToInteger(nowFiletime);
|
||
|
|
||
|
result.usageTime /= numCPUs;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Result<CPUStats, CPUUsageWatcherError>
|
||
|
GetGlobalCPUStats() {
|
||
|
CPUStats result = {};
|
||
|
FILETIME idleFiletime;
|
||
|
FILETIME kernelFiletime;
|
||
|
FILETIME userFiletime;
|
||
|
bool success = GetSystemTimes(&idleFiletime,
|
||
|
&kernelFiletime,
|
||
|
&userFiletime);
|
||
|
|
||
|
if (!success) {
|
||
|
return Err(GetSystemTimesError);
|
||
|
}
|
||
|
|
||
|
result.usageTime = FiletimeToInteger(kernelFiletime) +
|
||
|
FiletimeToInteger(userFiletime);
|
||
|
result.updateTime = result.usageTime + FiletimeToInteger(idleFiletime);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#endif // XP_WIN
|
||
|
|
||
|
Result<Ok, CPUUsageWatcherError>
|
||
|
CPUUsageWatcher::Init()
|
||
|
{
|
||
|
mNumCPUs = PR_GetNumberOfProcessors();
|
||
|
if (mNumCPUs <= 0) {
|
||
|
mExternalUsageThreshold = 1.0f;
|
||
|
return Err(GetNumberOfProcessorsError);
|
||
|
}
|
||
|
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;
|
||
|
mProcessUsageTime = processTimes.usageTime;
|
||
|
|
||
|
CPUStats globalTimes;
|
||
|
MOZ_TRY_VAR(globalTimes, GetGlobalCPUStats());
|
||
|
mGlobalUpdateTime = globalTimes.updateTime;
|
||
|
mGlobalUsageTime = globalTimes.usageTime;
|
||
|
|
||
|
mInitialized = true;
|
||
|
|
||
|
CPUUsageWatcher* self = this;
|
||
|
NS_DispatchToMainThread(
|
||
|
NS_NewRunnableFunction("CPUUsageWatcher::Init",
|
||
|
[=]() { HangMonitor::RegisterAnnotator(*self); }));
|
||
|
|
||
|
#endif // CPU_USAGE_WATCHER_ACTIVE
|
||
|
return Ok();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CPUUsageWatcher::Uninit()
|
||
|
{
|
||
|
mInitialized = false;
|
||
|
|
||
|
#ifdef CPU_USAGE_WATCHER_ACTIVE
|
||
|
HangMonitor::UnregisterAnnotator(*this);
|
||
|
#endif // CPU_USAGE_WATCHER_ACTIVE
|
||
|
}
|
||
|
|
||
|
Result<Ok, CPUUsageWatcherError>
|
||
|
CPUUsageWatcher::CollectCPUUsage()
|
||
|
{
|
||
|
if (!mInitialized) {
|
||
|
return Ok();
|
||
|
}
|
||
|
|
||
|
#ifdef CPU_USAGE_WATCHER_ACTIVE
|
||
|
mExternalUsageRatio = 0.0f;
|
||
|
|
||
|
CPUStats processTimes;
|
||
|
MOZ_TRY_VAR(processTimes, GetProcessCPUStats(mNumCPUs));
|
||
|
CPUStats globalTimes;
|
||
|
MOZ_TRY_VAR(globalTimes, GetGlobalCPUStats());
|
||
|
|
||
|
uint64_t processUsageDelta = processTimes.usageTime - mProcessUsageTime;
|
||
|
uint64_t processUpdateDelta = processTimes.updateTime - mProcessUpdateTime;
|
||
|
float processUsageNormalized = processUsageDelta > 0 ?
|
||
|
(float)processUsageDelta / (float)processUpdateDelta :
|
||
|
0.0f;
|
||
|
|
||
|
uint64_t globalUsageDelta = globalTimes.usageTime - mGlobalUsageTime;
|
||
|
uint64_t globalUpdateDelta = globalTimes.updateTime - mGlobalUpdateTime;
|
||
|
float globalUsageNormalized = globalUsageDelta > 0 ?
|
||
|
(float)globalUsageDelta / (float)globalUpdateDelta :
|
||
|
0.0f;
|
||
|
|
||
|
mProcessUsageTime = processTimes.usageTime;
|
||
|
mProcessUpdateTime = processTimes.updateTime;
|
||
|
mGlobalUsageTime = globalTimes.usageTime;
|
||
|
mGlobalUpdateTime = globalTimes.updateTime;
|
||
|
|
||
|
mExternalUsageRatio = std::max(0.0f,
|
||
|
globalUsageNormalized - processUsageNormalized);
|
||
|
#endif // CPU_USAGE_WATCHER_ACTIVE
|
||
|
|
||
|
return Ok();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CPUUsageWatcher::AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) {
|
||
|
if (!mInitialized) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mExternalUsageRatio > mExternalUsageThreshold) {
|
||
|
aAnnotations.AddAnnotation(NS_LITERAL_STRING("ExternalCPUHigh"), true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace mozilla
|