blob: 989e75de7fa499cb905791f79b9d87e7ab57fb65 [file] [log] [blame]
[email protected]32f5e9a02013-05-23 12:59:541// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/process/process_metrics.h"
6
7#include <windows.h>
8#include <psapi.h>
avibeced7c2015-12-24 06:47:599#include <stddef.h>
10#include <stdint.h>
georgesak7144e042015-07-29 17:11:4211#include <winternl.h>
[email protected]32f5e9a02013-05-23 12:59:5412
dcheng0917ec42015-11-19 07:00:2013#include <algorithm>
14
[email protected]32f5e9a02013-05-23 12:59:5415#include "base/logging.h"
thestig0df2bae82016-07-26 17:59:3616#include "base/memory/ptr_util.h"
stanisc75ab2122016-09-09 02:20:5317#include "base/process/memory.h"
[email protected]32f5e9a02013-05-23 12:59:5418#include "base/sys_info.h"
19
keishi12b598b92017-06-20 10:25:2620#if defined(OS_WIN)
21#include <windows.h>
22#endif
23
[email protected]32f5e9a02013-05-23 12:59:5424namespace base {
georgesak7144e042015-07-29 17:11:4225namespace {
[email protected]32f5e9a02013-05-23 12:59:5426
27// System pagesize. This value remains constant on x86/64 architectures.
28const int PAGESIZE_KB = 4;
29
georgesak7144e042015-07-29 17:11:4230typedef NTSTATUS(WINAPI* NTQUERYSYSTEMINFORMATION)(
31 SYSTEM_INFORMATION_CLASS SystemInformationClass,
32 PVOID SystemInformation,
33 ULONG SystemInformationLength,
34 PULONG ReturnLength);
35
36} // namespace
37
[email protected]32f5e9a02013-05-23 12:59:5438ProcessMetrics::~ProcessMetrics() { }
39
Chris Mumforda7fd9f072017-09-12 00:20:5140size_t GetMaxFds() {
41 // Windows is only limited by the amount of physical memory.
42 return std::numeric_limits<size_t>::max();
43}
44
[email protected]32f5e9a02013-05-23 12:59:5445// static
thestig0df2bae82016-07-26 17:59:3646std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
47 ProcessHandle process) {
48 return WrapUnique(new ProcessMetrics(process));
[email protected]32f5e9a02013-05-23 12:59:5449}
50
51size_t ProcessMetrics::GetPagefileUsage() const {
52 PROCESS_MEMORY_COUNTERS pmc;
stanisce73f1a42017-03-31 21:42:2253 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) {
[email protected]32f5e9a02013-05-23 12:59:5454 return pmc.PagefileUsage;
55 }
56 return 0;
57}
58
59// Returns the peak space allocated for the pagefile, in bytes.
60size_t ProcessMetrics::GetPeakPagefileUsage() const {
61 PROCESS_MEMORY_COUNTERS pmc;
stanisce73f1a42017-03-31 21:42:2262 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) {
[email protected]32f5e9a02013-05-23 12:59:5463 return pmc.PeakPagefileUsage;
64 }
65 return 0;
66}
67
68// Returns the current working set size, in bytes.
69size_t ProcessMetrics::GetWorkingSetSize() const {
70 PROCESS_MEMORY_COUNTERS pmc;
stanisce73f1a42017-03-31 21:42:2271 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) {
[email protected]32f5e9a02013-05-23 12:59:5472 return pmc.WorkingSetSize;
73 }
74 return 0;
75}
76
77// Returns the peak working set size, in bytes.
78size_t ProcessMetrics::GetPeakWorkingSetSize() const {
79 PROCESS_MEMORY_COUNTERS pmc;
stanisce73f1a42017-03-31 21:42:2280 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) {
[email protected]32f5e9a02013-05-23 12:59:5481 return pmc.PeakWorkingSetSize;
82 }
83 return 0;
84}
85
86bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
erikchen56722412017-03-13 22:46:4987 size_t* shared_bytes) const {
[email protected]32f5e9a02013-05-23 12:59:5488 // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
89 // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
90 // information is simply not available. Hence, we will return 0 on unsupported
91 // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
92 PROCESS_MEMORY_COUNTERS_EX pmcx;
93 if (private_bytes &&
stanisce73f1a42017-03-31 21:42:2294 GetProcessMemoryInfo(process_.Get(),
[email protected]32f5e9a02013-05-23 12:59:5495 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
96 sizeof(pmcx))) {
97 *private_bytes = pmcx.PrivateUsage;
98 }
99
100 if (shared_bytes) {
101 WorkingSetKBytes ws_usage;
102 if (!GetWorkingSetKBytes(&ws_usage))
103 return false;
104
105 *shared_bytes = ws_usage.shared * 1024;
106 }
107
108 return true;
109}
110
111void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
112 MEMORY_BASIC_INFORMATION mbi = {0};
113 size_t committed_private = 0;
114 size_t committed_mapped = 0;
115 size_t committed_image = 0;
116 void* base_address = NULL;
stanisce73f1a42017-03-31 21:42:22117 while (VirtualQueryEx(process_.Get(), base_address, &mbi, sizeof(mbi)) ==
118 sizeof(mbi)) {
[email protected]32f5e9a02013-05-23 12:59:54119 if (mbi.State == MEM_COMMIT) {
120 if (mbi.Type == MEM_PRIVATE) {
121 committed_private += mbi.RegionSize;
122 } else if (mbi.Type == MEM_MAPPED) {
123 committed_mapped += mbi.RegionSize;
124 } else if (mbi.Type == MEM_IMAGE) {
125 committed_image += mbi.RegionSize;
126 } else {
127 NOTREACHED();
128 }
129 }
130 void* new_base = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize;
131 // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION.
132 // If we query 64bit processes in a 32bit process, VirtualQueryEx()
133 // returns such data.
134 if (new_base <= base_address) {
135 usage->image = 0;
136 usage->mapped = 0;
137 usage->priv = 0;
138 return;
139 }
140 base_address = new_base;
141 }
142 usage->image = committed_image / 1024;
143 usage->mapped = committed_mapped / 1024;
144 usage->priv = committed_private / 1024;
145}
146
stanisc75ab2122016-09-09 02:20:53147namespace {
148
149class WorkingSetInformationBuffer {
150 public:
151 WorkingSetInformationBuffer() {}
152 ~WorkingSetInformationBuffer() { Clear(); }
153
154 bool Reserve(size_t size) {
155 Clear();
156 // Use UncheckedMalloc here because this can be called from the code
157 // that handles low memory condition.
158 return UncheckedMalloc(size, reinterpret_cast<void**>(&buffer_));
159 }
160
stanisc75ab2122016-09-09 02:20:53161 const PSAPI_WORKING_SET_INFORMATION* operator ->() const { return buffer_; }
162
chengxb17289272016-12-12 06:55:56163 size_t GetPageEntryCount() const { return number_of_entries; }
164
165 // This function is used to get page entries for a process.
stanisce73f1a42017-03-31 21:42:22166 bool QueryPageEntries(const ProcessHandle& process) {
chengxb17289272016-12-12 06:55:56167 int retries = 5;
168 number_of_entries = 4096; // Just a guess.
169
170 for (;;) {
171 size_t buffer_size =
172 sizeof(PSAPI_WORKING_SET_INFORMATION) +
173 (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK));
174
175 if (!Reserve(buffer_size))
176 return false;
177
178 // On success, |buffer_| is populated with info about the working set of
stanisce73f1a42017-03-31 21:42:22179 // |process|. On ERROR_BAD_LENGTH failure, increase the size of the
chengxb17289272016-12-12 06:55:56180 // buffer and try again.
stanisce73f1a42017-03-31 21:42:22181 if (QueryWorkingSet(process, buffer_, buffer_size))
chengxb17289272016-12-12 06:55:56182 break; // Success
183
184 if (GetLastError() != ERROR_BAD_LENGTH)
185 return false;
186
187 number_of_entries = buffer_->NumberOfEntries;
188
189 // Maybe some entries are being added right now. Increase the buffer to
190 // take that into account. Increasing by 10% should generally be enough,
191 // especially considering the potentially low memory condition during the
192 // call (when called from OomMemoryDetails) and the potentially high
193 // number of entries (300K was observed in crash dumps).
194 number_of_entries *= 1.1;
195
196 if (--retries == 0) {
197 // If we're looping, eventually fail.
198 return false;
199 }
200 }
201
202 // TODO(chengx): Remove the comment and the logic below. It is no longer
203 // needed since we don't have Win2000 support.
204 // On windows 2000 the function returns 1 even when the buffer is too small.
205 // The number of entries that we are going to parse is the minimum between
206 // the size we allocated and the real number of entries.
207 number_of_entries = std::min(number_of_entries,
208 static_cast<size_t>(buffer_->NumberOfEntries));
209
210 return true;
211 }
212
stanisc75ab2122016-09-09 02:20:53213 private:
214 void Clear() {
215 free(buffer_);
216 buffer_ = nullptr;
217 }
218
219 PSAPI_WORKING_SET_INFORMATION* buffer_ = nullptr;
220
chengxb17289272016-12-12 06:55:56221 // Number of page entries.
222 size_t number_of_entries = 0;
223
stanisc75ab2122016-09-09 02:20:53224 DISALLOW_COPY_AND_ASSIGN(WorkingSetInformationBuffer);
225};
226
227} // namespace
228
[email protected]32f5e9a02013-05-23 12:59:54229bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
230 size_t ws_private = 0;
231 size_t ws_shareable = 0;
232 size_t ws_shared = 0;
233
234 DCHECK(ws_usage);
235 memset(ws_usage, 0, sizeof(*ws_usage));
236
stanisc75ab2122016-09-09 02:20:53237 WorkingSetInformationBuffer buffer;
stanisce73f1a42017-03-31 21:42:22238 if (!buffer.QueryPageEntries(process_.Get()))
chengxb17289272016-12-12 06:55:56239 return false;
[email protected]32f5e9a02013-05-23 12:59:54240
chengxb17289272016-12-12 06:55:56241 size_t num_page_entries = buffer.GetPageEntryCount();
242 for (size_t i = 0; i < num_page_entries; i++) {
[email protected]32f5e9a02013-05-23 12:59:54243 if (buffer->WorkingSetInfo[i].Shared) {
244 ws_shareable++;
245 if (buffer->WorkingSetInfo[i].ShareCount > 1)
246 ws_shared++;
247 } else {
248 ws_private++;
249 }
250 }
251
252 ws_usage->priv = ws_private * PAGESIZE_KB;
253 ws_usage->shareable = ws_shareable * PAGESIZE_KB;
254 ws_usage->shared = ws_shared * PAGESIZE_KB;
chengxb17289272016-12-12 06:55:56255
256 return true;
257}
258
259// This function calculates the proportional set size for a process.
260bool ProcessMetrics::GetProportionalSetSizeBytes(uint64_t* pss_bytes) const {
261 double ws_pss = 0.0;
262
263 WorkingSetInformationBuffer buffer;
stanisce73f1a42017-03-31 21:42:22264 if (!buffer.QueryPageEntries(process_.Get()))
chengxb17289272016-12-12 06:55:56265 return false;
266
267 size_t num_page_entries = buffer.GetPageEntryCount();
268 for (size_t i = 0; i < num_page_entries; i++) {
269 if (buffer->WorkingSetInfo[i].Shared &&
270 buffer->WorkingSetInfo[i].ShareCount > 0)
271 ws_pss += 1.0 / buffer->WorkingSetInfo[i].ShareCount;
272 else
273 ws_pss += 1.0;
274 }
275
276 *pss_bytes = static_cast<uint64_t>(ws_pss * GetPageSize());
[email protected]32f5e9a02013-05-23 12:59:54277 return true;
278}
279
avibeced7c2015-12-24 06:47:59280static uint64_t FileTimeToUTC(const FILETIME& ftime) {
[email protected]32f5e9a02013-05-23 12:59:54281 LARGE_INTEGER li;
282 li.LowPart = ftime.dwLowDateTime;
283 li.HighPart = ftime.dwHighDateTime;
284 return li.QuadPart;
285}
286
Wez4ae8f292017-09-11 20:52:03287double ProcessMetrics::GetPlatformIndependentCPUUsage() {
[email protected]32f5e9a02013-05-23 12:59:54288 FILETIME creation_time;
289 FILETIME exit_time;
290 FILETIME kernel_time;
291 FILETIME user_time;
292
stanisce73f1a42017-03-31 21:42:22293 if (!GetProcessTimes(process_.Get(), &creation_time, &exit_time, &kernel_time,
294 &user_time)) {
[email protected]32f5e9a02013-05-23 12:59:54295 // We don't assert here because in some cases (such as in the Task Manager)
296 // we may call this function on a process that has just exited but we have
297 // not yet received the notification.
298 return 0;
299 }
Wez4ae8f292017-09-11 20:52:03300 int64_t system_time = FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time);
[email protected]e21c3322014-04-06 21:25:01301 TimeTicks time = TimeTicks::Now();
[email protected]32f5e9a02013-05-23 12:59:54302
[email protected]e21c3322014-04-06 21:25:01303 if (last_system_time_ == 0) {
[email protected]32f5e9a02013-05-23 12:59:54304 // First call, just set the last values.
305 last_system_time_ = system_time;
[email protected]ac6d0652014-01-14 00:06:37306 last_cpu_time_ = time;
[email protected]32f5e9a02013-05-23 12:59:54307 return 0;
308 }
309
avibeced7c2015-12-24 06:47:59310 int64_t system_time_delta = system_time - last_system_time_;
[email protected]e21c3322014-04-06 21:25:01311 // FILETIME is in 100-nanosecond units, so this needs microseconds times 10.
avibeced7c2015-12-24 06:47:59312 int64_t time_delta = (time - last_cpu_time_).InMicroseconds() * 10;
[email protected]32f5e9a02013-05-23 12:59:54313 DCHECK_NE(0U, time_delta);
314 if (time_delta == 0)
315 return 0;
316
[email protected]32f5e9a02013-05-23 12:59:54317
318 last_system_time_ = system_time;
[email protected]ac6d0652014-01-14 00:06:37319 last_cpu_time_ = time;
[email protected]32f5e9a02013-05-23 12:59:54320
Wez4ae8f292017-09-11 20:52:03321 return static_cast<double>(system_time_delta) / time_delta;
[email protected]32f5e9a02013-05-23 12:59:54322}
323
[email protected]32f5e9a02013-05-23 12:59:54324bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
stanisce73f1a42017-03-31 21:42:22325 return GetProcessIoCounters(process_.Get(), io_counters) != FALSE;
[email protected]32f5e9a02013-05-23 12:59:54326}
327
Wez4ae8f292017-09-11 20:52:03328ProcessMetrics::ProcessMetrics(ProcessHandle process) : last_system_time_(0) {
stanisce73f1a42017-03-31 21:42:22329 if (process) {
330 HANDLE duplicate_handle;
331 BOOL result = ::DuplicateHandle(::GetCurrentProcess(), process,
332 ::GetCurrentProcess(), &duplicate_handle,
333 PROCESS_QUERY_INFORMATION, FALSE, 0);
334 DCHECK(result);
335 process_.Set(duplicate_handle);
336 }
337}
[email protected]32f5e9a02013-05-23 12:59:54338
339size_t GetSystemCommitCharge() {
340 // Get the System Page Size.
341 SYSTEM_INFO system_info;
342 GetSystemInfo(&system_info);
343
344 PERFORMANCE_INFORMATION info;
thestig0df2bae82016-07-26 17:59:36345 if (!GetPerformanceInfo(&info, sizeof(info))) {
[email protected]32f5e9a02013-05-23 12:59:54346 DLOG(ERROR) << "Failed to fetch internal performance info.";
347 return 0;
348 }
349 return (info.CommitTotal * system_info.dwPageSize) / 1024;
350}
351
reveman1fef3ffc2014-12-18 19:22:28352size_t GetPageSize() {
353 return PAGESIZE_KB * 1024;
354}
355
georgesak7144e042015-07-29 17:11:42356// This function uses the following mapping between MEMORYSTATUSEX and
357// SystemMemoryInfoKB:
358// ullTotalPhys ==> total
mkolom01ac10b2017-03-22 01:47:29359// ullAvailPhys ==> avail_phys
georgesak7144e042015-07-29 17:11:42360// ullTotalPageFile ==> swap_total
361// ullAvailPageFile ==> swap_free
362bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
363 MEMORYSTATUSEX mem_status;
364 mem_status.dwLength = sizeof(mem_status);
365 if (!::GlobalMemoryStatusEx(&mem_status))
366 return false;
367
368 meminfo->total = mem_status.ullTotalPhys / 1024;
mkolom01ac10b2017-03-22 01:47:29369 meminfo->avail_phys = mem_status.ullAvailPhys / 1024;
georgesak7144e042015-07-29 17:11:42370 meminfo->swap_total = mem_status.ullTotalPageFile / 1024;
371 meminfo->swap_free = mem_status.ullAvailPageFile / 1024;
372
373 return true;
374}
375
keishi12b598b92017-06-20 10:25:26376size_t ProcessMetrics::GetMallocUsage() {
Keishi Hattori8cc2c3ab2017-08-04 20:55:30377 // Unsupported as getting malloc usage on Windows requires iterating through
378 // the heap which is slow and crashes.
379 return 0;
keishi12b598b92017-06-20 10:25:26380}
381
[email protected]32f5e9a02013-05-23 12:59:54382} // namespace base