VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/win/PerformanceWin.cpp@ 36964

Last change on this file since 36964 was 35368, checked in by vboxsync, 14 years ago

Main: source re-org.

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