VirtualBox

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

Last change on this file since 43978 was 43836, checked in by vboxsync, 12 years ago

Main/Metrics: removed unused functions (#6345)

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