OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/process/process_metrics.h" | 5 #include "base/process/process_metrics.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 #include <psapi.h> | 8 #include <psapi.h> |
9 #include <stddef.h> | 9 #include <stddef.h> |
10 #include <stdint.h> | 10 #include <stdint.h> |
(...skipping 23 matching lines...) Expand all Loading... |
34 ProcessMetrics::~ProcessMetrics() { } | 34 ProcessMetrics::~ProcessMetrics() { } |
35 | 35 |
36 // static | 36 // static |
37 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( | 37 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics( |
38 ProcessHandle process) { | 38 ProcessHandle process) { |
39 return WrapUnique(new ProcessMetrics(process)); | 39 return WrapUnique(new ProcessMetrics(process)); |
40 } | 40 } |
41 | 41 |
42 size_t ProcessMetrics::GetPagefileUsage() const { | 42 size_t ProcessMetrics::GetPagefileUsage() const { |
43 PROCESS_MEMORY_COUNTERS pmc; | 43 PROCESS_MEMORY_COUNTERS pmc; |
44 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { | 44 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) { |
45 return pmc.PagefileUsage; | 45 return pmc.PagefileUsage; |
46 } | 46 } |
47 return 0; | 47 return 0; |
48 } | 48 } |
49 | 49 |
50 // Returns the peak space allocated for the pagefile, in bytes. | 50 // Returns the peak space allocated for the pagefile, in bytes. |
51 size_t ProcessMetrics::GetPeakPagefileUsage() const { | 51 size_t ProcessMetrics::GetPeakPagefileUsage() const { |
52 PROCESS_MEMORY_COUNTERS pmc; | 52 PROCESS_MEMORY_COUNTERS pmc; |
53 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { | 53 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) { |
54 return pmc.PeakPagefileUsage; | 54 return pmc.PeakPagefileUsage; |
55 } | 55 } |
56 return 0; | 56 return 0; |
57 } | 57 } |
58 | 58 |
59 // Returns the current working set size, in bytes. | 59 // Returns the current working set size, in bytes. |
60 size_t ProcessMetrics::GetWorkingSetSize() const { | 60 size_t ProcessMetrics::GetWorkingSetSize() const { |
61 PROCESS_MEMORY_COUNTERS pmc; | 61 PROCESS_MEMORY_COUNTERS pmc; |
62 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { | 62 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) { |
63 return pmc.WorkingSetSize; | 63 return pmc.WorkingSetSize; |
64 } | 64 } |
65 return 0; | 65 return 0; |
66 } | 66 } |
67 | 67 |
68 // Returns the peak working set size, in bytes. | 68 // Returns the peak working set size, in bytes. |
69 size_t ProcessMetrics::GetPeakWorkingSetSize() const { | 69 size_t ProcessMetrics::GetPeakWorkingSetSize() const { |
70 PROCESS_MEMORY_COUNTERS pmc; | 70 PROCESS_MEMORY_COUNTERS pmc; |
71 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { | 71 if (GetProcessMemoryInfo(process_.Get(), &pmc, sizeof(pmc))) { |
72 return pmc.PeakWorkingSetSize; | 72 return pmc.PeakWorkingSetSize; |
73 } | 73 } |
74 return 0; | 74 return 0; |
75 } | 75 } |
76 | 76 |
77 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, | 77 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, |
78 size_t* shared_bytes) const { | 78 size_t* shared_bytes) const { |
79 // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2. | 79 // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2. |
80 // GetProcessMemoryInfo() will simply fail on prior OS. So the requested | 80 // GetProcessMemoryInfo() will simply fail on prior OS. So the requested |
81 // information is simply not available. Hence, we will return 0 on unsupported | 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. | 82 // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member. |
83 PROCESS_MEMORY_COUNTERS_EX pmcx; | 83 PROCESS_MEMORY_COUNTERS_EX pmcx; |
84 if (private_bytes && | 84 if (private_bytes && |
85 GetProcessMemoryInfo(process_, | 85 GetProcessMemoryInfo(process_.Get(), |
86 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx), | 86 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx), |
87 sizeof(pmcx))) { | 87 sizeof(pmcx))) { |
88 *private_bytes = pmcx.PrivateUsage; | 88 *private_bytes = pmcx.PrivateUsage; |
89 } | 89 } |
90 | 90 |
91 if (shared_bytes) { | 91 if (shared_bytes) { |
92 WorkingSetKBytes ws_usage; | 92 WorkingSetKBytes ws_usage; |
93 if (!GetWorkingSetKBytes(&ws_usage)) | 93 if (!GetWorkingSetKBytes(&ws_usage)) |
94 return false; | 94 return false; |
95 | 95 |
96 *shared_bytes = ws_usage.shared * 1024; | 96 *shared_bytes = ws_usage.shared * 1024; |
97 } | 97 } |
98 | 98 |
99 return true; | 99 return true; |
100 } | 100 } |
101 | 101 |
102 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { | 102 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { |
103 MEMORY_BASIC_INFORMATION mbi = {0}; | 103 MEMORY_BASIC_INFORMATION mbi = {0}; |
104 size_t committed_private = 0; | 104 size_t committed_private = 0; |
105 size_t committed_mapped = 0; | 105 size_t committed_mapped = 0; |
106 size_t committed_image = 0; | 106 size_t committed_image = 0; |
107 void* base_address = NULL; | 107 void* base_address = NULL; |
108 while (VirtualQueryEx(process_, base_address, &mbi, sizeof(mbi)) == | 108 while (VirtualQueryEx(process_.Get(), base_address, &mbi, sizeof(mbi)) == |
109 sizeof(mbi)) { | 109 sizeof(mbi)) { |
110 if (mbi.State == MEM_COMMIT) { | 110 if (mbi.State == MEM_COMMIT) { |
111 if (mbi.Type == MEM_PRIVATE) { | 111 if (mbi.Type == MEM_PRIVATE) { |
112 committed_private += mbi.RegionSize; | 112 committed_private += mbi.RegionSize; |
113 } else if (mbi.Type == MEM_MAPPED) { | 113 } else if (mbi.Type == MEM_MAPPED) { |
114 committed_mapped += mbi.RegionSize; | 114 committed_mapped += mbi.RegionSize; |
115 } else if (mbi.Type == MEM_IMAGE) { | 115 } else if (mbi.Type == MEM_IMAGE) { |
116 committed_image += mbi.RegionSize; | 116 committed_image += mbi.RegionSize; |
117 } else { | 117 } else { |
118 NOTREACHED(); | 118 NOTREACHED(); |
119 } | 119 } |
(...skipping 27 matching lines...) Expand all Loading... |
147 // Use UncheckedMalloc here because this can be called from the code | 147 // Use UncheckedMalloc here because this can be called from the code |
148 // that handles low memory condition. | 148 // that handles low memory condition. |
149 return UncheckedMalloc(size, reinterpret_cast<void**>(&buffer_)); | 149 return UncheckedMalloc(size, reinterpret_cast<void**>(&buffer_)); |
150 } | 150 } |
151 | 151 |
152 const PSAPI_WORKING_SET_INFORMATION* operator ->() const { return buffer_; } | 152 const PSAPI_WORKING_SET_INFORMATION* operator ->() const { return buffer_; } |
153 | 153 |
154 size_t GetPageEntryCount() const { return number_of_entries; } | 154 size_t GetPageEntryCount() const { return number_of_entries; } |
155 | 155 |
156 // This function is used to get page entries for a process. | 156 // This function is used to get page entries for a process. |
157 bool QueryPageEntries(const ProcessHandle& process_) { | 157 bool QueryPageEntries(const ProcessHandle& process) { |
158 int retries = 5; | 158 int retries = 5; |
159 number_of_entries = 4096; // Just a guess. | 159 number_of_entries = 4096; // Just a guess. |
160 | 160 |
161 for (;;) { | 161 for (;;) { |
162 size_t buffer_size = | 162 size_t buffer_size = |
163 sizeof(PSAPI_WORKING_SET_INFORMATION) + | 163 sizeof(PSAPI_WORKING_SET_INFORMATION) + |
164 (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK)); | 164 (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK)); |
165 | 165 |
166 if (!Reserve(buffer_size)) | 166 if (!Reserve(buffer_size)) |
167 return false; | 167 return false; |
168 | 168 |
169 // On success, |buffer_| is populated with info about the working set of | 169 // On success, |buffer_| is populated with info about the working set of |
170 // |process_|. On ERROR_BAD_LENGTH failure, increase the size of the | 170 // |process|. On ERROR_BAD_LENGTH failure, increase the size of the |
171 // buffer and try again. | 171 // buffer and try again. |
172 if (QueryWorkingSet(process_, buffer_, buffer_size)) | 172 if (QueryWorkingSet(process, buffer_, buffer_size)) |
173 break; // Success | 173 break; // Success |
174 | 174 |
175 if (GetLastError() != ERROR_BAD_LENGTH) | 175 if (GetLastError() != ERROR_BAD_LENGTH) |
176 return false; | 176 return false; |
177 | 177 |
178 number_of_entries = buffer_->NumberOfEntries; | 178 number_of_entries = buffer_->NumberOfEntries; |
179 | 179 |
180 // Maybe some entries are being added right now. Increase the buffer to | 180 // Maybe some entries are being added right now. Increase the buffer to |
181 // take that into account. Increasing by 10% should generally be enough, | 181 // take that into account. Increasing by 10% should generally be enough, |
182 // especially considering the potentially low memory condition during the | 182 // especially considering the potentially low memory condition during the |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 | 219 |
220 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { | 220 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { |
221 size_t ws_private = 0; | 221 size_t ws_private = 0; |
222 size_t ws_shareable = 0; | 222 size_t ws_shareable = 0; |
223 size_t ws_shared = 0; | 223 size_t ws_shared = 0; |
224 | 224 |
225 DCHECK(ws_usage); | 225 DCHECK(ws_usage); |
226 memset(ws_usage, 0, sizeof(*ws_usage)); | 226 memset(ws_usage, 0, sizeof(*ws_usage)); |
227 | 227 |
228 WorkingSetInformationBuffer buffer; | 228 WorkingSetInformationBuffer buffer; |
229 if (!buffer.QueryPageEntries(process_)) | 229 if (!buffer.QueryPageEntries(process_.Get())) |
230 return false; | 230 return false; |
231 | 231 |
232 size_t num_page_entries = buffer.GetPageEntryCount(); | 232 size_t num_page_entries = buffer.GetPageEntryCount(); |
233 for (size_t i = 0; i < num_page_entries; i++) { | 233 for (size_t i = 0; i < num_page_entries; i++) { |
234 if (buffer->WorkingSetInfo[i].Shared) { | 234 if (buffer->WorkingSetInfo[i].Shared) { |
235 ws_shareable++; | 235 ws_shareable++; |
236 if (buffer->WorkingSetInfo[i].ShareCount > 1) | 236 if (buffer->WorkingSetInfo[i].ShareCount > 1) |
237 ws_shared++; | 237 ws_shared++; |
238 } else { | 238 } else { |
239 ws_private++; | 239 ws_private++; |
240 } | 240 } |
241 } | 241 } |
242 | 242 |
243 ws_usage->priv = ws_private * PAGESIZE_KB; | 243 ws_usage->priv = ws_private * PAGESIZE_KB; |
244 ws_usage->shareable = ws_shareable * PAGESIZE_KB; | 244 ws_usage->shareable = ws_shareable * PAGESIZE_KB; |
245 ws_usage->shared = ws_shared * PAGESIZE_KB; | 245 ws_usage->shared = ws_shared * PAGESIZE_KB; |
246 | 246 |
247 return true; | 247 return true; |
248 } | 248 } |
249 | 249 |
250 // This function calculates the proportional set size for a process. | 250 // This function calculates the proportional set size for a process. |
251 bool ProcessMetrics::GetProportionalSetSizeBytes(uint64_t* pss_bytes) const { | 251 bool ProcessMetrics::GetProportionalSetSizeBytes(uint64_t* pss_bytes) const { |
252 double ws_pss = 0.0; | 252 double ws_pss = 0.0; |
253 | 253 |
254 WorkingSetInformationBuffer buffer; | 254 WorkingSetInformationBuffer buffer; |
255 if (!buffer.QueryPageEntries(process_)) | 255 if (!buffer.QueryPageEntries(process_.Get())) |
256 return false; | 256 return false; |
257 | 257 |
258 size_t num_page_entries = buffer.GetPageEntryCount(); | 258 size_t num_page_entries = buffer.GetPageEntryCount(); |
259 for (size_t i = 0; i < num_page_entries; i++) { | 259 for (size_t i = 0; i < num_page_entries; i++) { |
260 if (buffer->WorkingSetInfo[i].Shared && | 260 if (buffer->WorkingSetInfo[i].Shared && |
261 buffer->WorkingSetInfo[i].ShareCount > 0) | 261 buffer->WorkingSetInfo[i].ShareCount > 0) |
262 ws_pss += 1.0 / buffer->WorkingSetInfo[i].ShareCount; | 262 ws_pss += 1.0 / buffer->WorkingSetInfo[i].ShareCount; |
263 else | 263 else |
264 ws_pss += 1.0; | 264 ws_pss += 1.0; |
265 } | 265 } |
266 | 266 |
267 *pss_bytes = static_cast<uint64_t>(ws_pss * GetPageSize()); | 267 *pss_bytes = static_cast<uint64_t>(ws_pss * GetPageSize()); |
268 return true; | 268 return true; |
269 } | 269 } |
270 | 270 |
271 static uint64_t FileTimeToUTC(const FILETIME& ftime) { | 271 static uint64_t FileTimeToUTC(const FILETIME& ftime) { |
272 LARGE_INTEGER li; | 272 LARGE_INTEGER li; |
273 li.LowPart = ftime.dwLowDateTime; | 273 li.LowPart = ftime.dwLowDateTime; |
274 li.HighPart = ftime.dwHighDateTime; | 274 li.HighPart = ftime.dwHighDateTime; |
275 return li.QuadPart; | 275 return li.QuadPart; |
276 } | 276 } |
277 | 277 |
278 double ProcessMetrics::GetCPUUsage() { | 278 double ProcessMetrics::GetCPUUsage() { |
279 FILETIME creation_time; | 279 FILETIME creation_time; |
280 FILETIME exit_time; | 280 FILETIME exit_time; |
281 FILETIME kernel_time; | 281 FILETIME kernel_time; |
282 FILETIME user_time; | 282 FILETIME user_time; |
283 | 283 |
284 if (!GetProcessTimes(process_, &creation_time, &exit_time, | 284 if (!GetProcessTimes(process_.Get(), &creation_time, &exit_time, &kernel_time, |
285 &kernel_time, &user_time)) { | 285 &user_time)) { |
286 // We don't assert here because in some cases (such as in the Task Manager) | 286 // We don't assert here because in some cases (such as in the Task Manager) |
287 // we may call this function on a process that has just exited but we have | 287 // we may call this function on a process that has just exited but we have |
288 // not yet received the notification. | 288 // not yet received the notification. |
289 return 0; | 289 return 0; |
290 } | 290 } |
291 int64_t system_time = | 291 int64_t system_time = |
292 (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / | 292 (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / |
293 processor_count_; | 293 processor_count_; |
294 TimeTicks time = TimeTicks::Now(); | 294 TimeTicks time = TimeTicks::Now(); |
295 | 295 |
(...skipping 12 matching lines...) Expand all Loading... |
308 return 0; | 308 return 0; |
309 | 309 |
310 | 310 |
311 last_system_time_ = system_time; | 311 last_system_time_ = system_time; |
312 last_cpu_time_ = time; | 312 last_cpu_time_ = time; |
313 | 313 |
314 return static_cast<double>(system_time_delta * 100.0) / time_delta; | 314 return static_cast<double>(system_time_delta * 100.0) / time_delta; |
315 } | 315 } |
316 | 316 |
317 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { | 317 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { |
318 return GetProcessIoCounters(process_, io_counters) != FALSE; | 318 return GetProcessIoCounters(process_.Get(), io_counters) != FALSE; |
319 } | 319 } |
320 | 320 |
321 ProcessMetrics::ProcessMetrics(ProcessHandle process) | 321 ProcessMetrics::ProcessMetrics(ProcessHandle process) |
322 : process_(process), | 322 : processor_count_(SysInfo::NumberOfProcessors()), last_system_time_(0) { |
323 processor_count_(SysInfo::NumberOfProcessors()), | 323 if (process) { |
324 last_system_time_(0) {} | 324 HANDLE duplicate_handle; |
| 325 BOOL result = ::DuplicateHandle(::GetCurrentProcess(), process, |
| 326 ::GetCurrentProcess(), &duplicate_handle, |
| 327 PROCESS_QUERY_INFORMATION, FALSE, 0); |
| 328 DCHECK(result); |
| 329 process_.Set(duplicate_handle); |
| 330 } |
| 331 } |
325 | 332 |
326 size_t GetSystemCommitCharge() { | 333 size_t GetSystemCommitCharge() { |
327 // Get the System Page Size. | 334 // Get the System Page Size. |
328 SYSTEM_INFO system_info; | 335 SYSTEM_INFO system_info; |
329 GetSystemInfo(&system_info); | 336 GetSystemInfo(&system_info); |
330 | 337 |
331 PERFORMANCE_INFORMATION info; | 338 PERFORMANCE_INFORMATION info; |
332 if (!GetPerformanceInfo(&info, sizeof(info))) { | 339 if (!GetPerformanceInfo(&info, sizeof(info))) { |
333 DLOG(ERROR) << "Failed to fetch internal performance info."; | 340 DLOG(ERROR) << "Failed to fetch internal performance info."; |
334 return 0; | 341 return 0; |
(...skipping 19 matching lines...) Expand all Loading... |
354 | 361 |
355 meminfo->total = mem_status.ullTotalPhys / 1024; | 362 meminfo->total = mem_status.ullTotalPhys / 1024; |
356 meminfo->avail_phys = mem_status.ullAvailPhys / 1024; | 363 meminfo->avail_phys = mem_status.ullAvailPhys / 1024; |
357 meminfo->swap_total = mem_status.ullTotalPageFile / 1024; | 364 meminfo->swap_total = mem_status.ullTotalPageFile / 1024; |
358 meminfo->swap_free = mem_status.ullAvailPageFile / 1024; | 365 meminfo->swap_free = mem_status.ullAvailPageFile / 1024; |
359 | 366 |
360 return true; | 367 return true; |
361 } | 368 } |
362 | 369 |
363 } // namespace base | 370 } // namespace base |
OLD | NEW |