VirtualBox

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

Last change on this file since 85263 was 85263, checked in by vboxsync, 5 years ago

Main/Performance.cpp/h: A whole bunch of int/HRESULT mixups. bugref:9790

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