blob: 4d2ba62b1f35527406e77deb13d94392a31d8a15 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2013 The Chromium Authors
[email protected]32f5e9a02013-05-23 12:59:542// 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
Bruce Dawsonaa08ad822018-01-20 00:31:057#include <windows.h> // Must be in front of other Windows header files.
Peter Kasting134ef9af2024-12-28 02:30:098#include <winternl.h>
Bruce Dawsonaa08ad822018-01-20 00:31:059
[email protected]32f5e9a02013-05-23 12:59:5410#include <psapi.h>
avibeced7c2015-12-24 06:47:5911#include <stddef.h>
12#include <stdint.h>
[email protected]32f5e9a02013-05-23 12:59:5413
dcheng0917ec42015-11-19 07:00:2014#include <algorithm>
15
Joe Masoncddff4c92024-10-03 13:59:0016#include "base/check.h"
[email protected]32f5e9a02013-05-23 12:59:5417#include "base/logging.h"
thestig0df2bae82016-07-26 17:59:3618#include "base/memory/ptr_util.h"
Joe Masona0ba58b052023-02-22 20:46:2919#include "base/notreached.h"
Sebastien Marchand75a7cdf2018-11-13 23:47:0320#include "base/system/sys_info.h"
Sebastien Marchand05183ceb2018-10-30 02:20:0421#include "base/threading/scoped_blocking_call.h"
Etienne Pierre-dorayfc7952f02025-06-06 00:04:3322#include "base/trace_event/trace_event.h"
Hans Wennborg9e7d9022021-05-03 12:56:2023#include "base/values.h"
Patrick Monette24972c42022-04-14 02:34:0024#include "build/build_config.h"
[email protected]32f5e9a02013-05-23 12:59:5425
26namespace base {
georgesak7144e042015-07-29 17:11:4227namespace {
[email protected]32f5e9a02013-05-23 12:59:5428
Etienne Bergeron0b9d2b62018-08-15 20:34:1029// ntstatus.h conflicts with windows.h so define this locally.
30#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
31
32// Definition of this struct is taken from the book:
33// Windows NT/200, Native API reference, Gary Nebbett
34struct SYSTEM_PERFORMANCE_INFORMATION {
35 // Total idle time of all processes in the system (units of 100 ns).
36 LARGE_INTEGER IdleTime;
37 // Number of bytes read (by all call to ZwReadFile).
38 LARGE_INTEGER ReadTransferCount;
39 // Number of bytes written (by all call to ZwWriteFile).
40 LARGE_INTEGER WriteTransferCount;
41 // Number of bytes transferred (e.g. DeviceIoControlFile)
42 LARGE_INTEGER OtherTransferCount;
43 // The amount of read operations.
44 ULONG ReadOperationCount;
45 // The amount of write operations.
46 ULONG WriteOperationCount;
47 // The amount of other operations.
48 ULONG OtherOperationCount;
Sebastien Marchand57c036da2018-10-04 20:53:2049 // The number of pages of physical memory available to processes running on
50 // the system.
Etienne Bergeron0b9d2b62018-08-15 20:34:1051 ULONG AvailablePages;
52 ULONG TotalCommittedPages;
53 ULONG TotalCommitLimit;
54 ULONG PeakCommitment;
55 ULONG PageFaults;
56 ULONG WriteCopyFaults;
57 ULONG TransitionFaults;
58 ULONG CacheTransitionFaults;
59 ULONG DemandZeroFaults;
Sebastien Marchand57c036da2018-10-04 20:53:2060 // The number of pages read from disk to resolve page faults.
Etienne Bergeron0b9d2b62018-08-15 20:34:1061 ULONG PagesRead;
Sebastien Marchand57c036da2018-10-04 20:53:2062 // The number of read operations initiated to resolve page faults.
Etienne Bergeron0b9d2b62018-08-15 20:34:1063 ULONG PageReadIos;
64 ULONG CacheReads;
65 ULONG CacheIos;
66 // The number of pages written to the system's pagefiles.
67 ULONG PagefilePagesWritten;
68 // The number of write operations performed on the system's pagefiles.
69 ULONG PagefilePageWriteIos;
70 ULONG MappedFilePagesWritten;
71 ULONG MappedFilePageWriteIos;
72 ULONG PagedPoolUsage;
73 ULONG NonPagedPoolUsage;
74 ULONG PagedPoolAllocs;
75 ULONG PagedPoolFrees;
76 ULONG NonPagedPoolAllocs;
77 ULONG NonPagedPoolFrees;
78 ULONG TotalFreeSystemPtes;
79 ULONG SystemCodePage;
80 ULONG TotalSystemDriverPages;
81 ULONG TotalSystemCodePages;
82 ULONG SmallNonPagedLookasideListAllocateHits;
83 ULONG SmallPagedLookasideListAllocateHits;
84 ULONG Reserved3;
85 ULONG MmSystemCachePage;
86 ULONG PagedPoolPage;
87 ULONG SystemDriverPage;
88 ULONG FastReadNoWait;
89 ULONG FastReadWait;
90 ULONG FastReadResourceMiss;
91 ULONG FastReadNotPossible;
92 ULONG FastMdlReadNoWait;
93 ULONG FastMdlReadWait;
94 ULONG FastMdlReadResourceMiss;
95 ULONG FastMdlReadNotPossible;
96 ULONG MapDataNoWait;
97 ULONG MapDataWait;
98 ULONG MapDataNoWaitMiss;
99 ULONG MapDataWaitMiss;
100 ULONG PinMappedDataCount;
101 ULONG PinReadNoWait;
102 ULONG PinReadWait;
103 ULONG PinReadNoWaitMiss;
104 ULONG PinReadWaitMiss;
105 ULONG CopyReadNoWait;
106 ULONG CopyReadWait;
107 ULONG CopyReadNoWaitMiss;
108 ULONG CopyReadWaitMiss;
109 ULONG MdlReadNoWait;
110 ULONG MdlReadWait;
111 ULONG MdlReadNoWaitMiss;
112 ULONG MdlReadWaitMiss;
113 ULONG ReadAheadIos;
114 ULONG LazyWriteIos;
115 ULONG LazyWritePages;
116 ULONG DataFlushes;
117 ULONG DataPages;
118 ULONG ContextSwitches;
119 ULONG FirstLevelTbFills;
120 ULONG SecondLevelTbFills;
121 ULONG SystemCalls;
122};
georgesak7144e042015-07-29 17:11:42123
Joe Mason550eed62024-03-25 18:07:31124base::expected<TimeDelta, ProcessCPUUsageError> GetImpreciseCumulativeCPUUsage(
Joe Masonba145282024-03-12 20:18:19125 const win::ScopedHandle& process) {
Joe Masonabed99e92024-03-04 22:04:13126 FILETIME creation_time;
127 FILETIME exit_time;
128 FILETIME kernel_time;
129 FILETIME user_time;
130
131 if (!process.is_valid()) {
Joe Mason550eed62024-03-25 18:07:31132 return base::unexpected(ProcessCPUUsageError::kSystemError);
Joe Masonabed99e92024-03-04 22:04:13133 }
134
135 if (!GetProcessTimes(process.get(), &creation_time, &exit_time, &kernel_time,
136 &user_time)) {
137 // This should never fail when the handle is valid.
Joe Masoncddff4c92024-10-03 13:59:00138 NOTREACHED();
Joe Masonabed99e92024-03-04 22:04:13139 }
140
Joe Mason550eed62024-03-25 18:07:31141 return base::ok(TimeDelta::FromFileTime(kernel_time) +
142 TimeDelta::FromFileTime(user_time));
Joe Masonabed99e92024-03-04 22:04:13143}
144
georgesak7144e042015-07-29 17:11:42145} // namespace
146
Chris Mumforda7fd9f072017-09-12 00:20:51147size_t GetMaxFds() {
148 // Windows is only limited by the amount of physical memory.
149 return std::numeric_limits<size_t>::max();
150}
151
Matthew Cary144069d2019-07-11 19:26:17152size_t GetHandleLimit() {
153 // Rounded down from value reported here:
154 // http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
155 return static_cast<size_t>(1 << 23);
156}
157
[email protected]32f5e9a02013-05-23 12:59:54158// static
thestig0df2bae82016-07-26 17:59:36159std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
160 ProcessHandle process) {
161 return WrapUnique(new ProcessMetrics(process));
[email protected]32f5e9a02013-05-23 12:59:54162}
163
Etienne Pierre-doray490c80f2024-12-05 00:30:14164base::expected<ProcessMemoryInfo, ProcessUsageError>
165ProcessMetrics::GetMemoryInfo() const {
166 if (!process_.is_valid()) {
167 return base::unexpected(ProcessUsageError::kProcessNotFound);
168 }
169 PROCESS_MEMORY_COUNTERS_EX pmc;
170 if (!::GetProcessMemoryInfo(process_.get(),
171 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
172 sizeof(pmc))) {
173 return base::unexpected(ProcessUsageError::kSystemError);
174 }
175 ProcessMemoryInfo counters;
176 counters.private_bytes = pmc.PrivateUsage;
177 counters.resident_set_bytes = pmc.WorkingSetSize;
178 return counters;
179}
180
Joe Mason550eed62024-03-25 18:07:31181base::expected<TimeDelta, ProcessCPUUsageError>
182ProcessMetrics::GetCumulativeCPUUsage() {
Joe Mason122e628a2025-05-12 18:01:00183 TRACE_EVENT("base", "GetCumulativeCPUUsage");
Patrick Monette24972c42022-04-14 02:34:00184#if defined(ARCH_CPU_ARM64)
185 // Precise CPU usage is not available on Arm CPUs because they don't support
186 // constant rate TSC.
Joe Masonabed99e92024-03-04 22:04:13187 return GetImpreciseCumulativeCPUUsage(process_);
Patrick Monette24972c42022-04-14 02:34:00188#else // !defined(ARCH_CPU_ARM64)
Joe Masonabed99e92024-03-04 22:04:13189 if (!time_internal::HasConstantRateTSC()) {
190 return GetImpreciseCumulativeCPUUsage(process_);
191 }
Patrick Monette2597b7c2022-06-08 23:30:17192
Joe Mason9d02fe32023-06-06 20:16:23193 const double tsc_ticks_per_second = time_internal::TSCTicksPerSecond();
194 if (tsc_ticks_per_second == 0) {
195 // TSC is only initialized once TSCTicksPerSecond() is called twice 50 ms
Joe Masonabed99e92024-03-04 22:04:13196 // apart on the same thread to get a baseline. In unit tests, it is frequent
197 // for the initialization not to be complete. In production, it can also
198 // theoretically happen.
199 return GetImpreciseCumulativeCPUUsage(process_);
Joe Mason9d02fe32023-06-06 20:16:23200 }
201
Joe Masona678c4a22024-02-20 16:28:31202 if (!process_.is_valid()) {
Joe Mason550eed62024-03-25 18:07:31203 return base::unexpected(ProcessCPUUsageError::kProcessNotFound);
Joe Masona678c4a22024-02-20 16:28:31204 }
205
Patrick Monette24972c42022-04-14 02:34:00206 ULONG64 process_cycle_time = 0;
207 if (!QueryProcessCycleTime(process_.get(), &process_cycle_time)) {
Joe Masona678c4a22024-02-20 16:28:31208 // This should never fail when the handle is valid.
Joe Masoncddff4c92024-10-03 13:59:00209 NOTREACHED();
Patrick Monette24972c42022-04-14 02:34:00210 }
211
Patrick Monette24972c42022-04-14 02:34:00212 const double process_time_seconds = process_cycle_time / tsc_ticks_per_second;
Joe Mason550eed62024-03-25 18:07:31213 return base::ok(Seconds(process_time_seconds));
Patrick Monette24972c42022-04-14 02:34:00214#endif // !defined(ARCH_CPU_ARM64)
215}
216
Sigurdur Asgeirssoneb27eae72018-05-16 15:29:10217ProcessMetrics::ProcessMetrics(ProcessHandle process) {
Joe Masoncddff4c92024-10-03 13:59:00218 if (process == kNullProcessHandle) {
219 // Don't try to duplicate an invalid handle. However, INVALID_HANDLE_VALUE
220 // is also the pseudo-handle returned by ::GetCurrentProcess(), so DO try
221 // to duplicate that.
Joe Masona678c4a22024-02-20 16:28:31222 return;
stanisce73f1a42017-03-31 21:42:22223 }
Joe Masona678c4a22024-02-20 16:28:31224 HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
Joe Mason9bccdad2024-03-27 08:47:49225 BOOL result = ::DuplicateHandle(::GetCurrentProcess(), process,
226 ::GetCurrentProcess(), &duplicate_handle,
227 PROCESS_QUERY_LIMITED_INFORMATION, FALSE, 0);
Joe Masona678c4a22024-02-20 16:28:31228 if (!result) {
Joe Masoncddff4c92024-10-03 13:59:00229 // Even with PROCESS_QUERY_LIMITED_INFORMATION, DuplicateHandle can fail
230 // with ERROR_ACCESS_DENIED. And it's always possible to run out of handles.
Joe Masona678c4a22024-02-20 16:28:31231 const DWORD last_error = ::GetLastError();
Joe Masoncddff4c92024-10-03 13:59:00232 CHECK(last_error == ERROR_ACCESS_DENIED ||
233 last_error == ERROR_NO_SYSTEM_RESOURCES);
Joe Masona678c4a22024-02-20 16:28:31234 return;
235 }
236
237 process_.Set(duplicate_handle);
stanisce73f1a42017-03-31 21:42:22238}
[email protected]32f5e9a02013-05-23 12:59:54239
240size_t GetSystemCommitCharge() {
[email protected]32f5e9a02013-05-23 12:59:54241 PERFORMANCE_INFORMATION info;
Rulong Chen(陈汝龙)6d2bae1c2025-03-25 12:39:49242 if (!::GetPerformanceInfo(&info, sizeof(info))) {
[email protected]32f5e9a02013-05-23 12:59:54243 DLOG(ERROR) << "Failed to fetch internal performance info.";
244 return 0;
245 }
Rulong Chen(陈汝龙)6d2bae1c2025-03-25 12:39:49246 return (info.CommitTotal * info.PageSize) / 1024;
[email protected]32f5e9a02013-05-23 12:59:54247}
248
georgesak7144e042015-07-29 17:11:42249// This function uses the following mapping between MEMORYSTATUSEX and
250// SystemMemoryInfoKB:
251// ullTotalPhys ==> total
mkolom01ac10b2017-03-22 01:47:29252// ullAvailPhys ==> avail_phys
georgesak7144e042015-07-29 17:11:42253// ullTotalPageFile ==> swap_total
254// ullAvailPageFile ==> swap_free
255bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
256 MEMORYSTATUSEX mem_status;
257 mem_status.dwLength = sizeof(mem_status);
Joe Masonabed99e92024-03-04 22:04:13258 if (!::GlobalMemoryStatusEx(&mem_status)) {
georgesak7144e042015-07-29 17:11:42259 return false;
Joe Masonabed99e92024-03-04 22:04:13260 }
georgesak7144e042015-07-29 17:11:42261
Peter Kasting4d3664b2022-06-16 19:27:54262 meminfo->total = saturated_cast<int>(mem_status.ullTotalPhys / 1024);
263 meminfo->avail_phys = saturated_cast<int>(mem_status.ullAvailPhys / 1024);
264 meminfo->swap_total = saturated_cast<int>(mem_status.ullTotalPageFile / 1024);
265 meminfo->swap_free = saturated_cast<int>(mem_status.ullAvailPageFile / 1024);
georgesak7144e042015-07-29 17:11:42266
267 return true;
268}
269
keishi12b598b92017-06-20 10:25:26270size_t ProcessMetrics::GetMallocUsage() {
Keishi Hattori8cc2c3a2017-08-04 20:55:30271 // Unsupported as getting malloc usage on Windows requires iterating through
272 // the heap which is slow and crashes.
273 return 0;
keishi12b598b92017-06-20 10:25:26274}
275
Etienne Bergeron0b9d2b62018-08-15 20:34:10276SystemPerformanceInfo::SystemPerformanceInfo() = default;
277SystemPerformanceInfo::SystemPerformanceInfo(
278 const SystemPerformanceInfo& other) = default;
Arthur Eubanks08f7b0542022-09-16 00:53:01279SystemPerformanceInfo& SystemPerformanceInfo::operator=(
280 const SystemPerformanceInfo& other) = default;
Etienne Bergeron0b9d2b62018-08-15 20:34:10281
Roman Sorokina8cf50eb62023-01-18 11:22:26282Value::Dict SystemPerformanceInfo::ToDict() const {
283 Value::Dict result;
Etienne Bergeron0b9d2b62018-08-15 20:34:10284
285 // Write out uint64_t variables as doubles.
286 // Note: this may discard some precision, but for JS there's no other option.
Roman Sorokina8cf50eb62023-01-18 11:22:26287 result.Set("idle_time", strict_cast<double>(idle_time));
288 result.Set("read_transfer_count", strict_cast<double>(read_transfer_count));
289 result.Set("write_transfer_count", strict_cast<double>(write_transfer_count));
290 result.Set("other_transfer_count", strict_cast<double>(other_transfer_count));
291 result.Set("read_operation_count", strict_cast<double>(read_operation_count));
292 result.Set("write_operation_count",
293 strict_cast<double>(write_operation_count));
294 result.Set("other_operation_count",
295 strict_cast<double>(other_operation_count));
296 result.Set("pagefile_pages_written",
297 strict_cast<double>(pagefile_pages_written));
298 result.Set("pagefile_pages_write_ios",
299 strict_cast<double>(pagefile_pages_write_ios));
300 result.Set("available_pages", strict_cast<double>(available_pages));
301 result.Set("pages_read", strict_cast<double>(pages_read));
302 result.Set("page_read_ios", strict_cast<double>(page_read_ios));
Etienne Bergeron0b9d2b62018-08-15 20:34:10303
304 return result;
305}
306
307// Retrieves performance counters from the operating system.
308// Fills in the provided |info| structure. Returns true on success.
309BASE_EXPORT bool GetSystemPerformanceInfo(SystemPerformanceInfo* info) {
Etienne Bergeron0b9d2b62018-08-15 20:34:10310 SYSTEM_PERFORMANCE_INFORMATION counters = {};
Sebastien Marchand05183ceb2018-10-30 02:20:04311 {
312 // The call to NtQuerySystemInformation might block on a lock.
Etienne Bergeron436d42212019-02-26 17:15:12313 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
314 BlockingType::MAY_BLOCK);
Alex Goughda69b902023-02-13 20:55:53315 if (::NtQuerySystemInformation(::SystemPerformanceInformation, &counters,
316 sizeof(SYSTEM_PERFORMANCE_INFORMATION),
317 nullptr) != STATUS_SUCCESS) {
Sebastien Marchand05183ceb2018-10-30 02:20:04318 return false;
319 }
320 }
Etienne Bergeron0b9d2b62018-08-15 20:34:10321
Peter Kasting4d3664b2022-06-16 19:27:54322 info->idle_time = static_cast<uint64_t>(counters.IdleTime.QuadPart);
323 info->read_transfer_count =
324 static_cast<uint64_t>(counters.ReadTransferCount.QuadPart);
325 info->write_transfer_count =
326 static_cast<uint64_t>(counters.WriteTransferCount.QuadPart);
327 info->other_transfer_count =
328 static_cast<uint64_t>(counters.OtherTransferCount.QuadPart);
Etienne Bergeron0b9d2b62018-08-15 20:34:10329 info->read_operation_count = counters.ReadOperationCount;
330 info->write_operation_count = counters.WriteOperationCount;
331 info->other_operation_count = counters.OtherOperationCount;
332 info->pagefile_pages_written = counters.PagefilePagesWritten;
333 info->pagefile_pages_write_ios = counters.PagefilePageWriteIos;
Sebastien Marchand57c036da2018-10-04 20:53:20334 info->available_pages = counters.AvailablePages;
335 info->pages_read = counters.PagesRead;
336 info->page_read_ios = counters.PageReadIos;
Etienne Bergeron0b9d2b62018-08-15 20:34:10337
338 return true;
339}
340
[email protected]32f5e9a02013-05-23 12:59:54341} // namespace base