1 // Copyright 2013 The Chromium Authors
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> // Must be in front of other Windows header files.
8
9 #include <psapi.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <winternl.h>
13
14 #include <algorithm>
15
16 #include "base/logging.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/notreached.h"
19 #include "base/process/process_metrics_iocounters.h"
20 #include "base/system/sys_info.h"
21 #include "base/threading/scoped_blocking_call.h"
22 #include "base/values.h"
23 #include "build/build_config.h"
24
25 namespace base {
26 namespace {
27
28 // ntstatus.h conflicts with windows.h so define this locally.
29 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
30
31 // Definition of this struct is taken from the book:
32 // Windows NT/200, Native API reference, Gary Nebbett
33 struct SYSTEM_PERFORMANCE_INFORMATION {
34 // Total idle time of all processes in the system (units of 100 ns).
35 LARGE_INTEGER IdleTime;
36 // Number of bytes read (by all call to ZwReadFile).
37 LARGE_INTEGER ReadTransferCount;
38 // Number of bytes written (by all call to ZwWriteFile).
39 LARGE_INTEGER WriteTransferCount;
40 // Number of bytes transferred (e.g. DeviceIoControlFile)
41 LARGE_INTEGER OtherTransferCount;
42 // The amount of read operations.
43 ULONG ReadOperationCount;
44 // The amount of write operations.
45 ULONG WriteOperationCount;
46 // The amount of other operations.
47 ULONG OtherOperationCount;
48 // The number of pages of physical memory available to processes running on
49 // the system.
50 ULONG AvailablePages;
51 ULONG TotalCommittedPages;
52 ULONG TotalCommitLimit;
53 ULONG PeakCommitment;
54 ULONG PageFaults;
55 ULONG WriteCopyFaults;
56 ULONG TransitionFaults;
57 ULONG CacheTransitionFaults;
58 ULONG DemandZeroFaults;
59 // The number of pages read from disk to resolve page faults.
60 ULONG PagesRead;
61 // The number of read operations initiated to resolve page faults.
62 ULONG PageReadIos;
63 ULONG CacheReads;
64 ULONG CacheIos;
65 // The number of pages written to the system's pagefiles.
66 ULONG PagefilePagesWritten;
67 // The number of write operations performed on the system's pagefiles.
68 ULONG PagefilePageWriteIos;
69 ULONG MappedFilePagesWritten;
70 ULONG MappedFilePageWriteIos;
71 ULONG PagedPoolUsage;
72 ULONG NonPagedPoolUsage;
73 ULONG PagedPoolAllocs;
74 ULONG PagedPoolFrees;
75 ULONG NonPagedPoolAllocs;
76 ULONG NonPagedPoolFrees;
77 ULONG TotalFreeSystemPtes;
78 ULONG SystemCodePage;
79 ULONG TotalSystemDriverPages;
80 ULONG TotalSystemCodePages;
81 ULONG SmallNonPagedLookasideListAllocateHits;
82 ULONG SmallPagedLookasideListAllocateHits;
83 ULONG Reserved3;
84 ULONG MmSystemCachePage;
85 ULONG PagedPoolPage;
86 ULONG SystemDriverPage;
87 ULONG FastReadNoWait;
88 ULONG FastReadWait;
89 ULONG FastReadResourceMiss;
90 ULONG FastReadNotPossible;
91 ULONG FastMdlReadNoWait;
92 ULONG FastMdlReadWait;
93 ULONG FastMdlReadResourceMiss;
94 ULONG FastMdlReadNotPossible;
95 ULONG MapDataNoWait;
96 ULONG MapDataWait;
97 ULONG MapDataNoWaitMiss;
98 ULONG MapDataWaitMiss;
99 ULONG PinMappedDataCount;
100 ULONG PinReadNoWait;
101 ULONG PinReadWait;
102 ULONG PinReadNoWaitMiss;
103 ULONG PinReadWaitMiss;
104 ULONG CopyReadNoWait;
105 ULONG CopyReadWait;
106 ULONG CopyReadNoWaitMiss;
107 ULONG CopyReadWaitMiss;
108 ULONG MdlReadNoWait;
109 ULONG MdlReadWait;
110 ULONG MdlReadNoWaitMiss;
111 ULONG MdlReadWaitMiss;
112 ULONG ReadAheadIos;
113 ULONG LazyWriteIos;
114 ULONG LazyWritePages;
115 ULONG DataFlushes;
116 ULONG DataPages;
117 ULONG ContextSwitches;
118 ULONG FirstLevelTbFills;
119 ULONG SecondLevelTbFills;
120 ULONG SystemCalls;
121 };
122
123 } // namespace
124
GetMaxFds()125 size_t GetMaxFds() {
126 // Windows is only limited by the amount of physical memory.
127 return std::numeric_limits<size_t>::max();
128 }
129
GetHandleLimit()130 size_t GetHandleLimit() {
131 // Rounded down from value reported here:
132 // http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
133 return static_cast<size_t>(1 << 23);
134 }
135
136 // static
CreateProcessMetrics(ProcessHandle process)137 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
138 ProcessHandle process) {
139 return WrapUnique(new ProcessMetrics(process));
140 }
141
GetCumulativeCPUUsage()142 TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
143 FILETIME creation_time;
144 FILETIME exit_time;
145 FILETIME kernel_time;
146 FILETIME user_time;
147
148 if (!process_.is_valid())
149 return TimeDelta();
150
151 if (!GetProcessTimes(process_.get(), &creation_time, &exit_time, &kernel_time,
152 &user_time)) {
153 // This should never fail because we duplicate the handle to guarantee it
154 // will remain valid.
155 DCHECK(false);
156 return TimeDelta();
157 }
158
159 return TimeDelta::FromFileTime(kernel_time) +
160 TimeDelta::FromFileTime(user_time);
161 }
162
GetPreciseCumulativeCPUUsage()163 TimeDelta ProcessMetrics::GetPreciseCumulativeCPUUsage() {
164 #if defined(ARCH_CPU_ARM64)
165 // Precise CPU usage is not available on Arm CPUs because they don't support
166 // constant rate TSC.
167 return GetCumulativeCPUUsage();
168 #else // !defined(ARCH_CPU_ARM64)
169 if (!time_internal::HasConstantRateTSC())
170 return GetCumulativeCPUUsage();
171
172 ULONG64 process_cycle_time = 0;
173 if (!QueryProcessCycleTime(process_.get(), &process_cycle_time)) {
174 NOTREACHED();
175 return TimeDelta();
176 }
177
178 const double tsc_ticks_per_second = time_internal::TSCTicksPerSecond();
179 if (tsc_ticks_per_second == 0) {
180 return TimeDelta();
181 }
182
183 const double process_time_seconds = process_cycle_time / tsc_ticks_per_second;
184 return Seconds(process_time_seconds);
185 #endif // !defined(ARCH_CPU_ARM64)
186 }
187
GetIOCounters(IoCounters * io_counters) const188 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
189 if (!process_.is_valid())
190 return false;
191
192 return GetProcessIoCounters(process_.get(), io_counters) != FALSE;
193 }
194
GetCumulativeDiskUsageInBytes()195 uint64_t ProcessMetrics::GetCumulativeDiskUsageInBytes() {
196 IoCounters counters;
197 if (!GetIOCounters(&counters))
198 return 0;
199
200 return counters.ReadTransferCount + counters.WriteTransferCount +
201 counters.OtherTransferCount;
202 }
203
ProcessMetrics(ProcessHandle process)204 ProcessMetrics::ProcessMetrics(ProcessHandle process) {
205 if (process) {
206 HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
207 BOOL result = ::DuplicateHandle(::GetCurrentProcess(), process,
208 ::GetCurrentProcess(), &duplicate_handle,
209 PROCESS_QUERY_INFORMATION, FALSE, 0);
210 DPCHECK(result);
211 process_.Set(duplicate_handle);
212 }
213 }
214
GetSystemCommitCharge()215 size_t GetSystemCommitCharge() {
216 // Get the System Page Size.
217 SYSTEM_INFO system_info;
218 GetSystemInfo(&system_info);
219
220 PERFORMANCE_INFORMATION info;
221 if (!GetPerformanceInfo(&info, sizeof(info))) {
222 DLOG(ERROR) << "Failed to fetch internal performance info.";
223 return 0;
224 }
225 return (info.CommitTotal * system_info.dwPageSize) / 1024;
226 }
227
228 // This function uses the following mapping between MEMORYSTATUSEX and
229 // SystemMemoryInfoKB:
230 // ullTotalPhys ==> total
231 // ullAvailPhys ==> avail_phys
232 // ullTotalPageFile ==> swap_total
233 // ullAvailPageFile ==> swap_free
GetSystemMemoryInfo(SystemMemoryInfoKB * meminfo)234 bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
235 MEMORYSTATUSEX mem_status;
236 mem_status.dwLength = sizeof(mem_status);
237 if (!::GlobalMemoryStatusEx(&mem_status))
238 return false;
239
240 meminfo->total = saturated_cast<int>(mem_status.ullTotalPhys / 1024);
241 meminfo->avail_phys = saturated_cast<int>(mem_status.ullAvailPhys / 1024);
242 meminfo->swap_total = saturated_cast<int>(mem_status.ullTotalPageFile / 1024);
243 meminfo->swap_free = saturated_cast<int>(mem_status.ullAvailPageFile / 1024);
244
245 return true;
246 }
247
GetMallocUsage()248 size_t ProcessMetrics::GetMallocUsage() {
249 // Unsupported as getting malloc usage on Windows requires iterating through
250 // the heap which is slow and crashes.
251 return 0;
252 }
253
254 SystemPerformanceInfo::SystemPerformanceInfo() = default;
255 SystemPerformanceInfo::SystemPerformanceInfo(
256 const SystemPerformanceInfo& other) = default;
257 SystemPerformanceInfo& SystemPerformanceInfo::operator=(
258 const SystemPerformanceInfo& other) = default;
259
ToDict() const260 Value::Dict SystemPerformanceInfo::ToDict() const {
261 Value::Dict result;
262
263 // Write out uint64_t variables as doubles.
264 // Note: this may discard some precision, but for JS there's no other option.
265 result.Set("idle_time", strict_cast<double>(idle_time));
266 result.Set("read_transfer_count", strict_cast<double>(read_transfer_count));
267 result.Set("write_transfer_count", strict_cast<double>(write_transfer_count));
268 result.Set("other_transfer_count", strict_cast<double>(other_transfer_count));
269 result.Set("read_operation_count", strict_cast<double>(read_operation_count));
270 result.Set("write_operation_count",
271 strict_cast<double>(write_operation_count));
272 result.Set("other_operation_count",
273 strict_cast<double>(other_operation_count));
274 result.Set("pagefile_pages_written",
275 strict_cast<double>(pagefile_pages_written));
276 result.Set("pagefile_pages_write_ios",
277 strict_cast<double>(pagefile_pages_write_ios));
278 result.Set("available_pages", strict_cast<double>(available_pages));
279 result.Set("pages_read", strict_cast<double>(pages_read));
280 result.Set("page_read_ios", strict_cast<double>(page_read_ios));
281
282 return result;
283 }
284
285 // Retrieves performance counters from the operating system.
286 // Fills in the provided |info| structure. Returns true on success.
GetSystemPerformanceInfo(SystemPerformanceInfo * info)287 BASE_EXPORT bool GetSystemPerformanceInfo(SystemPerformanceInfo* info) {
288 SYSTEM_PERFORMANCE_INFORMATION counters = {};
289 {
290 // The call to NtQuerySystemInformation might block on a lock.
291 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
292 BlockingType::MAY_BLOCK);
293 if (::NtQuerySystemInformation(::SystemPerformanceInformation, &counters,
294 sizeof(SYSTEM_PERFORMANCE_INFORMATION),
295 nullptr) != STATUS_SUCCESS) {
296 return false;
297 }
298 }
299
300 info->idle_time = static_cast<uint64_t>(counters.IdleTime.QuadPart);
301 info->read_transfer_count =
302 static_cast<uint64_t>(counters.ReadTransferCount.QuadPart);
303 info->write_transfer_count =
304 static_cast<uint64_t>(counters.WriteTransferCount.QuadPart);
305 info->other_transfer_count =
306 static_cast<uint64_t>(counters.OtherTransferCount.QuadPart);
307 info->read_operation_count = counters.ReadOperationCount;
308 info->write_operation_count = counters.WriteOperationCount;
309 info->other_operation_count = counters.OtherOperationCount;
310 info->pagefile_pages_written = counters.PagefilePagesWritten;
311 info->pagefile_pages_write_ios = counters.PagefilePageWriteIos;
312 info->available_pages = counters.AvailablePages;
313 info->pages_read = counters.PagesRead;
314 info->page_read_ios = counters.PageReadIos;
315
316 return true;
317 }
318
319 } // namespace base
320