VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/Performance.cpp@ 49644

Last change on this file since 49644 was 48955, checked in by vboxsync, 11 years ago

Main: Whitespace (including tabs!) and svn:keywords cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.4 KB
Line 
1/* $Id: Performance.cpp 48955 2013-10-07 21:59:25Z vboxsync $ */
2/** @file
3 * VBox Performance Classes implementation.
4 */
5
6/*
7 * Copyright (C) 2008-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*
19 * @todo list:
20 *
21 * 1) Detection of erroneous metric names
22 */
23
24#ifndef VBOX_COLLECTOR_TEST_CASE
25#include "VirtualBoxImpl.h"
26#include "MachineImpl.h"
27#include "MediumImpl.h"
28#include "AutoCaller.h"
29#endif
30#include "Performance.h"
31#include "HostNetworkInterfaceImpl.h"
32#include "netif.h"
33
34#include <VBox/com/array.h>
35#include <VBox/com/ptr.h>
36#include <VBox/com/string.h>
37#include <VBox/err.h>
38#include <iprt/string.h>
39#include <iprt/mem.h>
40#include <iprt/cpuset.h>
41
42#include <algorithm>
43
44#include "Logging.h"
45
46using namespace pm;
47
48// Stubs for non-pure virtual methods
49
50int CollectorHAL::getHostCpuLoad(ULONG * /* user */, ULONG * /* kernel */, ULONG * /* idle */)
51{
52 return VERR_NOT_IMPLEMENTED;
53}
54
55int CollectorHAL::getProcessCpuLoad(RTPROCESS /* process */, ULONG * /* user */, ULONG * /* kernel */)
56{
57 return VERR_NOT_IMPLEMENTED;
58}
59
60int CollectorHAL::getRawHostCpuLoad(uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* idle */)
61{
62 return VERR_NOT_IMPLEMENTED;
63}
64
65int CollectorHAL::getRawHostNetworkLoad(const char * /* name */, uint64_t * /* rx */, uint64_t * /* tx */)
66{
67 return VERR_NOT_IMPLEMENTED;
68}
69
70int CollectorHAL::getRawHostDiskLoad(const char * /* name */, uint64_t * /* disk_ms */, uint64_t * /* total_ms */)
71{
72 return VERR_NOT_IMPLEMENTED;
73}
74
75int CollectorHAL::getRawProcessCpuLoad(RTPROCESS /* process */, uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* total */)
76{
77 return VERR_NOT_IMPLEMENTED;
78}
79
80int CollectorHAL::getHostMemoryUsage(ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
81{
82 return VERR_NOT_IMPLEMENTED;
83}
84
85int CollectorHAL::getHostFilesystemUsage(const char * /* name */, ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
86{
87 return VERR_NOT_IMPLEMENTED;
88}
89
90int CollectorHAL::getHostDiskSize(const char * /* name */, uint64_t * /* size */)
91{
92 return VERR_NOT_IMPLEMENTED;
93}
94
95int CollectorHAL::getProcessMemoryUsage(RTPROCESS /* process */, ULONG * /* used */)
96{
97 return VERR_NOT_IMPLEMENTED;
98}
99
100int CollectorHAL::getDiskListByFs(const char * /* name */, DiskList& /* listUsage */, DiskList& /* listLoad */)
101{
102 return VERR_NOT_IMPLEMENTED;
103}
104
105/* Generic implementations */
106
107int CollectorHAL::getHostCpuMHz(ULONG *mhz)
108{
109 unsigned cCpus = 0;
110 uint64_t u64TotalMHz = 0;
111 RTCPUSET OnlineSet;
112 RTMpGetOnlineSet(&OnlineSet);
113 for (RTCPUID iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
114 {
115 LogAleksey(("{%p} " LOG_FN_FMT ": Checking if CPU %d is member of online set...\n",
116 this, __PRETTY_FUNCTION__, (int)iCpu));
117 if (RTCpuSetIsMemberByIndex(&OnlineSet, iCpu))
118 {
119 LogAleksey(("{%p} " LOG_FN_FMT ": Getting frequency for CPU %d...\n",
120 this, __PRETTY_FUNCTION__, (int)iCpu));
121 uint32_t uMHz = RTMpGetCurFrequency(RTMpCpuIdFromSetIndex(iCpu));
122 if (uMHz != 0)
123 {
124 LogAleksey(("{%p} " LOG_FN_FMT ": CPU %d %u MHz\n",
125 this, __PRETTY_FUNCTION__, (int)iCpu, uMHz));
126 u64TotalMHz += uMHz;
127 cCpus++;
128 }
129 }
130 }
131
132 AssertReturn(cCpus, VERR_NOT_IMPLEMENTED);
133 *mhz = (ULONG)(u64TotalMHz / cCpus);
134
135 return VINF_SUCCESS;
136}
137
138#ifndef VBOX_COLLECTOR_TEST_CASE
139
140CollectorGuestQueue::CollectorGuestQueue()
141{
142 mEvent = NIL_RTSEMEVENT;
143 RTSemEventCreate(&mEvent);
144}
145
146CollectorGuestQueue::~CollectorGuestQueue()
147{
148 RTSemEventDestroy(mEvent);
149}
150
151void CollectorGuestQueue::push(CollectorGuestRequest* rq)
152{
153 RTCLock lock(mLockMtx);
154
155 mQueue.push(rq);
156 RTSemEventSignal(mEvent);
157}
158
159CollectorGuestRequest* CollectorGuestQueue::pop()
160{
161 int rc = VINF_SUCCESS;
162 CollectorGuestRequest* rq = NULL;
163
164 do
165 {
166 {
167 RTCLock lock(mLockMtx);
168
169 if (!mQueue.empty())
170 {
171 rq = mQueue.front();
172 mQueue.pop();
173 }
174 }
175
176 if (rq)
177 return rq;
178 else
179 rc = RTSemEventWaitNoResume(mEvent, RT_INDEFINITE_WAIT);
180 }
181 while (RT_SUCCESS(rc));
182
183 return NULL;
184}
185
186HRESULT CGRQEnable::execute()
187{
188 Assert(mCGuest);
189 return mCGuest->enableInternal(mMask);
190}
191
192void CGRQEnable::debugPrint(void *aObject, const char *aFunction, const char *aText)
193{
194 NOREF(aObject);
195 NOREF(aFunction);
196 NOREF(aText);
197 LogAleksey(("{%p} " LOG_FN_FMT ": CGRQEnable(mask=0x%x) %s\n",
198 aObject, aFunction, mMask, aText));
199}
200
201HRESULT CGRQDisable::execute()
202{
203 Assert(mCGuest);
204 return mCGuest->disableInternal(mMask);
205}
206
207void CGRQDisable::debugPrint(void *aObject, const char *aFunction, const char *aText)
208{
209 NOREF(aObject);
210 NOREF(aFunction);
211 NOREF(aText);
212 LogAleksey(("{%p} " LOG_FN_FMT ": CGRQDisable(mask=0x%x) %s\n",
213 aObject, aFunction, mMask, aText));
214}
215
216HRESULT CGRQAbort::execute()
217{
218 return E_ABORT;
219}
220
221void CGRQAbort::debugPrint(void *aObject, const char *aFunction, const char *aText)
222{
223 NOREF(aObject);
224 NOREF(aFunction);
225 NOREF(aText);
226 LogAleksey(("{%p} " LOG_FN_FMT ": CGRQAbort %s\n",
227 aObject, aFunction, aText));
228}
229
230CollectorGuest::CollectorGuest(Machine *machine, RTPROCESS process) :
231 mUnregistered(false), mEnabled(false), mValid(false), mMachine(machine), mProcess(process),
232 mCpuUser(0), mCpuKernel(0), mCpuIdle(0),
233 mMemTotal(0), mMemFree(0), mMemBalloon(0), mMemShared(0), mMemCache(0), mPageTotal(0),
234 mAllocVMM(0), mFreeVMM(0), mBalloonedVMM(0), mSharedVMM(0), mVmNetRx(0), mVmNetTx(0)
235{
236 Assert(mMachine);
237 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
238 mMachine->AddRef();
239}
240
241CollectorGuest::~CollectorGuest()
242{
243 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
244 mMachine->Release();
245 // Assert(!cEnabled); why?
246}
247
248int CollectorGuest::enableVMMStats(bool mCollectVMMStats)
249{
250 HRESULT ret = S_OK;
251
252 if (mGuest)
253 {
254 /* @todo: replace this with a direct call to mGuest in trunk! */
255 AutoCaller autoCaller(mMachine);
256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
257
258 ComPtr<IInternalSessionControl> directControl;
259
260 ret = mMachine->getDirectControl(&directControl);
261 if (ret != S_OK)
262 return ret;
263
264 /* enable statistics collection; this is a remote call (!) */
265 ret = directControl->EnableVMMStatistics(mCollectVMMStats);
266 LogAleksey(("{%p} " LOG_FN_FMT ": %sable VMM stats (%s)\n",
267 this, __PRETTY_FUNCTION__, mCollectVMMStats?"En":"Dis",
268 SUCCEEDED(ret)?"success":"failed"));
269 }
270
271 return ret;
272}
273
274int CollectorGuest::enable(ULONG mask)
275{
276 return enqueueRequest(new CGRQEnable(mask));
277}
278
279int CollectorGuest::disable(ULONG mask)
280{
281 return enqueueRequest(new CGRQDisable(mask));
282}
283
284HRESULT CollectorGuest::enableInternal(ULONG mask)
285{
286 HRESULT ret = S_OK;
287
288 if ((mEnabled & mask) == mask)
289 return E_UNEXPECTED;
290
291 if (!mEnabled)
292 {
293 /* Must make sure that the machine object does not get uninitialized
294 * in the middle of enabling this collector. Causes timing-related
295 * behavior otherwise, which we don't want. In particular the
296 * GetRemoteConsole call below can hang if the VM didn't completely
297 * terminate (the VM processes stop processing events shortly before
298 * closing the session). This avoids the hang. */
299 AutoCaller autoCaller(mMachine);
300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
301
302 mMachineName = mMachine->getName();
303
304 ComPtr<IInternalSessionControl> directControl;
305
306 ret = mMachine->getDirectControl(&directControl);
307 if (ret != S_OK)
308 return ret;
309
310 /* get the associated console; this is a remote call (!) */
311 ret = directControl->GetRemoteConsole(mConsole.asOutParam());
312 if (ret != S_OK)
313 return ret;
314
315 ret = mConsole->COMGETTER(Guest)(mGuest.asOutParam());
316 if (ret == S_OK)
317 {
318 ret = mGuest->COMSETTER(StatisticsUpdateInterval)(1 /* 1 sec */);
319 LogAleksey(("{%p} " LOG_FN_FMT ": Set guest statistics update interval to 1 sec (%s)\n",
320 this, __PRETTY_FUNCTION__, SUCCEEDED(ret)?"success":"failed"));
321 }
322 }
323 if ((mask & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM)
324 enableVMMStats(true);
325 mEnabled |= mask;
326
327 return ret;
328}
329
330int CollectorGuest::disableInternal(ULONG mask)
331{
332 if (!(mEnabled & mask))
333 return E_UNEXPECTED;
334
335 if ((mask & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM)
336 enableVMMStats(false);
337 mEnabled &= ~mask;
338 if (!mEnabled)
339 {
340 Assert(mGuest && mConsole);
341 HRESULT ret = mGuest->COMSETTER(StatisticsUpdateInterval)(0 /* off */);
342 NOREF(ret);
343 LogAleksey(("{%p} " LOG_FN_FMT ": Set guest statistics update interval to 0 sec (%s)\n",
344 this, __PRETTY_FUNCTION__, SUCCEEDED(ret)?"success":"failed"));
345 invalidate(VMSTATS_ALL);
346 }
347
348 return S_OK;
349}
350
351int CollectorGuest::enqueueRequest(CollectorGuestRequest *aRequest)
352{
353 if (mManager)
354 {
355 aRequest->setGuest(this);
356 return mManager->enqueueRequest(aRequest);
357 }
358
359 LogAleksey(("{%p} " LOG_FN_FMT ": Attempted enqueue guest request when mManager is null\n",
360 this, __PRETTY_FUNCTION__));
361 return E_POINTER;
362}
363
364void CollectorGuest::updateStats(ULONG aValidStats, ULONG aCpuUser,
365 ULONG aCpuKernel, ULONG aCpuIdle,
366 ULONG aMemTotal, ULONG aMemFree,
367 ULONG aMemBalloon, ULONG aMemShared,
368 ULONG aMemCache, ULONG aPageTotal,
369 ULONG aAllocVMM, ULONG aFreeVMM,
370 ULONG aBalloonedVMM, ULONG aSharedVMM,
371 ULONG aVmNetRx, ULONG aVmNetTx)
372{
373 if ((aValidStats & VMSTATS_GUEST_CPULOAD) == VMSTATS_GUEST_CPULOAD)
374 {
375 mCpuUser = aCpuUser;
376 mCpuKernel = aCpuKernel,
377 mCpuIdle = aCpuIdle;
378 }
379 if ((aValidStats & VMSTATS_GUEST_RAMUSAGE) == VMSTATS_GUEST_RAMUSAGE)
380 {
381 mMemTotal = aMemTotal;
382 mMemFree = aMemFree;
383 mMemBalloon = aMemBalloon;
384 mMemShared = aMemShared;
385 mMemCache = aMemCache;
386 mPageTotal = aPageTotal;
387 }
388 if ((aValidStats & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM)
389 {
390 mAllocVMM = aAllocVMM;
391 mFreeVMM = aFreeVMM;
392 mBalloonedVMM = aBalloonedVMM;
393 mSharedVMM = aSharedVMM;
394 }
395 if ((aValidStats & VMSTATS_NET_RATE) == VMSTATS_NET_RATE)
396 {
397 mVmNetRx = aVmNetRx;
398 mVmNetTx = aVmNetTx;
399 }
400 mValid = aValidStats;
401}
402
403CollectorGuestManager::CollectorGuestManager()
404 : mVMMStatsProvider(NULL), mGuestBeingCalled(NULL)
405{
406 int rc = RTThreadCreate(&mThread, CollectorGuestManager::requestProcessingThread,
407 this, 0, RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE,
408 "CGMgr");
409 NOREF(rc);
410 LogAleksey(("{%p} " LOG_FN_FMT ": RTThreadCreate returned %u (mThread=%p)\n",
411 this, __PRETTY_FUNCTION__, rc));
412}
413
414CollectorGuestManager::~CollectorGuestManager()
415{
416 Assert(mGuests.size() == 0);
417 int rcThread = 0;
418 int rc = enqueueRequest(new CGRQAbort());
419 if (SUCCEEDED(rc))
420 {
421 /* We wait only if we were able to put the abort request to a queue */
422 LogAleksey(("{%p} " LOG_FN_FMT ": Waiting for CGM request processing thread to stop...\n",
423 this, __PRETTY_FUNCTION__));
424 rc = RTThreadWait(mThread, 1000 /* 1 sec */, &rcThread);
425 LogAleksey(("{%p} " LOG_FN_FMT ": RTThreadWait returned %u (thread exit code: %u)\n",
426 this, __PRETTY_FUNCTION__, rc, rcThread));
427 }
428}
429
430void CollectorGuestManager::registerGuest(CollectorGuest* pGuest)
431{
432 pGuest->setManager(this);
433 mGuests.push_back(pGuest);
434 /*
435 * If no VMM stats provider was elected previously than this is our
436 * candidate.
437 */
438 if (!mVMMStatsProvider)
439 mVMMStatsProvider = pGuest;
440 LogAleksey(("{%p} " LOG_FN_FMT ": Registered guest=%p provider=%p\n",
441 this, __PRETTY_FUNCTION__, pGuest, mVMMStatsProvider));
442}
443
444void CollectorGuestManager::unregisterGuest(CollectorGuest* pGuest)
445{
446 int rc = S_OK;
447
448 LogAleksey(("{%p} " LOG_FN_FMT ": About to unregister guest=%p provider=%p\n",
449 this, __PRETTY_FUNCTION__, pGuest, mVMMStatsProvider));
450 //mGuests.remove(pGuest); => destroyUnregistered()
451 pGuest->unregister();
452 if (pGuest == mVMMStatsProvider)
453 {
454 /* This was our VMM stats provider, it is time to re-elect */
455 CollectorGuestList::iterator it;
456 /* Assume that nobody can provide VMM stats */
457 mVMMStatsProvider = NULL;
458
459 for (it = mGuests.begin(); it != mGuests.end(); it++)
460 {
461 /* Skip unregistered as they are about to be destroyed */
462 if ((*it)->isUnregistered())
463 continue;
464
465 if ((*it)->isEnabled())
466 {
467 /* Found the guest already collecting stats, elect it */
468 mVMMStatsProvider = *it;
469 rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(VMSTATS_VMM_RAM));
470 if (FAILED(rc))
471 {
472 /* This is not a good candidate -- try to find another */
473 mVMMStatsProvider = NULL;
474 continue;
475 }
476 break;
477 }
478 }
479 if (!mVMMStatsProvider)
480 {
481 /* If nobody collects stats, take the first registered */
482 for (it = mGuests.begin(); it != mGuests.end(); it++)
483 {
484 /* Skip unregistered as they are about to be destroyed */
485 if ((*it)->isUnregistered())
486 continue;
487
488 mVMMStatsProvider = *it;
489 //mVMMStatsProvider->enable(VMSTATS_VMM_RAM);
490 rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(VMSTATS_VMM_RAM));
491 if (SUCCEEDED(rc))
492 break;
493 /* This was not a good candidate -- try to find another */
494 mVMMStatsProvider = NULL;
495 }
496 }
497 }
498 LogAleksey(("{%p} " LOG_FN_FMT ": LEAVE new provider=%p\n",
499 this, __PRETTY_FUNCTION__, mVMMStatsProvider));
500}
501
502void CollectorGuestManager::destroyUnregistered()
503{
504 CollectorGuestList::iterator it;
505
506 for (it = mGuests.begin(); it != mGuests.end();)
507 if ((*it)->isUnregistered())
508 {
509 delete *it;
510 it = mGuests.erase(it);
511 LogAleksey(("{%p} " LOG_FN_FMT ": Number of guests after erasing unregistered is %d\n",
512 this, __PRETTY_FUNCTION__, mGuests.size()));
513 }
514 else
515 ++it;
516}
517
518int CollectorGuestManager::enqueueRequest(CollectorGuestRequest *aRequest)
519{
520#ifdef DEBUG
521 aRequest->debugPrint(this, __PRETTY_FUNCTION__, "added to CGM queue");
522#endif /* DEBUG */
523 /*
524 * It is very unlikely that we will get high frequency calls to configure
525 * guest metrics collection, so we rely on this fact to detect blocked
526 * guests. If the guest has not finished processing the previous request
527 * after half a second we consider it blocked.
528 */
529 if (aRequest->getGuest() && aRequest->getGuest() == mGuestBeingCalled)
530 {
531 /*
532 * Before we can declare a guest blocked we need to wait for a while
533 * and then check again as it may never had a chance to process
534 * the previous request. Half a second is an eternity for processes
535 * and is barely noticable by humans.
536 */
537 LogAleksey(("{%p} " LOG_FN_FMT ": Suspecting %s is stalled. Waiting for .5 sec...\n",
538 this, __PRETTY_FUNCTION__,
539 aRequest->getGuest()->getVMName().c_str()));
540 RTThreadSleep(500 /* ms */);
541 if (aRequest->getGuest() == mGuestBeingCalled) {
542 LogAleksey(("{%p} " LOG_FN_FMT ": Request processing stalled for %s\n",
543 this, __PRETTY_FUNCTION__,
544 aRequest->getGuest()->getVMName().c_str()));
545 /* Request execution got stalled for this guest -- report an error */
546 return E_FAIL;
547 }
548 }
549 mQueue.push(aRequest);
550 return S_OK;
551}
552
553/* static */
554DECLCALLBACK(int) CollectorGuestManager::requestProcessingThread(RTTHREAD /* aThread */, void *pvUser)
555{
556 CollectorGuestRequest *pReq;
557 CollectorGuestManager *mgr = static_cast<CollectorGuestManager*>(pvUser);
558
559 HRESULT rc = S_OK;
560
561 LogAleksey(("{%p} " LOG_FN_FMT ": Starting request processing loop...\n",
562 mgr, __PRETTY_FUNCTION__));
563 while ((pReq = mgr->mQueue.pop()) != NULL)
564 {
565#ifdef DEBUG
566 pReq->debugPrint(mgr, __PRETTY_FUNCTION__, "is being executed...");
567#endif /* DEBUG */
568 mgr->mGuestBeingCalled = pReq->getGuest();
569 rc = pReq->execute();
570 mgr->mGuestBeingCalled = NULL;
571 delete pReq;
572 if (rc == E_ABORT)
573 break;
574 if (FAILED(rc))
575 LogAleksey(("{%p} " LOG_FN_FMT ": request::execute returned %u\n",
576 mgr, __PRETTY_FUNCTION__, rc));
577 }
578 LogAleksey(("{%p} " LOG_FN_FMT ": Exiting request processing loop... rc=%u\n",
579 mgr, __PRETTY_FUNCTION__, rc));
580
581 return VINF_SUCCESS;
582}
583
584
585#endif /* !VBOX_COLLECTOR_TEST_CASE */
586
587bool BaseMetric::collectorBeat(uint64_t nowAt)
588{
589 if (isEnabled())
590 {
591 if (mLastSampleTaken == 0)
592 {
593 mLastSampleTaken = nowAt;
594 Log4(("{%p} " LOG_FN_FMT ": Collecting %s for obj(%p)...\n",
595 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
596 return true;
597 }
598 /*
599 * We use low resolution timers which may fire just a little bit early.
600 * We compensate for that by jumping into the future by several
601 * milliseconds (see @bugref{6345}).
602 */
603 if (nowAt - mLastSampleTaken + PM_SAMPLER_PRECISION_MS >= mPeriod * 1000)
604 {
605 /*
606 * We don't want the beat to drift. This is why the timestamp of
607 * the last taken sample is not the actual time but the time we
608 * should have taken the measurement at.
609 */
610 mLastSampleTaken += mPeriod * 1000;
611 Log4(("{%p} " LOG_FN_FMT ": Collecting %s for obj(%p)...\n",
612 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
613 return true;
614 }
615 Log4(("{%p} " LOG_FN_FMT ": Enabled but too early to collect %s for obj(%p)\n",
616 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
617 }
618 return false;
619}
620
621void HostCpuLoad::init(ULONG period, ULONG length)
622{
623 mPeriod = period;
624 mLength = length;
625 mUser->init(mLength);
626 mKernel->init(mLength);
627 mIdle->init(mLength);
628}
629
630void HostCpuLoad::collect()
631{
632 ULONG user, kernel, idle;
633 int rc = mHAL->getHostCpuLoad(&user, &kernel, &idle);
634 if (RT_SUCCESS(rc))
635 {
636 mUser->put(user);
637 mKernel->put(kernel);
638 mIdle->put(idle);
639 }
640}
641
642void HostCpuLoadRaw::init(ULONG period, ULONG length)
643{
644 HostCpuLoad::init(period, length);
645 mHAL->getRawHostCpuLoad(&mUserPrev, &mKernelPrev, &mIdlePrev);
646}
647
648void HostCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
649{
650 hints.collectHostCpuLoad();
651}
652
653void HostCpuLoadRaw::collect()
654{
655 uint64_t user, kernel, idle;
656 uint64_t userDiff, kernelDiff, idleDiff, totalDiff;
657
658 int rc = mHAL->getRawHostCpuLoad(&user, &kernel, &idle);
659 if (RT_SUCCESS(rc))
660 {
661 userDiff = user - mUserPrev;
662 kernelDiff = kernel - mKernelPrev;
663 idleDiff = idle - mIdlePrev;
664 totalDiff = userDiff + kernelDiff + idleDiff;
665
666 if (totalDiff == 0)
667 {
668 /* This is only possible if none of counters has changed! */
669 LogFlowThisFunc(("Impossible! User, kernel and idle raw "
670 "counters has not changed since last sample.\n" ));
671 mUser->put(0);
672 mKernel->put(0);
673 mIdle->put(0);
674 }
675 else
676 {
677 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff));
678 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff));
679 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff));
680 }
681
682 mUserPrev = user;
683 mKernelPrev = kernel;
684 mIdlePrev = idle;
685 }
686}
687
688#ifndef VBOX_COLLECTOR_TEST_CASE
689static bool getLinkSpeed(const char *szShortName, uint32_t *pSpeed)
690{
691 NETIFSTATUS enmState = NETIF_S_UNKNOWN;
692 int rc = NetIfGetState(szShortName, &enmState);
693 if (RT_FAILURE(rc))
694 return false;
695 if (enmState != NETIF_S_UP)
696 *pSpeed = 0;
697 else
698 {
699 rc = NetIfGetLinkSpeed(szShortName, pSpeed);
700 if (RT_FAILURE(rc))
701 return false;
702 }
703 return true;
704}
705
706void HostNetworkSpeed::init(ULONG period, ULONG length)
707{
708 mPeriod = period;
709 mLength = length;
710 mLinkSpeed->init(length);
711 /*
712 * Retrieve the link speed now as it may be wrong if the metric was
713 * registered at boot (see @bugref{6613}).
714 */
715 getLinkSpeed(mShortName.c_str(), &mSpeed);
716}
717
718void HostNetworkLoadRaw::init(ULONG period, ULONG length)
719{
720 mPeriod = period;
721 mLength = length;
722 mRx->init(mLength);
723 mTx->init(mLength);
724 /*
725 * Retrieve the link speed now as it may be wrong if the metric was
726 * registered at boot (see @bugref{6613}).
727 */
728 uint32_t uSpeedMbit = 65535;
729 if (getLinkSpeed(mShortName.c_str(), &uSpeedMbit))
730 mSpeed = (uint64_t)uSpeedMbit * (1000000/8); /* Convert to bytes/sec */
731 /*int rc =*/ mHAL->getRawHostNetworkLoad(mShortName.c_str(), &mRxPrev, &mTxPrev);
732 //AssertRC(rc);
733}
734
735void HostNetworkLoadRaw::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
736{
737 if (RT_FAILURE(mRc))
738 {
739 ComPtr<IHostNetworkInterface> networkInterface;
740 ComPtr<IHost> host = getObject();
741 HRESULT hrc = host->FindHostNetworkInterfaceByName(com::Bstr(mInterfaceName).raw(), networkInterface.asOutParam());
742 if (SUCCEEDED(hrc))
743 {
744 LogRel(("Failed to collect network metrics for %s: %Rrc (%d).\n", mInterfaceName.c_str(), mRc, mRc));
745 mRc = VINF_SUCCESS;
746 }
747 }
748}
749
750void HostNetworkLoadRaw::collect()
751{
752 uint64_t rx = mRxPrev;
753 uint64_t tx = mTxPrev;
754
755 if (RT_UNLIKELY(mSpeed * getPeriod() == 0))
756 {
757 LogFlowThisFunc(("Check cable for %s! speed=%llu period=%d.\n", mShortName.c_str(), mSpeed, getPeriod()));
758 /* We do not collect host network metrics for unplugged interfaces! */
759 return;
760 }
761 mRc = mHAL->getRawHostNetworkLoad(mShortName.c_str(), &rx, &tx);
762 if (RT_SUCCESS(mRc))
763 {
764 uint64_t rxDiff = rx - mRxPrev;
765 uint64_t txDiff = tx - mTxPrev;
766
767 mRx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * rxDiff / (mSpeed * getPeriod())));
768 mTx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * txDiff / (mSpeed * getPeriod())));
769
770 mRxPrev = rx;
771 mTxPrev = tx;
772 }
773 else
774 LogFlowThisFunc(("Failed to collect data: %Rrc (%d)."
775 " Will update the list of interfaces...\n", mRc,mRc));
776}
777#endif /* !VBOX_COLLECTOR_TEST_CASE */
778
779void HostDiskLoadRaw::init(ULONG period, ULONG length)
780{
781 mPeriod = period;
782 mLength = length;
783 mUtil->init(mLength);
784 int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &mDiskPrev, &mTotalPrev);
785 AssertRC(rc);
786}
787
788void HostDiskLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
789{
790 hints.collectHostCpuLoad();
791}
792
793void HostDiskLoadRaw::collect()
794{
795 uint64_t disk, total;
796
797 int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &disk, &total);
798 if (RT_SUCCESS(rc))
799 {
800 uint64_t diskDiff = disk - mDiskPrev;
801 uint64_t totalDiff = total - mTotalPrev;
802
803 if (RT_UNLIKELY(totalDiff == 0))
804 {
805 Assert(totalDiff);
806 LogFlowThisFunc(("Improbable! Less than millisecond passed! Disk=%s\n", mDiskName.c_str()));
807 mUtil->put(0);
808 }
809 else if (diskDiff > totalDiff)
810 {
811 /*
812 * It is possible that the disk spent more time than CPU because
813 * CPU measurements are taken during the pre-collect phase. We try
814 * to compensate for than by adding the extra to the next round of
815 * measurements.
816 */
817 mUtil->put(PM_NETWORK_LOAD_MULTIPLIER);
818 Assert((diskDiff - totalDiff) < mPeriod * 1000);
819 if ((diskDiff - totalDiff) > mPeriod * 1000)
820 {
821 LogRel(("Disk utilization time exceeds CPU time by more"
822 " than the collection period (%llu ms)\n", diskDiff - totalDiff));
823 }
824 else
825 {
826 disk = mDiskPrev + totalDiff;
827 LogFlowThisFunc(("Moved %u milliseconds to the next period.\n", (unsigned)(diskDiff - totalDiff)));
828 }
829 }
830 else
831 {
832 mUtil->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * diskDiff / totalDiff));
833 }
834
835 mDiskPrev = disk;
836 mTotalPrev = total;
837 }
838 else
839 LogFlowThisFunc(("Failed to collect data: %Rrc (%d).\n", rc));
840}
841
842void HostCpuMhz::init(ULONG period, ULONG length)
843{
844 mPeriod = period;
845 mLength = length;
846 mMHz->init(mLength);
847}
848
849void HostCpuMhz::collect()
850{
851 ULONG mhz;
852 int rc = mHAL->getHostCpuMHz(&mhz);
853 if (RT_SUCCESS(rc))
854 mMHz->put(mhz);
855}
856
857void HostRamUsage::init(ULONG period, ULONG length)
858{
859 mPeriod = period;
860 mLength = length;
861 mTotal->init(mLength);
862 mUsed->init(mLength);
863 mAvailable->init(mLength);
864}
865
866void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
867{
868 hints.collectHostRamUsage();
869}
870
871void HostRamUsage::collect()
872{
873 ULONG total, used, available;
874 int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
875 if (RT_SUCCESS(rc))
876 {
877 mTotal->put(total);
878 mUsed->put(used);
879 mAvailable->put(available);
880 }
881}
882
883void HostFilesystemUsage::init(ULONG period, ULONG length)
884{
885 mPeriod = period;
886 mLength = length;
887 mTotal->init(mLength);
888 mUsed->init(mLength);
889 mAvailable->init(mLength);
890}
891
892void HostFilesystemUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
893{
894}
895
896void HostFilesystemUsage::collect()
897{
898 ULONG total, used, available;
899 int rc = mHAL->getHostFilesystemUsage(mFsName.c_str(), &total, &used, &available);
900 if (RT_SUCCESS(rc))
901 {
902 mTotal->put(total);
903 mUsed->put(used);
904 mAvailable->put(available);
905 }
906}
907
908void HostDiskUsage::init(ULONG period, ULONG length)
909{
910 mPeriod = period;
911 mLength = length;
912 mTotal->init(mLength);
913}
914
915void HostDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
916{
917}
918
919void HostDiskUsage::collect()
920{
921 uint64_t total;
922 int rc = mHAL->getHostDiskSize(mDiskName.c_str(), &total);
923 if (RT_SUCCESS(rc))
924 mTotal->put((ULONG)(total / _1M));
925}
926
927#ifndef VBOX_COLLECTOR_TEST_CASE
928void HostRamVmm::init(ULONG period, ULONG length)
929{
930 mPeriod = period;
931 mLength = length;
932 mAllocVMM->init(mLength);
933 mFreeVMM->init(mLength);
934 mBalloonVMM->init(mLength);
935 mSharedVMM->init(mLength);
936}
937
938int HostRamVmm::enable()
939{
940 int rc = S_OK;
941 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
942 if (provider)
943 rc = provider->enable(VMSTATS_VMM_RAM);
944 BaseMetric::enable();
945 return rc;
946}
947
948int HostRamVmm::disable()
949{
950 int rc = S_OK;
951 BaseMetric::disable();
952 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
953 if (provider)
954 rc = provider->disable(VMSTATS_VMM_RAM);
955 return rc;
956}
957
958void HostRamVmm::preCollect(CollectorHints& hints, uint64_t /* iTick */)
959{
960 hints.collectHostRamVmm();
961}
962
963void HostRamVmm::collect()
964{
965 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
966 if (provider)
967 {
968 LogAleksey(("{%p} " LOG_FN_FMT ": provider=%p enabled=%s valid=%s...\n",
969 this, __PRETTY_FUNCTION__, provider, provider->isEnabled()?"y":"n",
970 provider->isValid(VMSTATS_VMM_RAM)?"y":"n"));
971 if (provider->isValid(VMSTATS_VMM_RAM))
972 {
973 /* Provider is ready, get updated stats */
974 mAllocCurrent = provider->getAllocVMM();
975 mFreeCurrent = provider->getFreeVMM();
976 mBalloonedCurrent = provider->getBalloonedVMM();
977 mSharedCurrent = provider->getSharedVMM();
978 provider->invalidate(VMSTATS_VMM_RAM);
979 }
980 /*
981 * Note that if there are no new values from the provider we will use
982 * the ones most recently provided instead of zeros, which is probably
983 * a desirable behavior.
984 */
985 }
986 else
987 {
988 mAllocCurrent = 0;
989 mFreeCurrent = 0;
990 mBalloonedCurrent = 0;
991 mSharedCurrent = 0;
992 }
993 LogAleksey(("{%p} " LOG_FN_FMT ": mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
994 this, __PRETTY_FUNCTION__,
995 mAllocCurrent, mFreeCurrent, mBalloonedCurrent, mSharedCurrent));
996 mAllocVMM->put(mAllocCurrent);
997 mFreeVMM->put(mFreeCurrent);
998 mBalloonVMM->put(mBalloonedCurrent);
999 mSharedVMM->put(mSharedCurrent);
1000}
1001#endif /* !VBOX_COLLECTOR_TEST_CASE */
1002
1003
1004
1005void MachineCpuLoad::init(ULONG period, ULONG length)
1006{
1007 mPeriod = period;
1008 mLength = length;
1009 mUser->init(mLength);
1010 mKernel->init(mLength);
1011}
1012
1013void MachineCpuLoad::collect()
1014{
1015 ULONG user, kernel;
1016 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
1017 if (RT_SUCCESS(rc))
1018 {
1019 mUser->put(user);
1020 mKernel->put(kernel);
1021 }
1022}
1023
1024void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1025{
1026 hints.collectProcessCpuLoad(mProcess);
1027}
1028
1029void MachineCpuLoadRaw::collect()
1030{
1031 uint64_t processUser, processKernel, hostTotal;
1032
1033 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
1034 if (RT_SUCCESS(rc))
1035 {
1036 if (hostTotal == mHostTotalPrev)
1037 {
1038 /* Nearly impossible, but... */
1039 mUser->put(0);
1040 mKernel->put(0);
1041 }
1042 else
1043 {
1044 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
1045 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
1046 }
1047
1048 mHostTotalPrev = hostTotal;
1049 mProcessUserPrev = processUser;
1050 mProcessKernelPrev = processKernel;
1051 }
1052}
1053
1054void MachineRamUsage::init(ULONG period, ULONG length)
1055{
1056 mPeriod = period;
1057 mLength = length;
1058 mUsed->init(mLength);
1059}
1060
1061void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1062{
1063 hints.collectProcessRamUsage(mProcess);
1064}
1065
1066void MachineRamUsage::collect()
1067{
1068 ULONG used;
1069 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
1070 if (RT_SUCCESS(rc))
1071 mUsed->put(used);
1072}
1073
1074
1075#ifndef VBOX_COLLECTOR_TEST_CASE
1076void MachineDiskUsage::init(ULONG period, ULONG length)
1077{
1078 mPeriod = period;
1079 mLength = length;
1080 mUsed->init(mLength);
1081}
1082
1083void MachineDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
1084{
1085}
1086
1087void MachineDiskUsage::collect()
1088{
1089 ULONG used = 0;
1090
1091 for (MediaList::iterator it = mDisks.begin(); it != mDisks.end(); ++it)
1092 {
1093 ComObjPtr<Medium> pMedium = *it;
1094
1095 /* just in case */
1096 AssertStmt(!pMedium.isNull(), continue);
1097
1098 AutoCaller localAutoCaller(pMedium);
1099 if (FAILED(localAutoCaller.rc())) continue;
1100
1101 AutoReadLock local_alock(pMedium COMMA_LOCKVAL_SRC_POS);
1102
1103 used += (ULONG)(pMedium->getSize() / _1M);
1104 }
1105
1106 mUsed->put(used);
1107}
1108
1109void MachineNetRate::init(ULONG period, ULONG length)
1110{
1111 mPeriod = period;
1112 mLength = length;
1113
1114 mRx->init(mLength);
1115 mTx->init(mLength);
1116}
1117
1118void MachineNetRate::collect()
1119{
1120 if (mCGuest->isValid(VMSTATS_NET_RATE))
1121 {
1122 mRx->put(mCGuest->getVmNetRx());
1123 mTx->put(mCGuest->getVmNetTx());
1124 mCGuest->invalidate(VMSTATS_NET_RATE);
1125 }
1126}
1127
1128int MachineNetRate::enable()
1129{
1130 int rc = mCGuest->enable(VMSTATS_NET_RATE);
1131 BaseMetric::enable();
1132 return rc;
1133}
1134
1135int MachineNetRate::disable()
1136{
1137 BaseMetric::disable();
1138 return mCGuest->disable(VMSTATS_NET_RATE);
1139}
1140
1141void MachineNetRate::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1142{
1143 hints.collectGuestStats(mCGuest->getProcess());
1144}
1145
1146void GuestCpuLoad::init(ULONG period, ULONG length)
1147{
1148 mPeriod = period;
1149 mLength = length;
1150
1151 mUser->init(mLength);
1152 mKernel->init(mLength);
1153 mIdle->init(mLength);
1154}
1155
1156void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1157{
1158 hints.collectGuestStats(mCGuest->getProcess());
1159}
1160
1161void GuestCpuLoad::collect()
1162{
1163 if (mCGuest->isValid(VMSTATS_GUEST_CPULOAD))
1164 {
1165 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100);
1166 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100);
1167 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100);
1168 mCGuest->invalidate(VMSTATS_GUEST_CPULOAD);
1169 }
1170}
1171
1172int GuestCpuLoad::enable()
1173{
1174 int rc = mCGuest->enable(VMSTATS_GUEST_CPULOAD);
1175 BaseMetric::enable();
1176 return rc;
1177}
1178
1179int GuestCpuLoad::disable()
1180{
1181 BaseMetric::disable();
1182 return mCGuest->disable(VMSTATS_GUEST_CPULOAD);
1183}
1184
1185void GuestRamUsage::init(ULONG period, ULONG length)
1186{
1187 mPeriod = period;
1188 mLength = length;
1189
1190 mTotal->init(mLength);
1191 mFree->init(mLength);
1192 mBallooned->init(mLength);
1193 mShared->init(mLength);
1194 mCache->init(mLength);
1195 mPagedTotal->init(mLength);
1196}
1197
1198void GuestRamUsage::collect()
1199{
1200 if (mCGuest->isValid(VMSTATS_GUEST_RAMUSAGE))
1201 {
1202 mTotal->put(mCGuest->getMemTotal());
1203 mFree->put(mCGuest->getMemFree());
1204 mBallooned->put(mCGuest->getMemBalloon());
1205 mShared->put(mCGuest->getMemShared());
1206 mCache->put(mCGuest->getMemCache());
1207 mPagedTotal->put(mCGuest->getPageTotal());
1208 mCGuest->invalidate(VMSTATS_GUEST_RAMUSAGE);
1209 }
1210}
1211
1212int GuestRamUsage::enable()
1213{
1214 int rc = mCGuest->enable(VMSTATS_GUEST_RAMUSAGE);
1215 BaseMetric::enable();
1216 return rc;
1217}
1218
1219int GuestRamUsage::disable()
1220{
1221 BaseMetric::disable();
1222 return mCGuest->disable(VMSTATS_GUEST_RAMUSAGE);
1223}
1224
1225void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1226{
1227 hints.collectGuestStats(mCGuest->getProcess());
1228}
1229#endif /* !VBOX_COLLECTOR_TEST_CASE */
1230
1231void CircularBuffer::init(ULONG ulLength)
1232{
1233 if (mData)
1234 RTMemFree(mData);
1235 mLength = ulLength;
1236 if (mLength)
1237 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
1238 else
1239 mData = NULL;
1240 mWrapped = false;
1241 mEnd = 0;
1242 mSequenceNumber = 0;
1243}
1244
1245ULONG CircularBuffer::length()
1246{
1247 return mWrapped ? mLength : mEnd;
1248}
1249
1250void CircularBuffer::put(ULONG value)
1251{
1252 if (mData)
1253 {
1254 mData[mEnd++] = value;
1255 if (mEnd >= mLength)
1256 {
1257 mEnd = 0;
1258 mWrapped = true;
1259 }
1260 ++mSequenceNumber;
1261 }
1262}
1263
1264void CircularBuffer::copyTo(ULONG *data)
1265{
1266 if (mWrapped)
1267 {
1268 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
1269 // Copy the wrapped part
1270 if (mEnd)
1271 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
1272 }
1273 else
1274 memcpy(data, mData, mEnd * sizeof(ULONG));
1275}
1276
1277void SubMetric::query(ULONG *data)
1278{
1279 copyTo(data);
1280}
1281
1282void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
1283{
1284 ULONG length;
1285 ULONG *tmpData;
1286
1287 length = mSubMetric->length();
1288 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
1289 if (length)
1290 {
1291 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
1292 mSubMetric->query(tmpData);
1293 if (mAggregate)
1294 {
1295 *count = 1;
1296 *data = (ULONG*)RTMemAlloc(sizeof(**data));
1297 **data = mAggregate->compute(tmpData, length);
1298 RTMemFree(tmpData);
1299 }
1300 else
1301 {
1302 *count = length;
1303 *data = tmpData;
1304 }
1305 }
1306 else
1307 {
1308 *count = 0;
1309 *data = 0;
1310 }
1311}
1312
1313ULONG AggregateAvg::compute(ULONG *data, ULONG length)
1314{
1315 uint64_t tmp = 0;
1316 for (ULONG i = 0; i < length; ++i)
1317 tmp += data[i];
1318 return (ULONG)(tmp / length);
1319}
1320
1321const char * AggregateAvg::getName()
1322{
1323 return "avg";
1324}
1325
1326ULONG AggregateMin::compute(ULONG *data, ULONG length)
1327{
1328 ULONG tmp = *data;
1329 for (ULONG i = 0; i < length; ++i)
1330 if (data[i] < tmp)
1331 tmp = data[i];
1332 return tmp;
1333}
1334
1335const char * AggregateMin::getName()
1336{
1337 return "min";
1338}
1339
1340ULONG AggregateMax::compute(ULONG *data, ULONG length)
1341{
1342 ULONG tmp = *data;
1343 for (ULONG i = 0; i < length; ++i)
1344 if (data[i] > tmp)
1345 tmp = data[i];
1346 return tmp;
1347}
1348
1349const char * AggregateMax::getName()
1350{
1351 return "max";
1352}
1353
1354Filter::Filter(ComSafeArrayIn(IN_BSTR, metricNames),
1355 ComSafeArrayIn(IUnknown *, objects))
1356{
1357 /*
1358 * Let's work around null/empty safe array mess. I am not sure there is
1359 * a way to pass null arrays via webservice, I haven't found one. So I
1360 * guess the users will be forced to use empty arrays instead. Constructing
1361 * an empty SafeArray is a bit awkward, so what we do in this method is
1362 * actually convert null arrays to empty arrays and pass them down to
1363 * init() method. If someone knows how to do it better, please be my guest,
1364 * fix it.
1365 */
1366 if (ComSafeArrayInIsNull(metricNames))
1367 {
1368 com::SafeArray<BSTR> nameArray;
1369 if (ComSafeArrayInIsNull(objects))
1370 {
1371 com::SafeIfaceArray<IUnknown> objectArray;
1372 objectArray.reset(0);
1373 init(ComSafeArrayAsInParam(nameArray),
1374 ComSafeArrayAsInParam(objectArray));
1375 }
1376 else
1377 {
1378 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1379 init(ComSafeArrayAsInParam(nameArray),
1380 ComSafeArrayAsInParam(objectArray));
1381 }
1382 }
1383 else
1384 {
1385 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1386 if (ComSafeArrayInIsNull(objects))
1387 {
1388 com::SafeIfaceArray<IUnknown> objectArray;
1389 objectArray.reset(0);
1390 init(ComSafeArrayAsInParam(nameArray),
1391 ComSafeArrayAsInParam(objectArray));
1392 }
1393 else
1394 {
1395 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1396 init(ComSafeArrayAsInParam(nameArray),
1397 ComSafeArrayAsInParam(objectArray));
1398 }
1399 }
1400}
1401
1402Filter::Filter(const com::Utf8Str name, const ComPtr<IUnknown> &aObject)
1403{
1404 processMetricList(name, aObject);
1405}
1406
1407void Filter::init(ComSafeArrayIn(IN_BSTR, metricNames),
1408 ComSafeArrayIn(IUnknown *, objects))
1409{
1410 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1411 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1412
1413 if (!objectArray.size())
1414 {
1415 if (nameArray.size())
1416 {
1417 for (size_t i = 0; i < nameArray.size(); ++i)
1418 processMetricList(com::Utf8Str(nameArray[i]), ComPtr<IUnknown>());
1419 }
1420 else
1421 processMetricList("*", ComPtr<IUnknown>());
1422 }
1423 else
1424 {
1425 for (size_t i = 0; i < objectArray.size(); ++i)
1426 switch (nameArray.size())
1427 {
1428 case 0:
1429 processMetricList("*", objectArray[i]);
1430 break;
1431 case 1:
1432 processMetricList(com::Utf8Str(nameArray[0]), objectArray[i]);
1433 break;
1434 default:
1435 processMetricList(com::Utf8Str(nameArray[i]), objectArray[i]);
1436 break;
1437 }
1438 }
1439}
1440
1441void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
1442{
1443 size_t startPos = 0;
1444
1445 for (size_t pos = name.find(",");
1446 pos != com::Utf8Str::npos;
1447 pos = name.find(",", startPos))
1448 {
1449 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos, pos - startPos).c_str())));
1450 startPos = pos + 1;
1451 }
1452 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos).c_str())));
1453}
1454
1455/**
1456 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
1457 * modified to handle the special case of trailing colon in the pattern.
1458 *
1459 * @returns True if matches, false if not.
1460 * @param pszPat Pattern.
1461 * @param pszName Name to match against the pattern.
1462 * @param fSeenColon Seen colon (':').
1463 */
1464bool Filter::patternMatch(const char *pszPat, const char *pszName,
1465 bool fSeenColon)
1466{
1467 /* ASSUMES ASCII */
1468 for (;;)
1469 {
1470 char chPat = *pszPat;
1471 switch (chPat)
1472 {
1473 default:
1474 if (*pszName != chPat)
1475 return false;
1476 break;
1477
1478 case '*':
1479 {
1480 while ((chPat = *++pszPat) == '*' || chPat == '?')
1481 /* nothing */;
1482
1483 /* Handle a special case, the mask terminating with a colon. */
1484 if (chPat == ':')
1485 {
1486 if (!fSeenColon && !pszPat[1])
1487 return !strchr(pszName, ':');
1488 fSeenColon = true;
1489 }
1490
1491 for (;;)
1492 {
1493 char ch = *pszName++;
1494 if ( ch == chPat
1495 && ( !chPat
1496 || patternMatch(pszPat + 1, pszName, fSeenColon)))
1497 return true;
1498 if (!ch)
1499 return false;
1500 }
1501 /* won't ever get here */
1502 break;
1503 }
1504
1505 case '?':
1506 if (!*pszName)
1507 return false;
1508 break;
1509
1510 /* Handle a special case, the mask terminating with a colon. */
1511 case ':':
1512 if (!fSeenColon && !pszPat[1])
1513 return !*pszName;
1514 if (*pszName != ':')
1515 return false;
1516 fSeenColon = true;
1517 break;
1518
1519 case '\0':
1520 return !*pszName;
1521 }
1522 pszName++;
1523 pszPat++;
1524 }
1525 return true;
1526}
1527
1528bool Filter::match(const ComPtr<IUnknown> object, const RTCString &name) const
1529{
1530 ElementList::const_iterator it;
1531
1532 //LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
1533 for (it = mElements.begin(); it != mElements.end(); it++)
1534 {
1535 //LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
1536 if ((*it).first.isNull() || (*it).first == object)
1537 {
1538 // Objects match, compare names
1539 if (patternMatch((*it).second.c_str(), name.c_str()))
1540 {
1541 //LogFlowThisFunc(("...found!\n"));
1542 return true;
1543 }
1544 }
1545 }
1546 //LogAleksey(("...no matches!\n"));
1547 return false;
1548}
1549/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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