Bug 1755481: correct documentation of `nsIClipboard::getData`. r=mccr8
[gecko.git] / xpcom / threads / CPUUsageWatcher.cpp
blob922ca81e8d6cf11fe3eecd20d21cf7dea980cb3a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/CPUUsageWatcher.h"
9 #include "prsystem.h"
11 #ifdef XP_MACOSX
12 # include <sys/resource.h>
13 # include <mach/clock.h>
14 # include <mach/mach_host.h>
15 #endif
17 #ifdef CPU_USAGE_WATCHER_ACTIVE
18 # include "mozilla/BackgroundHangMonitor.h"
19 #endif
21 namespace mozilla {
23 #ifdef CPU_USAGE_WATCHER_ACTIVE
25 // Even if the machine only has one processor, tolerate up to 50%
26 // external CPU usage.
27 static const float kTolerableExternalCPUUsageFloor = 0.5f;
29 struct CPUStats {
30 // The average CPU usage time, which can be summed across all cores in the
31 // system, or averaged between them. Whichever it is, it needs to be in the
32 // same units as updateTime.
33 uint64_t usageTime;
34 // A monotonically increasing value in the same units as usageTime, which can
35 // be used to determine the percentage of active vs idle time
36 uint64_t updateTime;
39 # ifdef XP_MACOSX
41 static const uint64_t kMicrosecondsPerSecond = 1000000LL;
42 static const uint64_t kNanosecondsPerMicrosecond = 1000LL;
44 static uint64_t GetMicroseconds(timeval time) {
45 return ((uint64_t)time.tv_sec) * kMicrosecondsPerSecond +
46 (uint64_t)time.tv_usec;
49 static uint64_t GetMicroseconds(mach_timespec_t time) {
50 return ((uint64_t)time.tv_sec) * kMicrosecondsPerSecond +
51 ((uint64_t)time.tv_nsec) / kNanosecondsPerMicrosecond;
54 static Result<CPUStats, CPUUsageWatcherError> GetProcessCPUStats(
55 int32_t numCPUs) {
56 CPUStats result = {};
57 rusage usage;
58 int32_t rusageResult = getrusage(RUSAGE_SELF, &usage);
59 if (rusageResult == -1) {
60 return Err(GetProcessTimesError);
62 result.usageTime =
63 GetMicroseconds(usage.ru_utime) + GetMicroseconds(usage.ru_stime);
65 clock_serv_t realtimeClock;
66 kern_return_t errorResult =
67 host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &realtimeClock);
68 if (errorResult != KERN_SUCCESS) {
69 return Err(GetProcessTimesError);
71 mach_timespec_t time;
72 errorResult = clock_get_time(realtimeClock, &time);
73 if (errorResult != KERN_SUCCESS) {
74 return Err(GetProcessTimesError);
76 result.updateTime = GetMicroseconds(time);
78 // getrusage will give us the sum of the values across all
79 // of our cores. Divide by the number of CPUs to get an average.
80 result.usageTime /= numCPUs;
81 return result;
84 static Result<CPUStats, CPUUsageWatcherError> GetGlobalCPUStats() {
85 CPUStats result = {};
86 host_cpu_load_info_data_t loadInfo;
87 mach_msg_type_number_t loadInfoCount = HOST_CPU_LOAD_INFO_COUNT;
88 kern_return_t statsResult =
89 host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
90 (host_info_t)&loadInfo, &loadInfoCount);
91 if (statsResult != KERN_SUCCESS) {
92 return Err(HostStatisticsError);
95 result.usageTime = loadInfo.cpu_ticks[CPU_STATE_USER] +
96 loadInfo.cpu_ticks[CPU_STATE_NICE] +
97 loadInfo.cpu_ticks[CPU_STATE_SYSTEM];
98 result.updateTime = result.usageTime + loadInfo.cpu_ticks[CPU_STATE_IDLE];
99 return result;
102 # endif // XP_MACOSX
104 # ifdef XP_WIN
106 // A FILETIME represents the number of 100-nanosecond ticks since 1/1/1601 UTC
107 uint64_t FiletimeToInteger(FILETIME filetime) {
108 return ((uint64_t)filetime.dwLowDateTime) | (uint64_t)filetime.dwHighDateTime
109 << 32;
112 Result<CPUStats, CPUUsageWatcherError> GetProcessCPUStats(int32_t numCPUs) {
113 CPUStats result = {};
114 FILETIME creationFiletime;
115 FILETIME exitFiletime;
116 FILETIME kernelFiletime;
117 FILETIME userFiletime;
118 bool success = GetProcessTimes(GetCurrentProcess(), &creationFiletime,
119 &exitFiletime, &kernelFiletime, &userFiletime);
120 if (!success) {
121 return Err(GetProcessTimesError);
124 result.usageTime =
125 FiletimeToInteger(kernelFiletime) + FiletimeToInteger(userFiletime);
127 FILETIME nowFiletime;
128 GetSystemTimeAsFileTime(&nowFiletime);
129 result.updateTime = FiletimeToInteger(nowFiletime);
131 result.usageTime /= numCPUs;
133 return result;
136 Result<CPUStats, CPUUsageWatcherError> GetGlobalCPUStats() {
137 CPUStats result = {};
138 FILETIME idleFiletime;
139 FILETIME kernelFiletime;
140 FILETIME userFiletime;
141 bool success = GetSystemTimes(&idleFiletime, &kernelFiletime, &userFiletime);
143 if (!success) {
144 return Err(GetSystemTimesError);
147 result.usageTime =
148 FiletimeToInteger(kernelFiletime) + FiletimeToInteger(userFiletime);
149 result.updateTime = result.usageTime + FiletimeToInteger(idleFiletime);
151 return result;
154 # endif // XP_WIN
156 Result<Ok, CPUUsageWatcherError> CPUUsageWatcher::Init() {
157 mNumCPUs = PR_GetNumberOfProcessors();
158 if (mNumCPUs <= 0) {
159 mExternalUsageThreshold = 1.0f;
160 return Err(GetNumberOfProcessorsError);
162 mExternalUsageThreshold =
163 std::max(1.0f - 1.0f / (float)mNumCPUs, kTolerableExternalCPUUsageFloor);
165 CPUStats processTimes;
166 MOZ_TRY_VAR(processTimes, GetProcessCPUStats(mNumCPUs));
167 mProcessUpdateTime = processTimes.updateTime;
168 mProcessUsageTime = processTimes.usageTime;
170 CPUStats globalTimes;
171 MOZ_TRY_VAR(globalTimes, GetGlobalCPUStats());
172 mGlobalUpdateTime = globalTimes.updateTime;
173 mGlobalUsageTime = globalTimes.usageTime;
175 mInitialized = true;
177 CPUUsageWatcher* self = this;
178 NS_DispatchToMainThread(NS_NewRunnableFunction(
179 "CPUUsageWatcher::Init",
180 [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
182 return Ok();
185 void CPUUsageWatcher::Uninit() {
186 if (mInitialized) {
187 BackgroundHangMonitor::UnregisterAnnotator(*this);
189 mInitialized = false;
192 Result<Ok, CPUUsageWatcherError> CPUUsageWatcher::CollectCPUUsage() {
193 if (!mInitialized) {
194 return Ok();
197 mExternalUsageRatio = 0.0f;
199 CPUStats processTimes;
200 MOZ_TRY_VAR(processTimes, GetProcessCPUStats(mNumCPUs));
201 CPUStats globalTimes;
202 MOZ_TRY_VAR(globalTimes, GetGlobalCPUStats());
204 uint64_t processUsageDelta = processTimes.usageTime - mProcessUsageTime;
205 uint64_t processUpdateDelta = processTimes.updateTime - mProcessUpdateTime;
206 float processUsageNormalized =
207 processUsageDelta > 0
208 ? (float)processUsageDelta / (float)processUpdateDelta
209 : 0.0f;
211 uint64_t globalUsageDelta = globalTimes.usageTime - mGlobalUsageTime;
212 uint64_t globalUpdateDelta = globalTimes.updateTime - mGlobalUpdateTime;
213 float globalUsageNormalized =
214 globalUsageDelta > 0 ? (float)globalUsageDelta / (float)globalUpdateDelta
215 : 0.0f;
217 mProcessUsageTime = processTimes.usageTime;
218 mProcessUpdateTime = processTimes.updateTime;
219 mGlobalUsageTime = globalTimes.usageTime;
220 mGlobalUpdateTime = globalTimes.updateTime;
222 mExternalUsageRatio =
223 std::max(0.0f, globalUsageNormalized - processUsageNormalized);
225 return Ok();
228 void CPUUsageWatcher::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {
229 if (!mInitialized) {
230 return;
233 if (mExternalUsageRatio > mExternalUsageThreshold) {
234 aAnnotations.AddAnnotation(u"ExternalCPUHigh"_ns, true);
238 #else // !CPU_USAGE_WATCHER_ACTIVE
240 Result<Ok, CPUUsageWatcherError> CPUUsageWatcher::Init() { return Ok(); }
242 void CPUUsageWatcher::Uninit() {}
244 Result<Ok, CPUUsageWatcherError> CPUUsageWatcher::CollectCPUUsage() {
245 return Ok();
248 void CPUUsageWatcher::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {}
250 #endif // CPU_USAGE_WATCHER_ACTIVE
252 } // namespace mozilla