Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/threads/CPUUsageWatcher.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "mozilla/CPUUsageWatcher.h"
8
9
#include "prsystem.h"
10
11
#ifdef XP_MACOSX
12
#include <sys/resource.h>
13
#include <mach/clock.h>
14
#include <mach/mach_host.h>
15
#endif
16
17
namespace mozilla {
18
19
#ifdef CPU_USAGE_WATCHER_ACTIVE
20
21
// Even if the machine only has one processor, tolerate up to 50%
22
// external CPU usage.
23
static const float kTolerableExternalCPUUsageFloor = 0.5f;
24
25
struct CPUStats {
26
  // The average CPU usage time, which can be summed across all cores in the
27
  // system, or averaged between them. Whichever it is, it needs to be in the
28
  // same units as updateTime.
29
  uint64_t usageTime;
30
  // A monotonically increasing value in the same units as usageTime, which can
31
  // be used to determine the percentage of active vs idle time
32
  uint64_t updateTime;
33
};
34
35
#ifdef XP_MACOSX
36
37
static const uint64_t kMicrosecondsPerSecond = 1000000LL;
38
static const uint64_t kNanosecondsPerMicrosecond = 1000LL;
39
static const uint64_t kCPUCheckInterval = kMicrosecondsPerSecond / 2LL;
40
41
uint64_t GetMicroseconds(timeval time) {
42
    return ((uint64_t)time.tv_sec) * kMicrosecondsPerSecond +
43
           (uint64_t)time.tv_usec;
44
}
45
46
uint64_t GetMicroseconds(mach_timespec_t time) {
47
    return ((uint64_t)time.tv_sec) * kMicrosecondsPerSecond +
48
           ((uint64_t)time.tv_nsec) / kNanosecondsPerMicrosecond;
49
}
50
51
Result<CPUStats, CPUUsageWatcherError>
52
GetProcessCPUStats(int32_t numCPUs) {
53
  CPUStats result = {};
54
  rusage usage;
55
  int32_t rusageResult = getrusage(RUSAGE_SELF, &usage);
56
  if (rusageResult == -1) {
57
    return Err(GetProcessTimesError);
58
  }
59
  result.usageTime = GetMicroseconds(usage.ru_utime) + GetMicroseconds(usage.ru_stime);
60
61
  clock_serv_t realtimeClock;
62
  kern_return_t errorResult =
63
      host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &realtimeClock);
64
  if (errorResult != KERN_SUCCESS) {
65
    return Err(GetProcessTimesError);
66
  }
67
  mach_timespec_t time;
68
  errorResult = clock_get_time(realtimeClock, &time);
69
  if (errorResult != KERN_SUCCESS) {
70
    return Err(GetProcessTimesError);
71
  }
72
  result.updateTime = GetMicroseconds(time);
73
74
  // getrusage will give us the sum of the values across all
75
  // of our cores. Divide by the number of CPUs to get an average.
76
  result.usageTime /= numCPUs;
77
  return result;
78
}
79
80
Result<CPUStats, CPUUsageWatcherError>
81
GetGlobalCPUStats() {
82
  CPUStats result = {};
83
  host_cpu_load_info_data_t loadInfo;
84
  mach_msg_type_number_t loadInfoCount = HOST_CPU_LOAD_INFO_COUNT;
85
  kern_return_t statsResult = host_statistics(mach_host_self(),
86
                                              HOST_CPU_LOAD_INFO,
87
                                              (host_info_t)&loadInfo,
88
                                              &loadInfoCount);
89
  if (statsResult != KERN_SUCCESS) {
90
    return Err(HostStatisticsError);
91
  }
92
93
  result.usageTime = loadInfo.cpu_ticks[CPU_STATE_USER] +
94
                     loadInfo.cpu_ticks[CPU_STATE_NICE] +
95
                     loadInfo.cpu_ticks[CPU_STATE_SYSTEM];
96
  result.updateTime = result.usageTime + loadInfo.cpu_ticks[CPU_STATE_IDLE];
97
  return result;
98
}
99
100
#endif // XP_MACOSX
101
102
#ifdef XP_WIN
103
104
// A FILETIME represents the number of 100-nanosecond ticks since 1/1/1601 UTC
105
static const uint64_t kFILETIMETicksPerSecond = 10000000;
106
static const uint64_t kCPUCheckInterval = kFILETIMETicksPerSecond / 2;
107
108
uint64_t
109
FiletimeToInteger(FILETIME filetime) {
110
  return ((uint64_t)filetime.dwLowDateTime) |
111
         (uint64_t)filetime.dwHighDateTime << 32;
112
}
113
114
Result<CPUStats, CPUUsageWatcherError> GetProcessCPUStats(int32_t numCPUs) {
115
  CPUStats result = {};
116
  FILETIME creationFiletime;
117
  FILETIME exitFiletime;
118
  FILETIME kernelFiletime;
119
  FILETIME userFiletime;
120
  bool success = GetProcessTimes(GetCurrentProcess(),
121
                                 &creationFiletime,
122
                                 &exitFiletime,
123
                                 &kernelFiletime,
124
                                 &userFiletime);
125
  if (!success) {
126
    return Err(GetProcessTimesError);
127
  }
128
129
  result.usageTime = FiletimeToInteger(kernelFiletime) +
130
                     FiletimeToInteger(userFiletime);
131
132
  FILETIME nowFiletime;
133
  GetSystemTimeAsFileTime(&nowFiletime);
134
  result.updateTime = FiletimeToInteger(nowFiletime);
135
136
  result.usageTime /= numCPUs;
137
138
  return result;
139
}
140
141
Result<CPUStats, CPUUsageWatcherError>
142
GetGlobalCPUStats() {
143
  CPUStats result = {};
144
  FILETIME idleFiletime;
145
  FILETIME kernelFiletime;
146
  FILETIME userFiletime;
147
  bool success = GetSystemTimes(&idleFiletime,
148
                                &kernelFiletime,
149
                                &userFiletime);
150
151
  if (!success) {
152
    return Err(GetSystemTimesError);
153
  }
154
155
  result.usageTime = FiletimeToInteger(kernelFiletime) +
156
                     FiletimeToInteger(userFiletime);
157
  result.updateTime = result.usageTime + FiletimeToInteger(idleFiletime);
158
159
  return result;
160
}
161
162
#endif // XP_WIN
163
164
Result<Ok, CPUUsageWatcherError>
165
CPUUsageWatcher::Init()
166
{
167
  mNumCPUs = PR_GetNumberOfProcessors();
168
  if (mNumCPUs <= 0) {
169
    mExternalUsageThreshold = 1.0f;
170
    return Err(GetNumberOfProcessorsError);
171
  }
172
  mExternalUsageThreshold = std::max(1.0f - 1.0f / (float)mNumCPUs,
173
                                     kTolerableExternalCPUUsageFloor);
174
175
  CPUStats processTimes;
176
  MOZ_TRY_VAR(processTimes, GetProcessCPUStats(mNumCPUs));
177
  mProcessUpdateTime = processTimes.updateTime;
178
  mProcessUsageTime = processTimes.usageTime;
179
180
  CPUStats globalTimes;
181
  MOZ_TRY_VAR(globalTimes, GetGlobalCPUStats());
182
  mGlobalUpdateTime = globalTimes.updateTime;
183
  mGlobalUsageTime = globalTimes.usageTime;
184
185
  mInitialized = true;
186
187
  CPUUsageWatcher* self = this;
188
  NS_DispatchToMainThread(
189
    NS_NewRunnableFunction("CPUUsageWatcher::Init",
190
                           [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
191
192
  return Ok();
193
}
194
195
void
196
CPUUsageWatcher::Uninit()
197
{
198
  if (mInitialized) {
199
    BackgroundHangMonitor::UnregisterAnnotator(*this);
200
  }
201
  mInitialized = false;
202
}
203
204
Result<Ok, CPUUsageWatcherError>
205
CPUUsageWatcher::CollectCPUUsage()
206
{
207
  if (!mInitialized) {
208
    return Ok();
209
  }
210
211
  mExternalUsageRatio = 0.0f;
212
213
  CPUStats processTimes;
214
  MOZ_TRY_VAR(processTimes, GetProcessCPUStats(mNumCPUs));
215
  CPUStats globalTimes;
216
  MOZ_TRY_VAR(globalTimes, GetGlobalCPUStats());
217
218
  uint64_t processUsageDelta = processTimes.usageTime - mProcessUsageTime;
219
  uint64_t processUpdateDelta = processTimes.updateTime - mProcessUpdateTime;
220
  float processUsageNormalized = processUsageDelta > 0 ?
221
                                (float)processUsageDelta / (float)processUpdateDelta :
222
                                0.0f;
223
224
  uint64_t globalUsageDelta = globalTimes.usageTime - mGlobalUsageTime;
225
  uint64_t globalUpdateDelta = globalTimes.updateTime - mGlobalUpdateTime;
226
  float globalUsageNormalized = globalUsageDelta > 0 ?
227
                                (float)globalUsageDelta / (float)globalUpdateDelta :
228
                                0.0f;
229
230
  mProcessUsageTime = processTimes.usageTime;
231
  mProcessUpdateTime = processTimes.updateTime;
232
  mGlobalUsageTime = globalTimes.usageTime;
233
  mGlobalUpdateTime = globalTimes.updateTime;
234
235
  mExternalUsageRatio = std::max(0.0f,
236
                                 globalUsageNormalized - processUsageNormalized);
237
238
  return Ok();
239
}
240
241
void
242
CPUUsageWatcher::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {
243
  if (!mInitialized) {
244
    return;
245
  }
246
247
  if (mExternalUsageRatio > mExternalUsageThreshold) {
248
    aAnnotations.AddAnnotation(NS_LITERAL_STRING("ExternalCPUHigh"), true);
249
  }
250
}
251
252
#else // !CPU_USAGE_WATCHER_ACTIVE
253
254
Result<Ok, CPUUsageWatcherError>
255
CPUUsageWatcher::Init()
256
3
{
257
3
  return Ok();
258
3
}
259
260
0
void CPUUsageWatcher::Uninit() {}
261
262
Result<Ok, CPUUsageWatcherError>
263
CPUUsageWatcher::CollectCPUUsage()
264
0
{
265
0
  return Ok();
266
0
}
267
268
0
void CPUUsageWatcher::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {}
269
270
#endif // CPU_USAGE_WATCHER_ACTIVE
271
272
} // namespace mozilla