Add ICU message format support
[chromium-blink-merge.git] / base / process / process_metrics_win.cc
blobc3b3e50ff615470814e47af1742103fa0523a7da
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"
7 #include <windows.h>
8 #include <psapi.h>
9 #include <winternl.h>
11 #include "base/logging.h"
12 #include "base/sys_info.h"
14 namespace base {
15 namespace {
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,
24 PULONG ReturnLength);
26 } // namespace
28 SystemMemoryInfoKB::SystemMemoryInfoKB() {
29 total = 0;
30 free = 0;
31 swap_total = 0;
32 swap_free = 0;
35 ProcessMetrics::~ProcessMetrics() { }
37 // static
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;
47 return 0;
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;
56 return 0;
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;
65 return 0;
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;
74 return 0;
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;
84 if (private_bytes &&
85 GetProcessMemoryInfo(process_,
86 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
87 sizeof(pmcx))) {
88 *private_bytes = pmcx.PrivateUsage;
91 if (shared_bytes) {
92 WorkingSetKBytes ws_usage;
93 if (!GetWorkingSetKBytes(&ws_usage))
94 return false;
96 *shared_bytes = ws_usage.shared * 1024;
99 return true;
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)) ==
109 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;
117 } else {
118 NOTREACHED();
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) {
126 usage->image = 0;
127 usage->mapped = 0;
128 usage->priv = 0;
129 return;
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;
143 DCHECK(ws_usage);
144 memset(ws_usage, 0, sizeof(*ws_usage));
146 DWORD number_of_entries = 4096; // Just a guess.
147 PSAPI_WORKING_SET_INFORMATION* buffer = NULL;
148 int retries = 5;
149 for (;;) {
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));
158 if (!new_buffer) {
159 free(buffer);
160 return false;
162 buffer = new_buffer;
164 // Call the function once to get number of items
165 if (QueryWorkingSet(process_, buffer, buffer_size))
166 break; // Success
168 if (GetLastError() != ERROR_BAD_LENGTH) {
169 free(buffer);
170 return false;
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.
181 return false;
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.
188 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) {
192 ws_shareable++;
193 if (buffer->WorkingSetInfo[i].ShareCount > 1)
194 ws_shared++;
195 } else {
196 ws_private++;
200 ws_usage->priv = ws_private * PAGESIZE_KB;
201 ws_usage->shareable = ws_shareable * PAGESIZE_KB;
202 ws_usage->shared = ws_shared * PAGESIZE_KB;
203 free(buffer);
204 return true;
207 static uint64 FileTimeToUTC(const FILETIME& ftime) {
208 LARGE_INTEGER li;
209 li.LowPart = ftime.dwLowDateTime;
210 li.HighPart = ftime.dwHighDateTime;
211 return li.QuadPart;
214 double ProcessMetrics::GetCPUUsage() {
215 FILETIME creation_time;
216 FILETIME exit_time;
217 FILETIME kernel_time;
218 FILETIME user_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.
225 return 0;
227 int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
228 processor_count_;
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;
235 return 0;
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);
242 if (time_delta == 0)
243 return 0;
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)
257 : process_(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,
267 DWORD cb);
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);
275 if (psapi_dll)
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);
282 return FALSE;
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.";
296 return 0;
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))
315 return false;
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;
322 return true;
325 } // namespace base