VirtualBox

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

Last change on this file since 44029 was 43994, checked in by vboxsync, 12 years ago

Main/Metrics: Do not collect data for unplugged host interfaces (#6345)

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