VirtualBox

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

Last change on this file since 45761 was 45051, checked in by vboxsync, 12 years ago

Main/Metrics: handle less common cases for FS to disk resolution (#6345)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.3 KB
Line 
1/* $Id: Performance.cpp 45051 2013-03-15 15:34:54Z 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 E_NOTIMPL;
53}
54
55int CollectorHAL::getProcessCpuLoad(RTPROCESS /* process */, ULONG * /* user */, ULONG * /* kernel */)
56{
57 return E_NOTIMPL;
58}
59
60int CollectorHAL::getRawHostCpuLoad(uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* idle */)
61{
62 return E_NOTIMPL;
63}
64
65int CollectorHAL::getRawHostNetworkLoad(const char * /* name */, uint64_t * /* rx */, uint64_t * /* tx */)
66{
67 return E_NOTIMPL;
68}
69
70int CollectorHAL::getRawHostDiskLoad(const char * /* name */, uint64_t * /* disk_ms */, uint64_t * /* total_ms */)
71{
72 return E_NOTIMPL;
73}
74
75int CollectorHAL::getRawProcessCpuLoad(RTPROCESS /* process */, uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* total */)
76{
77 return E_NOTIMPL;
78}
79
80int CollectorHAL::getHostMemoryUsage(ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
81{
82 return E_NOTIMPL;
83}
84
85int CollectorHAL::getHostFilesystemUsage(const char * /* name */, ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
86{
87 return E_NOTIMPL;
88}
89
90int CollectorHAL::getHostDiskSize(const char * /* name */, uint64_t * /* size */)
91{
92 return E_NOTIMPL;
93}
94
95int CollectorHAL::getProcessMemoryUsage(RTPROCESS /* process */, ULONG * /* used */)
96{
97 return E_NOTIMPL;
98}
99
100int CollectorHAL::getDiskListByFs(const char * /* name */, DiskList& /* listUsage */, DiskList& /* listLoad */)
101{
102 return E_NOTIMPL;
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
186int 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
201int 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
216int 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
284int 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}
883
884void HostFilesystemUsage::init(ULONG period, ULONG length)
885{
886 mPeriod = period;
887 mLength = length;
888 mTotal->init(mLength);
889 mUsed->init(mLength);
890 mAvailable->init(mLength);
891}
892
893void HostFilesystemUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
894{
895}
896
897void HostFilesystemUsage::collect()
898{
899 ULONG total, used, available;
900 int rc = mHAL->getHostFilesystemUsage(mFsName.c_str(), &total, &used, &available);
901 if (RT_SUCCESS(rc))
902 {
903 mTotal->put(total);
904 mUsed->put(used);
905 mAvailable->put(available);
906
907 }
908}
909
910void HostDiskUsage::init(ULONG period, ULONG length)
911{
912 mPeriod = period;
913 mLength = length;
914 mTotal->init(mLength);
915}
916
917void HostDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
918{
919}
920
921void HostDiskUsage::collect()
922{
923 uint64_t total;
924 int rc = mHAL->getHostDiskSize(mDiskName.c_str(), &total);
925 if (RT_SUCCESS(rc))
926 mTotal->put((ULONG)(total / (1024*1024)));
927}
928
929#ifndef VBOX_COLLECTOR_TEST_CASE
930void HostRamVmm::init(ULONG period, ULONG length)
931{
932 mPeriod = period;
933 mLength = length;
934 mAllocVMM->init(mLength);
935 mFreeVMM->init(mLength);
936 mBalloonVMM->init(mLength);
937 mSharedVMM->init(mLength);
938}
939
940int HostRamVmm::enable()
941{
942 int rc = S_OK;
943 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
944 if (provider)
945 rc = provider->enable(VMSTATS_VMM_RAM);
946 BaseMetric::enable();
947 return rc;
948}
949
950int HostRamVmm::disable()
951{
952 int rc = S_OK;
953 BaseMetric::disable();
954 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
955 if (provider)
956 rc = provider->disable(VMSTATS_VMM_RAM);
957 return rc;
958}
959
960void HostRamVmm::preCollect(CollectorHints& hints, uint64_t /* iTick */)
961{
962 hints.collectHostRamVmm();
963}
964
965void HostRamVmm::collect()
966{
967 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
968 if (provider)
969 {
970 LogAleksey(("{%p} " LOG_FN_FMT ": provider=%p enabled=%s valid=%s...\n",
971 this, __PRETTY_FUNCTION__, provider, provider->isEnabled()?"y":"n",
972 provider->isValid(VMSTATS_VMM_RAM)?"y":"n"));
973 if (provider->isValid(VMSTATS_VMM_RAM))
974 {
975 /* Provider is ready, get updated stats */
976 mAllocCurrent = provider->getAllocVMM();
977 mFreeCurrent = provider->getFreeVMM();
978 mBalloonedCurrent = provider->getBalloonedVMM();
979 mSharedCurrent = provider->getSharedVMM();
980 provider->invalidate(VMSTATS_VMM_RAM);
981 }
982 /*
983 * Note that if there are no new values from the provider we will use
984 * the ones most recently provided instead of zeros, which is probably
985 * a desirable behavior.
986 */
987 }
988 else
989 {
990 mAllocCurrent = 0;
991 mFreeCurrent = 0;
992 mBalloonedCurrent = 0;
993 mSharedCurrent = 0;
994 }
995 LogAleksey(("{%p} " LOG_FN_FMT ": mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
996 this, __PRETTY_FUNCTION__,
997 mAllocCurrent, mFreeCurrent, mBalloonedCurrent, mSharedCurrent));
998 mAllocVMM->put(mAllocCurrent);
999 mFreeVMM->put(mFreeCurrent);
1000 mBalloonVMM->put(mBalloonedCurrent);
1001 mSharedVMM->put(mSharedCurrent);
1002}
1003#endif /* !VBOX_COLLECTOR_TEST_CASE */
1004
1005
1006
1007void MachineCpuLoad::init(ULONG period, ULONG length)
1008{
1009 mPeriod = period;
1010 mLength = length;
1011 mUser->init(mLength);
1012 mKernel->init(mLength);
1013}
1014
1015void MachineCpuLoad::collect()
1016{
1017 ULONG user, kernel;
1018 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
1019 if (RT_SUCCESS(rc))
1020 {
1021 mUser->put(user);
1022 mKernel->put(kernel);
1023 }
1024}
1025
1026void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1027{
1028 hints.collectProcessCpuLoad(mProcess);
1029}
1030
1031void MachineCpuLoadRaw::collect()
1032{
1033 uint64_t processUser, processKernel, hostTotal;
1034
1035 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
1036 if (RT_SUCCESS(rc))
1037 {
1038 if (hostTotal == mHostTotalPrev)
1039 {
1040 /* Nearly impossible, but... */
1041 mUser->put(0);
1042 mKernel->put(0);
1043 }
1044 else
1045 {
1046 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
1047 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
1048 }
1049
1050 mHostTotalPrev = hostTotal;
1051 mProcessUserPrev = processUser;
1052 mProcessKernelPrev = processKernel;
1053 }
1054}
1055
1056void MachineRamUsage::init(ULONG period, ULONG length)
1057{
1058 mPeriod = period;
1059 mLength = length;
1060 mUsed->init(mLength);
1061}
1062
1063void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1064{
1065 hints.collectProcessRamUsage(mProcess);
1066}
1067
1068void MachineRamUsage::collect()
1069{
1070 ULONG used;
1071 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
1072 if (RT_SUCCESS(rc))
1073 mUsed->put(used);
1074}
1075
1076
1077#ifndef VBOX_COLLECTOR_TEST_CASE
1078void MachineDiskUsage::init(ULONG period, ULONG length)
1079{
1080 mPeriod = period;
1081 mLength = length;
1082 mUsed->init(mLength);
1083}
1084
1085void MachineDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
1086{
1087}
1088
1089void MachineDiskUsage::collect()
1090{
1091 ULONG used = 0;
1092
1093 for (MediaList::iterator it = mDisks.begin(); it != mDisks.end(); ++it)
1094 {
1095 ComObjPtr<Medium> pMedium = *it;
1096
1097 /* just in case */
1098 AssertStmt(!pMedium.isNull(), continue);
1099
1100 AutoCaller localAutoCaller(pMedium);
1101 if (FAILED(localAutoCaller.rc())) continue;
1102
1103 AutoReadLock local_alock(pMedium COMMA_LOCKVAL_SRC_POS);
1104
1105 used += pMedium->getSize() / (1024 * 1024);
1106 }
1107
1108 mUsed->put(used);
1109}
1110
1111void MachineNetRate::init(ULONG period, ULONG length)
1112{
1113 mPeriod = period;
1114 mLength = length;
1115
1116 mRx->init(mLength);
1117 mTx->init(mLength);
1118}
1119
1120void MachineNetRate::collect()
1121{
1122 if (mCGuest->isValid(VMSTATS_NET_RATE))
1123 {
1124 mRx->put(mCGuest->getVmNetRx());
1125 mTx->put(mCGuest->getVmNetTx());
1126 mCGuest->invalidate(VMSTATS_NET_RATE);
1127 }
1128}
1129
1130int MachineNetRate::enable()
1131{
1132 int rc = mCGuest->enable(VMSTATS_NET_RATE);
1133 BaseMetric::enable();
1134 return rc;
1135}
1136
1137int MachineNetRate::disable()
1138{
1139 BaseMetric::disable();
1140 return mCGuest->disable(VMSTATS_NET_RATE);
1141}
1142
1143void MachineNetRate::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1144{
1145 hints.collectGuestStats(mCGuest->getProcess());
1146}
1147
1148void GuestCpuLoad::init(ULONG period, ULONG length)
1149{
1150 mPeriod = period;
1151 mLength = length;
1152
1153 mUser->init(mLength);
1154 mKernel->init(mLength);
1155 mIdle->init(mLength);
1156}
1157
1158void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1159{
1160 hints.collectGuestStats(mCGuest->getProcess());
1161}
1162
1163void GuestCpuLoad::collect()
1164{
1165 if (mCGuest->isValid(VMSTATS_GUEST_CPULOAD))
1166 {
1167 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100);
1168 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100);
1169 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100);
1170 mCGuest->invalidate(VMSTATS_GUEST_CPULOAD);
1171 }
1172}
1173
1174int GuestCpuLoad::enable()
1175{
1176 int rc = mCGuest->enable(VMSTATS_GUEST_CPULOAD);
1177 BaseMetric::enable();
1178 return rc;
1179}
1180
1181int GuestCpuLoad::disable()
1182{
1183 BaseMetric::disable();
1184 return mCGuest->disable(VMSTATS_GUEST_CPULOAD);
1185}
1186
1187void GuestRamUsage::init(ULONG period, ULONG length)
1188{
1189 mPeriod = period;
1190 mLength = length;
1191
1192 mTotal->init(mLength);
1193 mFree->init(mLength);
1194 mBallooned->init(mLength);
1195 mShared->init(mLength);
1196 mCache->init(mLength);
1197 mPagedTotal->init(mLength);
1198}
1199
1200void GuestRamUsage::collect()
1201{
1202 if (mCGuest->isValid(VMSTATS_GUEST_RAMUSAGE))
1203 {
1204 mTotal->put(mCGuest->getMemTotal());
1205 mFree->put(mCGuest->getMemFree());
1206 mBallooned->put(mCGuest->getMemBalloon());
1207 mShared->put(mCGuest->getMemShared());
1208 mCache->put(mCGuest->getMemCache());
1209 mPagedTotal->put(mCGuest->getPageTotal());
1210 mCGuest->invalidate(VMSTATS_GUEST_RAMUSAGE);
1211 }
1212}
1213
1214int GuestRamUsage::enable()
1215{
1216 int rc = mCGuest->enable(VMSTATS_GUEST_RAMUSAGE);
1217 BaseMetric::enable();
1218 return rc;
1219}
1220
1221int GuestRamUsage::disable()
1222{
1223 BaseMetric::disable();
1224 return mCGuest->disable(VMSTATS_GUEST_RAMUSAGE);
1225}
1226
1227void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1228{
1229 hints.collectGuestStats(mCGuest->getProcess());
1230}
1231#endif /* !VBOX_COLLECTOR_TEST_CASE */
1232
1233void CircularBuffer::init(ULONG ulLength)
1234{
1235 if (mData)
1236 RTMemFree(mData);
1237 mLength = ulLength;
1238 if (mLength)
1239 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
1240 else
1241 mData = NULL;
1242 mWrapped = false;
1243 mEnd = 0;
1244 mSequenceNumber = 0;
1245}
1246
1247ULONG CircularBuffer::length()
1248{
1249 return mWrapped ? mLength : mEnd;
1250}
1251
1252void CircularBuffer::put(ULONG value)
1253{
1254 if (mData)
1255 {
1256 mData[mEnd++] = value;
1257 if (mEnd >= mLength)
1258 {
1259 mEnd = 0;
1260 mWrapped = true;
1261 }
1262 ++mSequenceNumber;
1263 }
1264}
1265
1266void CircularBuffer::copyTo(ULONG *data)
1267{
1268 if (mWrapped)
1269 {
1270 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
1271 // Copy the wrapped part
1272 if (mEnd)
1273 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
1274 }
1275 else
1276 memcpy(data, mData, mEnd * sizeof(ULONG));
1277}
1278
1279void SubMetric::query(ULONG *data)
1280{
1281 copyTo(data);
1282}
1283
1284void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
1285{
1286 ULONG length;
1287 ULONG *tmpData;
1288
1289 length = mSubMetric->length();
1290 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
1291 if (length)
1292 {
1293 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
1294 mSubMetric->query(tmpData);
1295 if (mAggregate)
1296 {
1297 *count = 1;
1298 *data = (ULONG*)RTMemAlloc(sizeof(**data));
1299 **data = mAggregate->compute(tmpData, length);
1300 RTMemFree(tmpData);
1301 }
1302 else
1303 {
1304 *count = length;
1305 *data = tmpData;
1306 }
1307 }
1308 else
1309 {
1310 *count = 0;
1311 *data = 0;
1312 }
1313}
1314
1315ULONG AggregateAvg::compute(ULONG *data, ULONG length)
1316{
1317 uint64_t tmp = 0;
1318 for (ULONG i = 0; i < length; ++i)
1319 tmp += data[i];
1320 return (ULONG)(tmp / length);
1321}
1322
1323const char * AggregateAvg::getName()
1324{
1325 return "avg";
1326}
1327
1328ULONG AggregateMin::compute(ULONG *data, ULONG length)
1329{
1330 ULONG tmp = *data;
1331 for (ULONG i = 0; i < length; ++i)
1332 if (data[i] < tmp)
1333 tmp = data[i];
1334 return tmp;
1335}
1336
1337const char * AggregateMin::getName()
1338{
1339 return "min";
1340}
1341
1342ULONG AggregateMax::compute(ULONG *data, ULONG length)
1343{
1344 ULONG tmp = *data;
1345 for (ULONG i = 0; i < length; ++i)
1346 if (data[i] > tmp)
1347 tmp = data[i];
1348 return tmp;
1349}
1350
1351const char * AggregateMax::getName()
1352{
1353 return "max";
1354}
1355
1356Filter::Filter(ComSafeArrayIn(IN_BSTR, metricNames),
1357 ComSafeArrayIn(IUnknown *, objects))
1358{
1359 /*
1360 * Let's work around null/empty safe array mess. I am not sure there is
1361 * a way to pass null arrays via webservice, I haven't found one. So I
1362 * guess the users will be forced to use empty arrays instead. Constructing
1363 * an empty SafeArray is a bit awkward, so what we do in this method is
1364 * actually convert null arrays to empty arrays and pass them down to
1365 * init() method. If someone knows how to do it better, please be my guest,
1366 * fix it.
1367 */
1368 if (ComSafeArrayInIsNull(metricNames))
1369 {
1370 com::SafeArray<BSTR> nameArray;
1371 if (ComSafeArrayInIsNull(objects))
1372 {
1373 com::SafeIfaceArray<IUnknown> objectArray;
1374 objectArray.reset(0);
1375 init(ComSafeArrayAsInParam(nameArray),
1376 ComSafeArrayAsInParam(objectArray));
1377 }
1378 else
1379 {
1380 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1381 init(ComSafeArrayAsInParam(nameArray),
1382 ComSafeArrayAsInParam(objectArray));
1383 }
1384 }
1385 else
1386 {
1387 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1388 if (ComSafeArrayInIsNull(objects))
1389 {
1390 com::SafeIfaceArray<IUnknown> objectArray;
1391 objectArray.reset(0);
1392 init(ComSafeArrayAsInParam(nameArray),
1393 ComSafeArrayAsInParam(objectArray));
1394 }
1395 else
1396 {
1397 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1398 init(ComSafeArrayAsInParam(nameArray),
1399 ComSafeArrayAsInParam(objectArray));
1400 }
1401 }
1402}
1403
1404Filter::Filter(const com::Utf8Str name, const ComPtr<IUnknown> &aObject)
1405{
1406 processMetricList(name, aObject);
1407}
1408
1409void Filter::init(ComSafeArrayIn(IN_BSTR, metricNames),
1410 ComSafeArrayIn(IUnknown *, objects))
1411{
1412 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1413 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1414
1415 if (!objectArray.size())
1416 {
1417 if (nameArray.size())
1418 {
1419 for (size_t i = 0; i < nameArray.size(); ++i)
1420 processMetricList(com::Utf8Str(nameArray[i]), ComPtr<IUnknown>());
1421 }
1422 else
1423 processMetricList("*", ComPtr<IUnknown>());
1424 }
1425 else
1426 {
1427 for (size_t i = 0; i < objectArray.size(); ++i)
1428 switch (nameArray.size())
1429 {
1430 case 0:
1431 processMetricList("*", objectArray[i]);
1432 break;
1433 case 1:
1434 processMetricList(com::Utf8Str(nameArray[0]), objectArray[i]);
1435 break;
1436 default:
1437 processMetricList(com::Utf8Str(nameArray[i]), objectArray[i]);
1438 break;
1439 }
1440 }
1441}
1442
1443void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
1444{
1445 size_t startPos = 0;
1446
1447 for (size_t pos = name.find(",");
1448 pos != com::Utf8Str::npos;
1449 pos = name.find(",", startPos))
1450 {
1451 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos, pos - startPos).c_str())));
1452 startPos = pos + 1;
1453 }
1454 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos).c_str())));
1455}
1456
1457/**
1458 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
1459 * modified to handle the special case of trailing colon in the pattern.
1460 *
1461 * @returns True if matches, false if not.
1462 * @param pszPat Pattern.
1463 * @param pszName Name to match against the pattern.
1464 * @param fSeenColon Seen colon (':').
1465 */
1466bool Filter::patternMatch(const char *pszPat, const char *pszName,
1467 bool fSeenColon)
1468{
1469 /* ASSUMES ASCII */
1470 for (;;)
1471 {
1472 char chPat = *pszPat;
1473 switch (chPat)
1474 {
1475 default:
1476 if (*pszName != chPat)
1477 return false;
1478 break;
1479
1480 case '*':
1481 {
1482 while ((chPat = *++pszPat) == '*' || chPat == '?')
1483 /* nothing */;
1484
1485 /* Handle a special case, the mask terminating with a colon. */
1486 if (chPat == ':')
1487 {
1488 if (!fSeenColon && !pszPat[1])
1489 return !strchr(pszName, ':');
1490 fSeenColon = true;
1491 }
1492
1493 for (;;)
1494 {
1495 char ch = *pszName++;
1496 if ( ch == chPat
1497 && ( !chPat
1498 || patternMatch(pszPat + 1, pszName, fSeenColon)))
1499 return true;
1500 if (!ch)
1501 return false;
1502 }
1503 /* won't ever get here */
1504 break;
1505 }
1506
1507 case '?':
1508 if (!*pszName)
1509 return false;
1510 break;
1511
1512 /* Handle a special case, the mask terminating with a colon. */
1513 case ':':
1514 if (!fSeenColon && !pszPat[1])
1515 return !*pszName;
1516 if (*pszName != ':')
1517 return false;
1518 fSeenColon = true;
1519 break;
1520
1521 case '\0':
1522 return !*pszName;
1523 }
1524 pszName++;
1525 pszPat++;
1526 }
1527 return true;
1528}
1529
1530bool Filter::match(const ComPtr<IUnknown> object, const RTCString &name) const
1531{
1532 ElementList::const_iterator it;
1533
1534 //LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
1535 for (it = mElements.begin(); it != mElements.end(); it++)
1536 {
1537 //LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
1538 if ((*it).first.isNull() || (*it).first == object)
1539 {
1540 // Objects match, compare names
1541 if (patternMatch((*it).second.c_str(), name.c_str()))
1542 {
1543 //LogFlowThisFunc(("...found!\n"));
1544 return true;
1545 }
1546 }
1547 }
1548 //LogAleksey(("...no matches!\n"));
1549 return false;
1550}
1551/* 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