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"
10 #include "base/logging.h"
11 #include "base/sys_info.h"
15 // System pagesize. This value remains constant on x86/64 architectures.
16 const int PAGESIZE_KB
= 4;
18 ProcessMetrics::~ProcessMetrics() { }
21 ProcessMetrics
* ProcessMetrics::CreateProcessMetrics(ProcessHandle process
) {
22 return new ProcessMetrics(process
);
25 size_t ProcessMetrics::GetPagefileUsage() const {
26 PROCESS_MEMORY_COUNTERS pmc
;
27 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
28 return pmc
.PagefileUsage
;
33 // Returns the peak space allocated for the pagefile, in bytes.
34 size_t ProcessMetrics::GetPeakPagefileUsage() const {
35 PROCESS_MEMORY_COUNTERS pmc
;
36 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
37 return pmc
.PeakPagefileUsage
;
42 // Returns the current working set size, in bytes.
43 size_t ProcessMetrics::GetWorkingSetSize() const {
44 PROCESS_MEMORY_COUNTERS pmc
;
45 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
46 return pmc
.WorkingSetSize
;
51 // Returns the peak working set size, in bytes.
52 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
53 PROCESS_MEMORY_COUNTERS pmc
;
54 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
55 return pmc
.PeakWorkingSetSize
;
60 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes
,
61 size_t* shared_bytes
) {
62 // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
63 // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
64 // information is simply not available. Hence, we will return 0 on unsupported
65 // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
66 PROCESS_MEMORY_COUNTERS_EX pmcx
;
68 GetProcessMemoryInfo(process_
,
69 reinterpret_cast<PROCESS_MEMORY_COUNTERS
*>(&pmcx
),
71 *private_bytes
= pmcx
.PrivateUsage
;
75 WorkingSetKBytes ws_usage
;
76 if (!GetWorkingSetKBytes(&ws_usage
))
79 *shared_bytes
= ws_usage
.shared
* 1024;
85 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes
* usage
) const {
86 MEMORY_BASIC_INFORMATION mbi
= {0};
87 size_t committed_private
= 0;
88 size_t committed_mapped
= 0;
89 size_t committed_image
= 0;
90 void* base_address
= NULL
;
91 while (VirtualQueryEx(process_
, base_address
, &mbi
, sizeof(mbi
)) ==
93 if (mbi
.State
== MEM_COMMIT
) {
94 if (mbi
.Type
== MEM_PRIVATE
) {
95 committed_private
+= mbi
.RegionSize
;
96 } else if (mbi
.Type
== MEM_MAPPED
) {
97 committed_mapped
+= mbi
.RegionSize
;
98 } else if (mbi
.Type
== MEM_IMAGE
) {
99 committed_image
+= mbi
.RegionSize
;
104 void* new_base
= (static_cast<BYTE
*>(mbi
.BaseAddress
)) + mbi
.RegionSize
;
105 // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION.
106 // If we query 64bit processes in a 32bit process, VirtualQueryEx()
107 // returns such data.
108 if (new_base
<= base_address
) {
114 base_address
= new_base
;
116 usage
->image
= committed_image
/ 1024;
117 usage
->mapped
= committed_mapped
/ 1024;
118 usage
->priv
= committed_private
/ 1024;
121 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes
* ws_usage
) const {
122 size_t ws_private
= 0;
123 size_t ws_shareable
= 0;
124 size_t ws_shared
= 0;
127 memset(ws_usage
, 0, sizeof(*ws_usage
));
129 DWORD number_of_entries
= 4096; // Just a guess.
130 PSAPI_WORKING_SET_INFORMATION
* buffer
= NULL
;
133 DWORD buffer_size
= sizeof(PSAPI_WORKING_SET_INFORMATION
) +
134 (number_of_entries
* sizeof(PSAPI_WORKING_SET_BLOCK
));
136 // if we can't expand the buffer, don't leak the previous
137 // contents or pass a NULL pointer to QueryWorkingSet
138 PSAPI_WORKING_SET_INFORMATION
* new_buffer
=
139 reinterpret_cast<PSAPI_WORKING_SET_INFORMATION
*>(
140 realloc(buffer
, buffer_size
));
147 // Call the function once to get number of items
148 if (QueryWorkingSet(process_
, buffer
, buffer_size
))
151 if (GetLastError() != ERROR_BAD_LENGTH
) {
156 number_of_entries
= static_cast<DWORD
>(buffer
->NumberOfEntries
);
158 // Maybe some entries are being added right now. Increase the buffer to
159 // take that into account.
160 number_of_entries
= static_cast<DWORD
>(number_of_entries
* 1.25);
162 if (--retries
== 0) {
163 free(buffer
); // If we're looping, eventually fail.
168 // On windows 2000 the function returns 1 even when the buffer is too small.
169 // The number of entries that we are going to parse is the minimum between the
170 // size we allocated and the real number of entries.
172 std::min(number_of_entries
, static_cast<DWORD
>(buffer
->NumberOfEntries
));
173 for (unsigned int i
= 0; i
< number_of_entries
; i
++) {
174 if (buffer
->WorkingSetInfo
[i
].Shared
) {
176 if (buffer
->WorkingSetInfo
[i
].ShareCount
> 1)
183 ws_usage
->priv
= ws_private
* PAGESIZE_KB
;
184 ws_usage
->shareable
= ws_shareable
* PAGESIZE_KB
;
185 ws_usage
->shared
= ws_shared
* PAGESIZE_KB
;
190 static uint64
FileTimeToUTC(const FILETIME
& ftime
) {
192 li
.LowPart
= ftime
.dwLowDateTime
;
193 li
.HighPart
= ftime
.dwHighDateTime
;
197 double ProcessMetrics::GetCPUUsage() {
198 FILETIME creation_time
;
200 FILETIME kernel_time
;
203 if (!GetProcessTimes(process_
, &creation_time
, &exit_time
,
204 &kernel_time
, &user_time
)) {
205 // We don't assert here because in some cases (such as in the Task Manager)
206 // we may call this function on a process that has just exited but we have
207 // not yet received the notification.
210 int64 system_time
= (FileTimeToUTC(kernel_time
) + FileTimeToUTC(user_time
)) /
212 TimeTicks time
= TimeTicks::Now();
214 if (last_system_time_
== 0) {
215 // First call, just set the last values.
216 last_system_time_
= system_time
;
217 last_cpu_time_
= time
;
221 int64 system_time_delta
= system_time
- last_system_time_
;
222 // FILETIME is in 100-nanosecond units, so this needs microseconds times 10.
223 int64 time_delta
= (time
- last_cpu_time_
).InMicroseconds() * 10;
224 DCHECK_NE(0U, time_delta
);
228 // We add time_delta / 2 so the result is rounded.
229 int cpu
= static_cast<int>((system_time_delta
* 100 + time_delta
/ 2) /
232 last_system_time_
= system_time
;
233 last_cpu_time_
= time
;
238 bool ProcessMetrics::CalculateFreeMemory(FreeMBytes
* free
) const {
239 const SIZE_T kTopAddress
= 0x7F000000;
240 const SIZE_T kMegabyte
= 1024 * 1024;
241 SIZE_T accumulated
= 0;
243 MEMORY_BASIC_INFORMATION largest
= {0};
245 while (scan
< kTopAddress
) {
246 MEMORY_BASIC_INFORMATION info
;
247 if (!::VirtualQueryEx(process_
, reinterpret_cast<void*>(scan
),
248 &info
, sizeof(info
)))
250 if (info
.State
== MEM_FREE
) {
251 accumulated
+= info
.RegionSize
;
252 if (info
.RegionSize
> largest
.RegionSize
)
255 scan
+= info
.RegionSize
;
257 free
->largest
= largest
.RegionSize
/ kMegabyte
;
258 free
->largest_ptr
= largest
.BaseAddress
;
259 free
->total
= accumulated
/ kMegabyte
;
263 bool ProcessMetrics::GetIOCounters(IoCounters
* io_counters
) const {
264 return GetProcessIoCounters(process_
, io_counters
) != FALSE
;
267 ProcessMetrics::ProcessMetrics(ProcessHandle process
)
269 processor_count_(base::SysInfo::NumberOfProcessors()),
270 last_system_time_(0) {
273 // GetPerformanceInfo is not available on WIN2K. So we'll
274 // load it on-the-fly.
275 const wchar_t kPsapiDllName
[] = L
"psapi.dll";
276 typedef BOOL (WINAPI
*GetPerformanceInfoFunction
) (
277 PPERFORMANCE_INFORMATION pPerformanceInformation
,
280 // Beware of races if called concurrently from multiple threads.
281 static BOOL
InternalGetPerformanceInfo(
282 PPERFORMANCE_INFORMATION pPerformanceInformation
, DWORD cb
) {
283 static GetPerformanceInfoFunction GetPerformanceInfo_func
= NULL
;
284 if (!GetPerformanceInfo_func
) {
285 HMODULE psapi_dll
= ::GetModuleHandle(kPsapiDllName
);
287 GetPerformanceInfo_func
= reinterpret_cast<GetPerformanceInfoFunction
>(
288 GetProcAddress(psapi_dll
, "GetPerformanceInfo"));
290 if (!GetPerformanceInfo_func
) {
291 // The function could be loaded!
292 memset(pPerformanceInformation
, 0, cb
);
296 return GetPerformanceInfo_func(pPerformanceInformation
, cb
);
299 size_t GetSystemCommitCharge() {
300 // Get the System Page Size.
301 SYSTEM_INFO system_info
;
302 GetSystemInfo(&system_info
);
304 PERFORMANCE_INFORMATION info
;
305 if (!InternalGetPerformanceInfo(&info
, sizeof(info
))) {
306 DLOG(ERROR
) << "Failed to fetch internal performance info.";
309 return (info
.CommitTotal
* system_info
.dwPageSize
) / 1024;