VirtualBox

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

Last change on this file since 12460 was 12460, checked in by vboxsync, 16 years ago

Proper exit paths in case of failure.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: PerformanceWin.cpp 12460 2008-09-15 12:15:17Z 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#include <Wbemidl.h>
25extern "C" {
26#include <powrprof.h>
27}
28#include <iprt/err.h>
29#include <iprt/mp.h>
30
31#include <map>
32
33#include "Logging.h"
34#include "Performance.h"
35
36namespace pm {
37
38class CollectorWin : public CollectorHAL
39{
40public:
41 CollectorWin();
42 virtual ~CollectorWin();
43
44 virtual int preCollect(const CollectorHints& hints);
45 virtual int getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle);
46 virtual int getHostCpuMHz(ULONG *mhz);
47 virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
48 virtual int getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel);
49 virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used);
50
51 virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
52 virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
53private:
54 long getPropertyHandle(IWbemObjectAccess *objAccess, LPCWSTR name);
55 int getObjects(IWbemHiPerfEnum *mEnum, IWbemObjectAccess ***objArray, DWORD *numReturned);
56
57 IWbemRefresher *mRefresher;
58 IWbemServices *mNameSpace;
59
60 IWbemHiPerfEnum *mEnumProcessor;
61 long mEnumProcessorID;
62 long mHostCpuLoadNameHandle;
63 long mHostCpuLoadUserHandle;
64 long mHostCpuLoadKernelHandle;
65 long mHostCpuLoadIdleHandle;
66
67 IWbemHiPerfEnum *mEnumProcess;
68 long mEnumProcessID;
69 long mProcessPIDHandle;
70 long mProcessCpuLoadUserHandle;
71 long mProcessCpuLoadKernelHandle;
72 long mProcessCpuLoadTimestampHandle;
73 long mProcessMemoryUsedHandle;
74
75 struct VMProcessStats
76 {
77 uint64_t cpuUser;
78 uint64_t cpuKernel;
79 uint64_t cpuTotal;
80 uint64_t ramUsed;
81 };
82
83 typedef std::map<RTPROCESS, VMProcessStats> VMProcessMap;
84
85 VMProcessMap mProcessStats;
86};
87
88CollectorHAL *createHAL()
89{
90 return new CollectorWin();
91}
92
93CollectorWin::CollectorWin() : mRefresher(0), mNameSpace(0), mEnumProcessor(0), mEnumProcess(0)
94{
95 HRESULT hr = S_OK;
96 IWbemConfigureRefresher *pConfig = NULL;
97 IWbemLocator *pWbemLocator = NULL;
98 BSTR bstrNameSpace = NULL;
99
100 if (SUCCEEDED (hr = CoCreateInstance(
101 CLSID_WbemLocator,
102 NULL,
103 CLSCTX_INPROC_SERVER,
104 IID_IWbemLocator,
105 (void**) &pWbemLocator)))
106 {
107 // Connect to the desired namespace.
108 bstrNameSpace = SysAllocString(L"\\\\.\\root\\cimv2");
109 if (bstrNameSpace)
110 {
111 hr = pWbemLocator->ConnectServer(
112 bstrNameSpace,
113 NULL, // User name
114 NULL, // Password
115 NULL, // Locale
116 0L, // Security flags
117 NULL, // Authority
118 NULL, // Wbem context
119 &mNameSpace);
120 }
121 pWbemLocator->Release();
122 SysFreeString(bstrNameSpace);
123 }
124
125 if (FAILED (hr)) {
126 Log (("Failed to get namespace. HR = %x\n", hr));
127 return;
128 }
129
130 if (SUCCEEDED (hr = CoCreateInstance(
131 CLSID_WbemRefresher,
132 NULL,
133 CLSCTX_INPROC_SERVER,
134 IID_IWbemRefresher,
135 (void**) &mRefresher)))
136 {
137 if (SUCCEEDED (hr = mRefresher->QueryInterface(
138 IID_IWbemConfigureRefresher,
139 (void **)&pConfig)))
140 {
141 // Add an enumerator to the refresher.
142 if (SUCCEEDED (hr = pConfig->AddEnum(
143 mNameSpace,
144 L"Win32_PerfRawData_PerfOS_Processor",
145 0,
146 NULL,
147 &mEnumProcessor,
148 &mEnumProcessorID)))
149 {
150 hr = pConfig->AddEnum(
151 mNameSpace,
152 L"Win32_PerfRawData_PerfProc_Process",
153 0,
154 NULL,
155 &mEnumProcess,
156 &mEnumProcessID);
157 }
158 pConfig->Release();
159 }
160 }
161
162
163 if (FAILED (hr)) {
164 Log (("Failed to add enumerators. HR = %x\n", hr));
165 return;
166 }
167
168 // Retrieve property handles (Returned handles work across all instances of a class!)
169
170 if (FAILED (hr = mRefresher->Refresh(0L)))
171 {
172 Log (("Refresher failed. HR = %x\n", hr));
173 return;
174 }
175
176 IWbemObjectAccess **apEnumAccess = NULL;
177 DWORD dwNumReturned = 0;
178
179 if (RT_FAILURE(getObjects(mEnumProcessor, &apEnumAccess, &dwNumReturned)))
180 return;
181
182 mHostCpuLoadNameHandle = getPropertyHandle(apEnumAccess[0], L"Name");
183 mHostCpuLoadUserHandle = getPropertyHandle(apEnumAccess[0], L"PercentUserTime");
184 mHostCpuLoadKernelHandle = getPropertyHandle(apEnumAccess[0], L"PercentPrivilegedTime");
185 mHostCpuLoadIdleHandle = getPropertyHandle(apEnumAccess[0], L"PercentProcessorTime");
186
187 for (unsigned i=0;i<dwNumReturned;i++)
188 apEnumAccess[i]->Release();
189
190 delete [] apEnumAccess;
191
192 if (RT_FAILURE(getObjects(mEnumProcess, &apEnumAccess, &dwNumReturned)))
193 return;
194
195 mProcessPIDHandle = getPropertyHandle(apEnumAccess[0], L"IDProcess");
196 mProcessCpuLoadUserHandle = getPropertyHandle(apEnumAccess[0], L"PercentUserTime");
197 mProcessCpuLoadKernelHandle = getPropertyHandle(apEnumAccess[0], L"PercentPrivilegedTime");
198 mProcessCpuLoadTimestampHandle = getPropertyHandle(apEnumAccess[0], L"Timestamp_Sys100NS");
199 mProcessMemoryUsedHandle = getPropertyHandle(apEnumAccess[0], L"WorkingSet");
200
201 for (unsigned i=0;i<dwNumReturned;i++)
202 apEnumAccess[i]->Release();
203
204 delete [] apEnumAccess;
205}
206
207CollectorWin::~CollectorWin()
208{
209 IWbemConfigureRefresher *pConfig = NULL;
210 HRESULT hr = S_OK;
211
212 if (NULL != mNameSpace)
213 {
214 mNameSpace->Release();
215 }
216 if (NULL != mRefresher)
217 {
218 if (SUCCEEDED (hr = mRefresher->QueryInterface(
219 IID_IWbemConfigureRefresher,
220 (void **)&pConfig)))
221 {
222 // Remove the enumerators from the refresher.
223 hr = pConfig->Remove(mEnumProcessorID, 0);
224 Assert(SUCCEEDED(hr));
225 hr = pConfig->Remove(mEnumProcessID, 0);
226 Assert(SUCCEEDED(hr));
227
228 pConfig->Release();
229 }
230 }
231 if (NULL != mEnumProcessor)
232 {
233 mEnumProcessor->Release();
234 }
235 if (NULL != mEnumProcess)
236 {
237 mEnumProcess->Release();
238 }
239 if (NULL != mRefresher)
240 {
241 mRefresher->Release();
242 }
243}
244
245long CollectorWin::getPropertyHandle(IWbemObjectAccess *objAccess, LPCWSTR name)
246{
247 HRESULT hr;
248 CIMTYPE tmp;
249 long handle;
250
251 if (FAILED (hr = objAccess->GetPropertyHandle(
252 name,
253 &tmp,
254 &handle)))
255 {
256 Log (("Failed to get property handle for '%ls'. HR = %x\n", name, hr));
257 return 0; /// @todo use throw
258 }
259
260 return handle;
261}
262
263int CollectorWin::getObjects(IWbemHiPerfEnum *mEnum, IWbemObjectAccess ***objArray, DWORD *numReturned)
264{
265 /*
266 * Get the number of objects.
267 * Note that the caller ASSUMES that at least one object is returned, so fail if there are none.
268 */
269 *objArray = NULL;
270 *numReturned = 0;
271 HRESULT hr = mEnum->GetObjects(0L, 0, *objArray, numReturned);
272 if (hr != WBEM_E_BUFFER_TOO_SMALL)
273 {
274 Log (("Failed to get the object count from the enumerator. HR = %x *numReturned=%d\n", hr, *numReturned));
275 return VERR_INTERNAL_ERROR;
276 }
277
278 /*
279 * Allocate an array with the right lenght and get the actual objects.
280 */
281 DWORD cObjects = *numReturned;
282 *objArray = new IWbemObjectAccess*[cObjects];
283 if (!*objArray)
284 {
285 Log (("Could not allocate enumerator access objects\n"));
286 return VERR_NO_MEMORY;
287 }
288 SecureZeroMemory(*objArray, cObjects * sizeof(IWbemObjectAccess*));
289 hr = mEnum->GetObjects(0L, cObjects, *objArray, numReturned);
290 if (FAILED(hr) || *numReturned == 0)
291 {
292 delete [] *objArray;
293 *objArray = NULL;
294 Log (("Failed to get the objects from the enumerator. HR = %x *numReturned=%d cObjects=%d\n", hr, *numReturned, cObjects));
295 return VERR_INTERNAL_ERROR;
296 }
297
298 return VINF_SUCCESS;
299}
300
301int CollectorWin::preCollect(const CollectorHints& hints)
302{
303
304 std::vector<RTPROCESS> processes;
305 hints.getProcesses(processes);
306
307 HRESULT hr;
308 IWbemObjectAccess **apEnumAccess = NULL;
309 DWORD dwNumReturned = 0;
310
311 LogFlowThisFuncEnter();
312
313 if (FAILED (hr = mRefresher->Refresh(0L)))
314 {
315 Log (("Refresher failed. HR = %x\n", hr));
316 return VERR_INTERNAL_ERROR;
317 }
318
319 int rc = getObjects(mEnumProcess, &apEnumAccess, &dwNumReturned);
320 if (RT_FAILURE(rc))
321 return rc;
322
323 rc = VERR_NOT_FOUND;
324
325 for (unsigned i = 0; i < dwNumReturned; i++)
326 {
327 DWORD dwIDProcess;
328
329 if (FAILED (hr = apEnumAccess[i]->ReadDWORD(
330 mProcessPIDHandle,
331 &dwIDProcess)))
332 {
333 Log (("Failed to read 'IDProcess' property. HR = %x\n", hr));
334 rc = VERR_INTERNAL_ERROR;
335 break;
336 }
337 LogFlowThisFunc (("Matching process %x against the list of machines...\n", dwIDProcess));
338 if (std::find(processes.begin(), processes.end(), dwIDProcess) != processes.end())
339 {
340 VMProcessStats vmStats;
341
342 LogFlowThisFunc (("Match found.\n"));
343 if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
344 mProcessCpuLoadUserHandle,
345 &vmStats.cpuUser)))
346 {
347 Log (("Failed to read 'PercentUserTime' property. HR = %x\n", hr));
348 rc = VERR_INTERNAL_ERROR;
349 break;
350 }
351 if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
352 mProcessCpuLoadKernelHandle,
353 &vmStats.cpuKernel)))
354 {
355 Log (("Failed to read 'PercentPrivilegedTime' property. HR = %x\n", hr));
356 rc = VERR_INTERNAL_ERROR;
357 break;
358 }
359 if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
360 mProcessCpuLoadTimestampHandle,
361 &vmStats.cpuTotal)))
362 {
363 Log (("Failed to read 'Timestamp_Sys100NS' property. HR = %x\n", hr));
364 rc = VERR_INTERNAL_ERROR;
365 break;
366 }
367 if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
368 mProcessMemoryUsedHandle,
369 &vmStats.ramUsed)))
370 {
371 Log (("Failed to read 'WorkingSet' property. HR = %x\n", hr));
372 rc = VERR_INTERNAL_ERROR;
373 break;
374 }
375
376 mProcessStats[dwIDProcess] = vmStats;
377 LogFlowThisFunc(("process=%x user=%lu kernel=%lu total=%lu\n", dwIDProcess, vmStats.cpuUser, vmStats.cpuKernel, vmStats.cpuTotal));
378 rc = VINF_SUCCESS;
379 }
380 }
381
382 for (unsigned i = 0; i < dwNumReturned; i++)
383 {
384 apEnumAccess[i]->Release();
385 apEnumAccess[i] = NULL;
386 }
387 delete [] apEnumAccess;
388
389 LogFlowThisFuncLeave();
390
391 return rc;
392}
393
394int CollectorWin::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle)
395{
396 return VERR_NOT_IMPLEMENTED;
397}
398
399int CollectorWin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
400{
401 HRESULT hr;
402 IWbemObjectAccess **apEnumAccess = NULL;
403 DWORD dwNumReturned = 0;
404
405 LogFlowThisFuncEnter();
406
407 int rc = getObjects(mEnumProcessor, &apEnumAccess, &dwNumReturned);
408 if (RT_FAILURE(rc))
409 return rc;
410
411 for (unsigned i = 0; i < dwNumReturned; i++)
412 {
413 long bytesRead = 0;
414 WCHAR tmpBuf[200];
415
416 if (FAILED (hr = apEnumAccess[i]->ReadPropertyValue(
417 mHostCpuLoadNameHandle,
418 sizeof(tmpBuf),
419 &bytesRead,
420 (byte*)tmpBuf)))
421 {
422 Log (("Failed to read 'Name' property. HR = %x\n", hr));
423 rc = VERR_INTERNAL_ERROR;
424 break;
425 }
426 if (wcscmp(tmpBuf, L"_Total") == 0)
427 {
428 if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
429 mHostCpuLoadUserHandle,
430 user)))
431 {
432 Log (("Failed to read 'PercentUserTime' property. HR = %x\n", hr));
433 rc = VERR_INTERNAL_ERROR;
434 break;
435 }
436 if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
437 mHostCpuLoadKernelHandle,
438 kernel)))
439 {
440 Log (("Failed to read 'PercentPrivilegedTime' property. HR = %x\n", hr));
441 rc = VERR_INTERNAL_ERROR;
442 break;
443 }
444 if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
445 mHostCpuLoadIdleHandle,
446 idle)))
447 {
448 Log (("Failed to read 'PercentProcessorTime' property. HR = %x\n", hr));
449 rc = VERR_INTERNAL_ERROR;
450 break;
451 }
452 rc = VINF_SUCCESS;
453 }
454 }
455 for (unsigned i = 0; i < dwNumReturned; i++)
456 {
457 apEnumAccess[i]->Release();
458 apEnumAccess[i] = NULL;
459 }
460 delete [] apEnumAccess;
461
462 LogFlowThisFunc(("user=%lu kernel=%lu idle=%lu\n", *user, *kernel, *idle));
463 LogFlowThisFuncLeave();
464
465 return rc;
466}
467
468typedef struct _PROCESSOR_POWER_INFORMATION {
469 ULONG Number;
470 ULONG MaxMhz;
471 ULONG CurrentMhz;
472 ULONG MhzLimit;
473 ULONG MaxIdleState;
474 ULONG CurrentIdleState;
475} PROCESSOR_POWER_INFORMATION , *PPROCESSOR_POWER_INFORMATION;
476
477int CollectorWin::getHostCpuMHz(ULONG *mhz)
478{
479 uint64_t uTotalMhz = 0;
480 RTCPUID nProcessors = RTMpGetCount();
481 PPROCESSOR_POWER_INFORMATION ppi = new PROCESSOR_POWER_INFORMATION[nProcessors];
482 LONG ns = CallNtPowerInformation(ProcessorInformation, NULL, 0, ppi,
483 nProcessors * sizeof(PROCESSOR_POWER_INFORMATION));
484 if (ns)
485 {
486 Log(("CallNtPowerInformation() -> %x\n", ns));
487 return VERR_INTERNAL_ERROR;
488 }
489
490 /* Compute an average over all CPUs */
491 for (unsigned i = 0; i < nProcessors; i++)
492 uTotalMhz += ppi[i].CurrentMhz;
493 *mhz = (ULONG)(uTotalMhz / nProcessors);
494
495 LogFlowThisFunc(("mhz=%u\n", *mhz));
496 LogFlowThisFuncLeave();
497
498 return VINF_SUCCESS;
499}
500
501int CollectorWin::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available)
502{
503 MEMORYSTATUSEX mstat;
504
505 mstat.dwLength = sizeof(mstat);
506 if (GlobalMemoryStatusEx(&mstat))
507 {
508 *total = (ULONG)( mstat.ullTotalPhys / 1000 );
509 *available = (ULONG)( mstat.ullAvailPhys / 1000 );
510 *used = *total - *available;
511 }
512 else
513 return RTErrConvertFromWin32(GetLastError());
514
515 return VINF_SUCCESS;
516}
517
518int CollectorWin::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel)
519{
520 return VERR_NOT_IMPLEMENTED;
521}
522
523int CollectorWin::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
524{
525 VMProcessMap::const_iterator it = mProcessStats.find(process);
526
527 if (it == mProcessStats.end())
528 {
529 Log (("No stats pre-collected for process %x\n", process));
530 return VERR_INTERNAL_ERROR;
531 }
532 *user = it->second.cpuUser;
533 *kernel = it->second.cpuKernel;
534 *total = it->second.cpuTotal;
535 return VINF_SUCCESS;
536}
537
538int CollectorWin::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
539{
540 VMProcessMap::const_iterator it = mProcessStats.find(process);
541
542 if (it == mProcessStats.end())
543 {
544 Log (("No stats pre-collected for process %x\n", process));
545 return VERR_INTERNAL_ERROR;
546 }
547 *used = (ULONG)(it->second.ramUsed / 1024);
548 return VINF_SUCCESS;
549}
550
551}
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