1 // 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.
5 #include "base/process/process_metrics.h"
11 #include "base/logging.h"
12 #include "base/sys_info.h"
17 // System pagesize. This value remains constant on x86/64 architectures.
18 const int PAGESIZE_KB
= 4;
20 typedef NTSTATUS(WINAPI
* NTQUERYSYSTEMINFORMATION
)(
21 SYSTEM_INFORMATION_CLASS SystemInformationClass
,
22 PVOID SystemInformation
,
23 ULONG SystemInformationLength
,
28 SystemMemoryInfoKB::SystemMemoryInfoKB() {
35 ProcessMetrics::~ProcessMetrics() { }
38 ProcessMetrics
* ProcessMetrics::CreateProcessMetrics(ProcessHandle process
) {
39 return new ProcessMetrics(process
);
42 size_t ProcessMetrics::GetPagefileUsage() const {
43 PROCESS_MEMORY_COUNTERS pmc
;
44 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
45 return pmc
.PagefileUsage
;
50 // Returns the peak space allocated for the pagefile, in bytes.
51 size_t ProcessMetrics::GetPeakPagefileUsage() const {
52 PROCESS_MEMORY_COUNTERS pmc
;
53 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
54 return pmc
.PeakPagefileUsage
;
59 // Returns the current working set size, in bytes.
60 size_t ProcessMetrics::GetWorkingSetSize() const {
61 PROCESS_MEMORY_COUNTERS pmc
;
62 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
63 return pmc
.WorkingSetSize
;
68 // Returns the peak working set size, in bytes.
69 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
70 PROCESS_MEMORY_COUNTERS pmc
;
71 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
72 return pmc
.PeakWorkingSetSize
;
77 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes
,
78 size_t* shared_bytes
) {
79 // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
80 // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
81 // information is simply not available. Hence, we will return 0 on unsupported
82 // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
83 PROCESS_MEMORY_COUNTERS_EX pmcx
;
85 GetProcessMemoryInfo(process_
,
86 reinterpret_cast<PROCESS_MEMORY_COUNTERS
*>(&pmcx
),
88 *private_bytes
= pmcx
.PrivateUsage
;
92 WorkingSetKBytes ws_usage
;
93 if (!GetWorkingSetKBytes(&ws_usage
))
96 *shared_bytes
= ws_usage
.shared
* 1024;
102 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes
* usage
) const {
103 MEMORY_BASIC_INFORMATION mbi
= {0};
104 size_t committed_private
= 0;
105 size_t committed_mapped
= 0;
106 size_t committed_image
= 0;
107 void* base_address
= NULL
;
108 while (VirtualQueryEx(process_
, base_address
, &mbi
, sizeof(mbi
)) ==
110 if (mbi
.State
== MEM_COMMIT
) {
111 if (mbi
.Type
== MEM_PRIVATE
) {
112 committed_private
+= mbi
.RegionSize
;
113 } else if (mbi
.Type
== MEM_MAPPED
) {
114 committed_mapped
+= mbi
.RegionSize
;
115 } else if (mbi
.Type
== MEM_IMAGE
) {
116 committed_image
+= mbi
.RegionSize
;
121 void* new_base
= (static_cast<BYTE
*>(mbi
.BaseAddress
)) + mbi
.RegionSize
;
122 // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION.
123 // If we query 64bit processes in a 32bit process, VirtualQueryEx()
124 // returns such data.
125 if (new_base
<= base_address
) {
131 base_address
= new_base
;
133 usage
->image
= committed_image
/ 1024;
134 usage
->mapped
= committed_mapped
/ 1024;
135 usage
->priv
= committed_private
/ 1024;
138 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes
* ws_usage
) const {
139 size_t ws_private
= 0;
140 size_t ws_shareable
= 0;
141 size_t ws_shared
= 0;
144 memset(ws_usage
, 0, sizeof(*ws_usage
));
146 DWORD number_of_entries
= 4096; // Just a guess.
147 PSAPI_WORKING_SET_INFORMATION
* buffer
= NULL
;
150 DWORD buffer_size
= sizeof(PSAPI_WORKING_SET_INFORMATION
) +
151 (number_of_entries
* sizeof(PSAPI_WORKING_SET_BLOCK
));
153 // if we can't expand the buffer, don't leak the previous
154 // contents or pass a NULL pointer to QueryWorkingSet
155 PSAPI_WORKING_SET_INFORMATION
* new_buffer
=
156 reinterpret_cast<PSAPI_WORKING_SET_INFORMATION
*>(
157 realloc(buffer
, buffer_size
));
164 // Call the function once to get number of items
165 if (QueryWorkingSet(process_
, buffer
, buffer_size
))
168 if (GetLastError() != ERROR_BAD_LENGTH
) {
173 number_of_entries
= static_cast<DWORD
>(buffer
->NumberOfEntries
);
175 // Maybe some entries are being added right now. Increase the buffer to
176 // take that into account.
177 number_of_entries
= static_cast<DWORD
>(number_of_entries
* 1.25);
179 if (--retries
== 0) {
180 free(buffer
); // If we're looping, eventually fail.
185 // On windows 2000 the function returns 1 even when the buffer is too small.
186 // The number of entries that we are going to parse is the minimum between the
187 // size we allocated and the real number of entries.
189 std::min(number_of_entries
, static_cast<DWORD
>(buffer
->NumberOfEntries
));
190 for (unsigned int i
= 0; i
< number_of_entries
; i
++) {
191 if (buffer
->WorkingSetInfo
[i
].Shared
) {
193 if (buffer
->WorkingSetInfo
[i
].ShareCount
> 1)
200 ws_usage
->priv
= ws_private
* PAGESIZE_KB
;
201 ws_usage
->shareable
= ws_shareable
* PAGESIZE_KB
;
202 ws_usage
->shared
= ws_shared
* PAGESIZE_KB
;
207 static uint64
FileTimeToUTC(const FILETIME
& ftime
) {
209 li
.LowPart
= ftime
.dwLowDateTime
;
210 li
.HighPart
= ftime
.dwHighDateTime
;
214 double ProcessMetrics::GetCPUUsage() {
215 FILETIME creation_time
;
217 FILETIME kernel_time
;
220 if (!GetProcessTimes(process_
, &creation_time
, &exit_time
,
221 &kernel_time
, &user_time
)) {
222 // We don't assert here because in some cases (such as in the Task Manager)
223 // we may call this function on a process that has just exited but we have
224 // not yet received the notification.
227 int64 system_time
= (FileTimeToUTC(kernel_time
) + FileTimeToUTC(user_time
)) /
229 TimeTicks time
= TimeTicks::Now();
231 if (last_system_time_
== 0) {
232 // First call, just set the last values.
233 last_system_time_
= system_time
;
234 last_cpu_time_
= time
;
238 int64 system_time_delta
= system_time
- last_system_time_
;
239 // FILETIME is in 100-nanosecond units, so this needs microseconds times 10.
240 int64 time_delta
= (time
- last_cpu_time_
).InMicroseconds() * 10;
241 DCHECK_NE(0U, time_delta
);
246 last_system_time_
= system_time
;
247 last_cpu_time_
= time
;
249 return static_cast<double>(system_time_delta
* 100.0) / time_delta
;
252 bool ProcessMetrics::GetIOCounters(IoCounters
* io_counters
) const {
253 return GetProcessIoCounters(process_
, io_counters
) != FALSE
;
256 ProcessMetrics::ProcessMetrics(ProcessHandle process
)
258 processor_count_(base::SysInfo::NumberOfProcessors()),
259 last_system_time_(0) {
262 // GetPerformanceInfo is not available on WIN2K. So we'll
263 // load it on-the-fly.
264 const wchar_t kPsapiDllName
[] = L
"psapi.dll";
265 typedef BOOL (WINAPI
*GetPerformanceInfoFunction
) (
266 PPERFORMANCE_INFORMATION pPerformanceInformation
,
269 // Beware of races if called concurrently from multiple threads.
270 static BOOL
InternalGetPerformanceInfo(
271 PPERFORMANCE_INFORMATION pPerformanceInformation
, DWORD cb
) {
272 static GetPerformanceInfoFunction GetPerformanceInfo_func
= NULL
;
273 if (!GetPerformanceInfo_func
) {
274 HMODULE psapi_dll
= ::GetModuleHandle(kPsapiDllName
);
276 GetPerformanceInfo_func
= reinterpret_cast<GetPerformanceInfoFunction
>(
277 GetProcAddress(psapi_dll
, "GetPerformanceInfo"));
279 if (!GetPerformanceInfo_func
) {
280 // The function could not be loaded!
281 memset(pPerformanceInformation
, 0, cb
);
285 return GetPerformanceInfo_func(pPerformanceInformation
, cb
);
288 size_t GetSystemCommitCharge() {
289 // Get the System Page Size.
290 SYSTEM_INFO system_info
;
291 GetSystemInfo(&system_info
);
293 PERFORMANCE_INFORMATION info
;
294 if (!InternalGetPerformanceInfo(&info
, sizeof(info
))) {
295 DLOG(ERROR
) << "Failed to fetch internal performance info.";
298 return (info
.CommitTotal
* system_info
.dwPageSize
) / 1024;
301 size_t GetPageSize() {
302 return PAGESIZE_KB
* 1024;
305 // This function uses the following mapping between MEMORYSTATUSEX and
306 // SystemMemoryInfoKB:
307 // ullTotalPhys ==> total
308 // ullAvailPhys ==> free
309 // ullTotalPageFile ==> swap_total
310 // ullAvailPageFile ==> swap_free
311 bool GetSystemMemoryInfo(SystemMemoryInfoKB
* meminfo
) {
312 MEMORYSTATUSEX mem_status
;
313 mem_status
.dwLength
= sizeof(mem_status
);
314 if (!::GlobalMemoryStatusEx(&mem_status
))
317 meminfo
->total
= mem_status
.ullTotalPhys
/ 1024;
318 meminfo
->free
= mem_status
.ullAvailPhys
/ 1024;
319 meminfo
->swap_total
= mem_status
.ullTotalPageFile
/ 1024;
320 meminfo
->swap_free
= mem_status
.ullAvailPageFile
/ 1024;