VirtualBox

source: vbox/trunk/src/VBox/Main/win/PerformanceWin.cpp@ 27950

Last change on this file since 27950 was 27950, checked in by vboxsync, 15 years ago

#4790: Metrics: Fixed preCollect for Windows and Linux

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: PerformanceWin.cpp 27950 2010-04-01 17:07:40Z vboxsync $ */
2
3/** @file
4 *
5 * VBox Windows-specific Performance Classes implementation.
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#ifndef _WIN32_WINNT
25#define _WIN32_WINNT 0x0500
26#else /* !_WIN32_WINNT */
27#if (_WIN32_WINNT < 0x0500)
28#error Win XP or later required!
29#endif /* _WIN32_WINNT < 0x0500 */
30#endif /* !_WIN32_WINNT */
31
32#include <windows.h>
33#include <winternl.h>
34#include <psapi.h>
35extern "C" {
36#include <powrprof.h>
37}
38
39#include <iprt/err.h>
40#include <iprt/mp.h>
41#include <iprt/mem.h>
42
43#include <map>
44
45#include "Logging.h"
46#include "Performance.h"
47
48#ifndef NT_ERROR
49#define NT_ERROR(Status) ((ULONG)(Status) >> 30 == 3)
50#endif
51
52namespace pm {
53
54class CollectorWin : public CollectorHAL
55{
56public:
57 CollectorWin();
58 virtual ~CollectorWin();
59 virtual int preCollect(const CollectorHints& hints, uint64_t /* iTick */);
60 virtual int getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle);
61 virtual int getHostCpuMHz(ULONG *mhz);
62 virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
63 virtual int getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel);
64 virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used);
65
66 virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
67 virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
68private:
69 struct VMProcessStats
70 {
71 uint64_t cpuUser;
72 uint64_t cpuKernel;
73 uint64_t cpuTotal;
74 uint64_t ramUsed;
75 };
76
77 typedef std::map<RTPROCESS, VMProcessStats> VMProcessMap;
78
79 VMProcessMap mProcessStats;
80
81 typedef BOOL (WINAPI *PFNGST)(
82 LPFILETIME lpIdleTime,
83 LPFILETIME lpKernelTime,
84 LPFILETIME lpUserTime);
85 typedef NTSTATUS (WINAPI *PFNNQSI)(
86 SYSTEM_INFORMATION_CLASS SystemInformationClass,
87 PVOID SystemInformation,
88 ULONG SystemInformationLength,
89 PULONG ReturnLength);
90
91 PFNGST mpfnGetSystemTimes;
92 PFNNQSI mpfnNtQuerySystemInformation;
93 HMODULE mhNtDll;
94};
95
96CollectorHAL *createHAL()
97{
98 return new CollectorWin();
99}
100
101CollectorWin::CollectorWin() : mhNtDll(0)
102{
103 mpfnGetSystemTimes = (PFNGST)GetProcAddress(
104 GetModuleHandle(TEXT("kernel32.dll")),
105 "GetSystemTimes");
106 if (!mpfnGetSystemTimes)
107 {
108 /* Fall back to deprecated NtQuerySystemInformation */
109 if (!(mhNtDll = LoadLibrary(TEXT("ntdll.dll"))))
110 {
111 LogRel(("Failed to load NTDLL.DLL with error 0x%x. GetSystemTimes() is"
112 " not available either. CPU and VM metrics will not be collected.\n",
113 GetLastError()));
114 mpfnNtQuerySystemInformation = 0;
115 }
116 else if (!(mpfnNtQuerySystemInformation = (PFNNQSI)GetProcAddress(mhNtDll,
117 "NtQuerySystemInformation")))
118 {
119 LogRel(("Neither GetSystemTimes() nor NtQuerySystemInformation() is"
120 " not available. CPU and VM metrics will not be collected.\n"));
121 mpfnNtQuerySystemInformation = 0;
122 }
123 }
124}
125
126CollectorWin::~CollectorWin()
127{
128 if (mhNtDll)
129 FreeLibrary(mhNtDll);
130}
131
132#define FILETTIME_TO_100NS(ft) (((uint64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime)
133
134int CollectorWin::preCollect(const CollectorHints& hints, uint64_t /* iTick */)
135{
136 LogFlowThisFuncEnter();
137
138 uint64_t user, kernel, idle, total;
139 int rc = getRawHostCpuLoad(&user, &kernel, &idle);
140 if (RT_FAILURE(rc))
141 return rc;
142 total = user + kernel + idle;
143
144 DWORD dwError;
145 const CollectorHints::ProcessList& processes = hints.getProcessFlags();
146 CollectorHints::ProcessList::const_iterator it;
147
148 mProcessStats.clear();
149
150 for (it = processes.begin(); it != processes.end() && RT_SUCCESS(rc); it++)
151 {
152 RTPROCESS process = it->first;
153 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
154 FALSE, process);
155
156 if (!h)
157 {
158 dwError = GetLastError();
159 Log (("OpenProcess() -> 0x%x\n", dwError));
160 rc = RTErrConvertFromWin32(dwError);
161 break;
162 }
163
164 VMProcessStats vmStats;
165 if ((it->second & COLLECT_CPU_LOAD) != 0)
166 {
167 FILETIME ftCreate, ftExit, ftKernel, ftUser;
168 if (!GetProcessTimes(h, &ftCreate, &ftExit, &ftKernel, &ftUser))
169 {
170 dwError = GetLastError();
171 Log (("GetProcessTimes() -> 0x%x\n", dwError));
172 rc = RTErrConvertFromWin32(dwError);
173 }
174 else
175 {
176 vmStats.cpuKernel = FILETTIME_TO_100NS(ftKernel);
177 vmStats.cpuUser = FILETTIME_TO_100NS(ftUser);
178 vmStats.cpuTotal = total;
179 }
180 }
181 if (RT_SUCCESS(rc) && (it->second & COLLECT_RAM_USAGE) != 0)
182 {
183 PROCESS_MEMORY_COUNTERS pmc;
184 if (!GetProcessMemoryInfo(h, &pmc, sizeof(pmc)))
185 {
186 dwError = GetLastError();
187 Log (("GetProcessMemoryInfo() -> 0x%x\n", dwError));
188 rc = RTErrConvertFromWin32(dwError);
189 }
190 else
191 vmStats.ramUsed = pmc.WorkingSetSize;
192 }
193 CloseHandle(h);
194 mProcessStats[process] = vmStats;
195 }
196
197 LogFlowThisFuncLeave();
198
199 return rc;
200}
201
202int CollectorWin::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle)
203{
204 return VERR_NOT_IMPLEMENTED;
205}
206
207typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
208{
209 LARGE_INTEGER IdleTime;
210 LARGE_INTEGER KernelTime;
211 LARGE_INTEGER UserTime;
212 LARGE_INTEGER Reserved1[2];
213 ULONG Reserved2;
214} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
215
216int CollectorWin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
217{
218 LogFlowThisFuncEnter();
219
220 FILETIME ftIdle, ftKernel, ftUser;
221
222 if (mpfnGetSystemTimes)
223 {
224 if (!mpfnGetSystemTimes(&ftIdle, &ftKernel, &ftUser))
225 {
226 DWORD dwError = GetLastError();
227 Log (("GetSystemTimes() -> 0x%x\n", dwError));
228 return RTErrConvertFromWin32(dwError);
229 }
230
231 *user = FILETTIME_TO_100NS(ftUser);
232 *idle = FILETTIME_TO_100NS(ftIdle);
233 *kernel = FILETTIME_TO_100NS(ftKernel) - *idle;
234 }
235 else
236 {
237 /* GetSystemTimes is not available, fall back to NtQuerySystemInformation */
238 if (!mpfnNtQuerySystemInformation)
239 return VERR_NOT_IMPLEMENTED;
240
241 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION sppi[MAXIMUM_PROCESSORS];
242 ULONG ulReturned;
243 NTSTATUS status = mpfnNtQuerySystemInformation(
244 SystemProcessorPerformanceInformation, &sppi, sizeof(sppi), &ulReturned);
245 if (NT_ERROR(status))
246 {
247 Log(("NtQuerySystemInformation() -> 0x%x\n", status));
248 return RTErrConvertFromNtStatus(status);
249 }
250 /* Sum up values accross all processors */
251 *user = *kernel = *idle = 0;
252 for (unsigned i = 0; i < ulReturned / sizeof(sppi[0]); ++i)
253 {
254 *idle += sppi[i].IdleTime.QuadPart;
255 *kernel += sppi[i].KernelTime.QuadPart - sppi[i].IdleTime.QuadPart;
256 *user += sppi[i].UserTime.QuadPart;
257 }
258 }
259
260 LogFlowThisFunc(("user=%lu kernel=%lu idle=%lu\n", *user, *kernel, *idle));
261 LogFlowThisFuncLeave();
262
263 return VINF_SUCCESS;
264}
265
266typedef struct _PROCESSOR_POWER_INFORMATION {
267 ULONG Number;
268 ULONG MaxMhz;
269 ULONG CurrentMhz;
270 ULONG MhzLimit;
271 ULONG MaxIdleState;
272 ULONG CurrentIdleState;
273} PROCESSOR_POWER_INFORMATION , *PPROCESSOR_POWER_INFORMATION;
274
275int CollectorWin::getHostCpuMHz(ULONG *mhz)
276{
277 uint64_t uTotalMhz = 0;
278 RTCPUID nProcessors = RTMpGetCount();
279 PPROCESSOR_POWER_INFORMATION ppi = (PPROCESSOR_POWER_INFORMATION)RTMemAllocZ(nProcessors * sizeof(PROCESSOR_POWER_INFORMATION));
280
281 if (!ppi)
282 return VERR_NO_MEMORY;
283
284 LONG ns = CallNtPowerInformation(ProcessorInformation, NULL, 0, ppi,
285 nProcessors * sizeof(PROCESSOR_POWER_INFORMATION));
286 if (ns)
287 {
288 Log(("CallNtPowerInformation() -> %x\n", ns));
289 RTMemFree(ppi);
290 return VERR_INTERNAL_ERROR;
291 }
292
293 /* Compute an average over all CPUs */
294 for (unsigned i = 0; i < nProcessors; i++)
295 uTotalMhz += ppi[i].CurrentMhz;
296 *mhz = (ULONG)(uTotalMhz / nProcessors);
297
298 RTMemFree(ppi);
299 LogFlowThisFunc(("mhz=%u\n", *mhz));
300 LogFlowThisFuncLeave();
301
302 return VINF_SUCCESS;
303}
304
305int CollectorWin::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available)
306{
307 MEMORYSTATUSEX mstat;
308
309 mstat.dwLength = sizeof(mstat);
310 if (GlobalMemoryStatusEx(&mstat))
311 {
312 *total = (ULONG)( mstat.ullTotalPhys / 1024 );
313 *available = (ULONG)( mstat.ullAvailPhys / 1024 );
314 *used = *total - *available;
315 }
316 else
317 return RTErrConvertFromWin32(GetLastError());
318
319 return VINF_SUCCESS;
320}
321
322int CollectorWin::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel)
323{
324 return VERR_NOT_IMPLEMENTED;
325}
326
327int CollectorWin::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
328{
329 VMProcessMap::const_iterator it = mProcessStats.find(process);
330
331 if (it == mProcessStats.end())
332 {
333 Log (("No stats pre-collected for process %x\n", process));
334 return VERR_INTERNAL_ERROR;
335 }
336 *user = it->second.cpuUser;
337 *kernel = it->second.cpuKernel;
338 *total = it->second.cpuTotal;
339 return VINF_SUCCESS;
340}
341
342int CollectorWin::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
343{
344 VMProcessMap::const_iterator it = mProcessStats.find(process);
345
346 if (it == mProcessStats.end())
347 {
348 Log (("No stats pre-collected for process %x\n", process));
349 return VERR_INTERNAL_ERROR;
350 }
351 *used = (ULONG)(it->second.ramUsed / 1024);
352 return VINF_SUCCESS;
353}
354
355}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette