VirtualBox

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

Last change on this file since 78349 was 76593, checked in by vboxsync, 6 years ago

Main: Don't use Logging.h.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.6 KB
Line 
1/* $Id: Performance.cpp 76593 2019-01-01 20:13:29Z vboxsync $ */
2/** @file
3 * VBox Performance Classes implementation.
4 */
5
6/*
7 * Copyright (C) 2008-2019 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 (RTCPUID 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 rc = VINF_SUCCESS;
162 CollectorGuestRequest* rq = NULL;
163
164 do
165 {
166 {
167 RTCLock lock(mLockMtx);
168
169 if (!mQueue.empty())
170 {
171 rq = mQueue.front();
172 mQueue.pop();
173 }
174 }
175
176 if (rq)
177 return rq;
178 else
179 rc = RTSemEventWaitNoResume(mEvent, RT_INDEFINITE_WAIT);
180 }
181 while (RT_SUCCESS(rc));
182
183 return NULL;
184}
185
186HRESULT CGRQEnable::execute()
187{
188 Assert(mCGuest);
189 return mCGuest->enableInternal(mMask);
190}
191
192void CGRQEnable::debugPrint(void *aObject, const char *aFunction, const char *aText)
193{
194 NOREF(aObject);
195 NOREF(aFunction);
196 NOREF(aText);
197 Log7((LOG_FN_FMT ": {%p}: CGRQEnable(mask=0x%x) %s\n", aObject, aFunction, mMask, aText));
198}
199
200HRESULT CGRQDisable::execute()
201{
202 Assert(mCGuest);
203 return mCGuest->disableInternal(mMask);
204}
205
206void CGRQDisable::debugPrint(void *aObject, const char *aFunction, const char *aText)
207{
208 NOREF(aObject);
209 NOREF(aFunction);
210 NOREF(aText);
211 Log7((LOG_FN_FMT ": {%p}: CGRQDisable(mask=0x%x) %s\n", aObject, aFunction, mMask, aText));
212}
213
214HRESULT 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 Log7((LOG_FN_FMT ": {%p}: CGRQAbort %s\n", aObject, aFunction, aText));
225}
226
227CollectorGuest::CollectorGuest(Machine *machine, RTPROCESS process) :
228 mUnregistered(false), mEnabled(false), mValid(false), mMachine(machine), mProcess(process),
229 mCpuUser(0), mCpuKernel(0), mCpuIdle(0),
230 mMemTotal(0), mMemFree(0), mMemBalloon(0), mMemShared(0), mMemCache(0), mPageTotal(0),
231 mAllocVMM(0), mFreeVMM(0), mBalloonedVMM(0), mSharedVMM(0), mVmNetRx(0), mVmNetTx(0)
232{
233 Assert(mMachine);
234 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
235 mMachine->AddRef();
236}
237
238CollectorGuest::~CollectorGuest()
239{
240 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
241 mMachine->Release();
242 // Assert(!cEnabled); why?
243}
244
245int CollectorGuest::enableVMMStats(bool mCollectVMMStats)
246{
247 HRESULT ret = S_OK;
248
249 if (mGuest)
250 {
251 /** @todo replace this with a direct call to mGuest in trunk! */
252 AutoCaller autoCaller(mMachine);
253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
254
255 ComPtr<IInternalSessionControl> directControl;
256
257 ret = mMachine->i_getDirectControl(&directControl);
258 if (ret != S_OK)
259 return ret;
260
261 /* enable statistics collection; this is a remote call (!) */
262 ret = directControl->EnableVMMStatistics(mCollectVMMStats);
263 Log7Func(("{%p}: %sable VMM stats (%s)\n",
264 this, mCollectVMMStats ? "En" : "Dis", SUCCEEDED(ret) ? "success" : "failed"));
265 }
266
267 return ret;
268}
269
270int CollectorGuest::enable(ULONG mask)
271{
272 return enqueueRequest(new CGRQEnable(mask));
273}
274
275int CollectorGuest::disable(ULONG mask)
276{
277 return enqueueRequest(new CGRQDisable(mask));
278}
279
280HRESULT CollectorGuest::enableInternal(ULONG mask)
281{
282 HRESULT ret = S_OK;
283
284 if ((mEnabled & mask) == mask)
285 return E_UNEXPECTED;
286
287 if (!mEnabled)
288 {
289 /* Must make sure that the machine object does not get uninitialized
290 * in the middle of enabling this collector. Causes timing-related
291 * behavior otherwise, which we don't want. In particular the
292 * GetRemoteConsole call below can hang if the VM didn't completely
293 * terminate (the VM processes stop processing events shortly before
294 * closing the session). This avoids the hang. */
295 AutoCaller autoCaller(mMachine);
296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
297
298 mMachineName = mMachine->i_getName();
299
300 ComPtr<IInternalSessionControl> directControl;
301
302 ret = mMachine->i_getDirectControl(&directControl);
303 if (ret != S_OK)
304 return ret;
305
306 /* get the associated console; this is a remote call (!) */
307 ret = directControl->COMGETTER(RemoteConsole)(mConsole.asOutParam());
308 if (ret != S_OK)
309 return ret;
310
311 ret = mConsole->COMGETTER(Guest)(mGuest.asOutParam());
312 if (ret == S_OK)
313 {
314 ret = mGuest->COMSETTER(StatisticsUpdateInterval)(1 /* 1 sec */);
315 Log7Func(("{%p}: Set guest statistics update interval to 1 sec (%s)\n",
316 this, SUCCEEDED(ret) ? "success" : "failed"));
317 }
318 }
319 if ((mask & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM)
320 enableVMMStats(true);
321 mEnabled |= mask;
322
323 return ret;
324}
325
326int CollectorGuest::disableInternal(ULONG mask)
327{
328 if (!(mEnabled & mask))
329 return E_UNEXPECTED;
330
331 if ((mask & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM)
332 enableVMMStats(false);
333 mEnabled &= ~mask;
334 if (!mEnabled)
335 {
336 Assert(mGuest && mConsole);
337 HRESULT ret = mGuest->COMSETTER(StatisticsUpdateInterval)(0 /* off */);
338 NOREF(ret);
339 Log7Func(("{%p}: Set guest statistics update interval to 0 sec (%s)\n",
340 this, SUCCEEDED(ret) ? "success" : "failed"));
341 invalidate(VMSTATS_ALL);
342 }
343
344 return S_OK;
345}
346
347int CollectorGuest::enqueueRequest(CollectorGuestRequest *aRequest)
348{
349 if (mManager)
350 {
351 aRequest->setGuest(this);
352 return mManager->enqueueRequest(aRequest);
353 }
354
355 Log7Func(("{%p}: Attempted enqueue guest request when mManager is null\n", this));
356 return E_POINTER;
357}
358
359void CollectorGuest::updateStats(ULONG aValidStats, ULONG aCpuUser,
360 ULONG aCpuKernel, ULONG aCpuIdle,
361 ULONG aMemTotal, ULONG aMemFree,
362 ULONG aMemBalloon, ULONG aMemShared,
363 ULONG aMemCache, ULONG aPageTotal,
364 ULONG aAllocVMM, ULONG aFreeVMM,
365 ULONG aBalloonedVMM, ULONG aSharedVMM,
366 ULONG aVmNetRx, ULONG aVmNetTx)
367{
368 if ((aValidStats & VMSTATS_GUEST_CPULOAD) == VMSTATS_GUEST_CPULOAD)
369 {
370 mCpuUser = aCpuUser;
371 mCpuKernel = aCpuKernel,
372 mCpuIdle = aCpuIdle;
373 }
374 if ((aValidStats & VMSTATS_GUEST_RAMUSAGE) == VMSTATS_GUEST_RAMUSAGE)
375 {
376 mMemTotal = aMemTotal;
377 mMemFree = aMemFree;
378 mMemBalloon = aMemBalloon;
379 mMemShared = aMemShared;
380 mMemCache = aMemCache;
381 mPageTotal = aPageTotal;
382 }
383 if ((aValidStats & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM)
384 {
385 mAllocVMM = aAllocVMM;
386 mFreeVMM = aFreeVMM;
387 mBalloonedVMM = aBalloonedVMM;
388 mSharedVMM = aSharedVMM;
389 }
390 if ((aValidStats & VMSTATS_NET_RATE) == VMSTATS_NET_RATE)
391 {
392 mVmNetRx = aVmNetRx;
393 mVmNetTx = aVmNetTx;
394 }
395 mValid = aValidStats;
396}
397
398CollectorGuestManager::CollectorGuestManager()
399 : mVMMStatsProvider(NULL), mGuestBeingCalled(NULL)
400{
401 int rc = RTThreadCreate(&mThread, CollectorGuestManager::requestProcessingThread,
402 this, 0, RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE,
403 "CGMgr");
404 NOREF(rc);
405 Log7Func(("{%p}: RTThreadCreate returned %Rrc (mThread=%p)\n", this, rc, mThread));
406}
407
408CollectorGuestManager::~CollectorGuestManager()
409{
410 Assert(mGuests.size() == 0);
411 int rcThread = 0;
412 int rc = enqueueRequest(new CGRQAbort());
413 if (SUCCEEDED(rc))
414 {
415 /* We wait only if we were able to put the abort request to a queue */
416 Log7Func(("{%p}: Waiting for CGM request processing thread to stop...\n", this));
417 rc = RTThreadWait(mThread, 1000 /* 1 sec */, &rcThread);
418 Log7Func(("{%p}: RTThreadWait returned %u (thread exit code: %u)\n", this, rc, rcThread));
419 }
420}
421
422void CollectorGuestManager::registerGuest(CollectorGuest* pGuest)
423{
424 pGuest->setManager(this);
425 mGuests.push_back(pGuest);
426 /*
427 * If no VMM stats provider was elected previously than this is our
428 * candidate.
429 */
430 if (!mVMMStatsProvider)
431 mVMMStatsProvider = pGuest;
432 Log7Func(("{%p}: Registered guest=%p provider=%p\n", this, pGuest, mVMMStatsProvider));
433}
434
435void CollectorGuestManager::unregisterGuest(CollectorGuest* pGuest)
436{
437 int rc = S_OK;
438
439 Log7Func(("{%p}: About to unregister guest=%p provider=%p\n", this, pGuest, mVMMStatsProvider));
440 //mGuests.remove(pGuest); => destroyUnregistered()
441 pGuest->unregister();
442 if (pGuest == mVMMStatsProvider)
443 {
444 /* This was our VMM stats provider, it is time to re-elect */
445 CollectorGuestList::iterator it;
446 /* Assume that nobody can provide VMM stats */
447 mVMMStatsProvider = NULL;
448
449 for (it = mGuests.begin(); it != mGuests.end(); ++it)
450 {
451 /* Skip unregistered as they are about to be destroyed */
452 if ((*it)->isUnregistered())
453 continue;
454
455 if ((*it)->isEnabled())
456 {
457 /* Found the guest already collecting stats, elect it */
458 mVMMStatsProvider = *it;
459 rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(VMSTATS_VMM_RAM));
460 if (FAILED(rc))
461 {
462 /* This is not a good candidate -- try to find another */
463 mVMMStatsProvider = NULL;
464 continue;
465 }
466 break;
467 }
468 }
469 if (!mVMMStatsProvider)
470 {
471 /* If nobody collects stats, take the first registered */
472 for (it = mGuests.begin(); it != mGuests.end(); ++it)
473 {
474 /* Skip unregistered as they are about to be destroyed */
475 if ((*it)->isUnregistered())
476 continue;
477
478 mVMMStatsProvider = *it;
479 //mVMMStatsProvider->enable(VMSTATS_VMM_RAM);
480 rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(VMSTATS_VMM_RAM));
481 if (SUCCEEDED(rc))
482 break;
483 /* This was not a good candidate -- try to find another */
484 mVMMStatsProvider = NULL;
485 }
486 }
487 }
488 Log7Func(("[%p}: LEAVE new provider=%p\n", this, mVMMStatsProvider));
489}
490
491void CollectorGuestManager::destroyUnregistered()
492{
493 CollectorGuestList::iterator it;
494
495 for (it = mGuests.begin(); it != mGuests.end();)
496 if ((*it)->isUnregistered())
497 {
498 delete *it;
499 it = mGuests.erase(it);
500 Log7Func(("{%p}: Number of guests after erasing unregistered is %d\n",
501 this, mGuests.size()));
502 }
503 else
504 ++it;
505}
506
507int CollectorGuestManager::enqueueRequest(CollectorGuestRequest *aRequest)
508{
509#ifdef DEBUG
510 aRequest->debugPrint(this, __PRETTY_FUNCTION__, "added to CGM queue");
511#endif /* DEBUG */
512 /*
513 * It is very unlikely that we will get high frequency calls to configure
514 * guest metrics collection, so we rely on this fact to detect blocked
515 * guests. If the guest has not finished processing the previous request
516 * after half a second we consider it blocked.
517 */
518 if (aRequest->getGuest() && aRequest->getGuest() == mGuestBeingCalled)
519 {
520 /*
521 * Before we can declare a guest blocked we need to wait for a while
522 * and then check again as it may never had a chance to process
523 * the previous request. Half a second is an eternity for processes
524 * and is barely noticable by humans.
525 */
526 Log7Func(("{%p}: Suspecting %s is stalled. Waiting for .5 sec...\n",
527 this, aRequest->getGuest()->getVMName().c_str()));
528 RTThreadSleep(500 /* ms */);
529 if (aRequest->getGuest() == mGuestBeingCalled) {
530 Log7Func(("{%p}: Request processing stalled for %s\n",
531 this, aRequest->getGuest()->getVMName().c_str()));
532 /* Request execution got stalled for this guest -- report an error */
533 return E_FAIL;
534 }
535 }
536 mQueue.push(aRequest);
537 return S_OK;
538}
539
540/* static */
541DECLCALLBACK(int) CollectorGuestManager::requestProcessingThread(RTTHREAD /* aThread */, void *pvUser)
542{
543 CollectorGuestRequest *pReq;
544 CollectorGuestManager *mgr = static_cast<CollectorGuestManager*>(pvUser);
545
546 HRESULT rc = S_OK;
547
548 Log7Func(("{%p}: Starting request processing loop...\n", mgr));
549 while ((pReq = mgr->mQueue.pop()) != NULL)
550 {
551#ifdef DEBUG
552 pReq->debugPrint(mgr, __PRETTY_FUNCTION__, "is being executed...");
553#endif /* DEBUG */
554 mgr->mGuestBeingCalled = pReq->getGuest();
555 rc = pReq->execute();
556 mgr->mGuestBeingCalled = NULL;
557 delete pReq;
558 if (rc == E_ABORT)
559 break;
560 if (FAILED(rc))
561 Log7Func(("{%p}: request::execute returned %u\n", mgr, rc));
562 }
563 Log7Func(("{%p}: Exiting request processing loop... rc=%u\n", mgr, rc));
564
565 return VINF_SUCCESS;
566}
567
568
569#endif /* !VBOX_COLLECTOR_TEST_CASE */
570
571bool BaseMetric::collectorBeat(uint64_t nowAt)
572{
573 if (isEnabled())
574 {
575 if (mLastSampleTaken == 0)
576 {
577 mLastSampleTaken = nowAt;
578 Log4Func(("{%p}: Collecting %s for obj(%p)...\n",
579 this, getName(), (void *)mObject));
580 return true;
581 }
582 /*
583 * We use low resolution timers which may fire just a little bit early.
584 * We compensate for that by jumping into the future by several
585 * milliseconds (see @bugref{6345}).
586 */
587 if (nowAt - mLastSampleTaken + PM_SAMPLER_PRECISION_MS >= mPeriod * 1000)
588 {
589 /*
590 * We don't want the beat to drift. This is why the timestamp of
591 * the last taken sample is not the actual time but the time we
592 * should have taken the measurement at.
593 */
594 mLastSampleTaken += mPeriod * 1000;
595 Log4Func(("{%p}: Collecting %s for obj(%p)...\n",
596 this, getName(), (void *)mObject));
597 return true;
598 }
599 Log4Func(("{%p}: Enabled but too early to collect %s for obj(%p)\n",
600 this, getName(), (void *)mObject));
601 }
602 return false;
603}
604
605void HostCpuLoad::init(ULONG period, ULONG length)
606{
607 mPeriod = period;
608 mLength = length;
609 mUser->init(mLength);
610 mKernel->init(mLength);
611 mIdle->init(mLength);
612}
613
614void HostCpuLoad::collect()
615{
616 ULONG user, kernel, idle;
617 int rc = mHAL->getHostCpuLoad(&user, &kernel, &idle);
618 if (RT_SUCCESS(rc))
619 {
620 mUser->put(user);
621 mKernel->put(kernel);
622 mIdle->put(idle);
623 }
624}
625
626void HostCpuLoadRaw::init(ULONG period, ULONG length)
627{
628 HostCpuLoad::init(period, length);
629 mHAL->getRawHostCpuLoad(&mUserPrev, &mKernelPrev, &mIdlePrev);
630}
631
632void HostCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
633{
634 hints.collectHostCpuLoad();
635}
636
637void HostCpuLoadRaw::collect()
638{
639 uint64_t user, kernel, idle;
640 uint64_t userDiff, kernelDiff, idleDiff, totalDiff;
641
642 int rc = mHAL->getRawHostCpuLoad(&user, &kernel, &idle);
643 if (RT_SUCCESS(rc))
644 {
645 userDiff = user - mUserPrev;
646 kernelDiff = kernel - mKernelPrev;
647 idleDiff = idle - mIdlePrev;
648 totalDiff = userDiff + kernelDiff + idleDiff;
649
650 if (totalDiff == 0)
651 {
652 /* This is only possible if none of counters has changed! */
653 LogFlowThisFunc(("Impossible! User, kernel and idle raw "
654 "counters has not changed since last sample.\n" ));
655 mUser->put(0);
656 mKernel->put(0);
657 mIdle->put(0);
658 }
659 else
660 {
661 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff));
662 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff));
663 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff));
664 }
665
666 mUserPrev = user;
667 mKernelPrev = kernel;
668 mIdlePrev = idle;
669 }
670}
671
672#ifndef VBOX_COLLECTOR_TEST_CASE
673static bool getLinkSpeed(const char *szShortName, uint32_t *pSpeed)
674{
675 NETIFSTATUS enmState = NETIF_S_UNKNOWN;
676 int rc = NetIfGetState(szShortName, &enmState);
677 if (RT_FAILURE(rc))
678 return false;
679 if (enmState != NETIF_S_UP)
680 *pSpeed = 0;
681 else
682 {
683 rc = NetIfGetLinkSpeed(szShortName, pSpeed);
684 if (RT_FAILURE(rc))
685 return false;
686 }
687 return true;
688}
689
690void HostNetworkSpeed::init(ULONG period, ULONG length)
691{
692 mPeriod = period;
693 mLength = length;
694 mLinkSpeed->init(length);
695 /*
696 * Retrieve the link speed now as it may be wrong if the metric was
697 * registered at boot (see @bugref{6613}).
698 */
699 getLinkSpeed(mShortName.c_str(), &mSpeed);
700}
701
702void HostNetworkLoadRaw::init(ULONG period, ULONG length)
703{
704 mPeriod = period;
705 mLength = length;
706 mRx->init(mLength);
707 mTx->init(mLength);
708 /*
709 * Retrieve the link speed now as it may be wrong if the metric was
710 * registered at boot (see @bugref{6613}).
711 */
712 uint32_t uSpeedMbit = 65535;
713 if (getLinkSpeed(mShortName.c_str(), &uSpeedMbit))
714 mSpeed = (uint64_t)uSpeedMbit * (1000000/8); /* Convert to bytes/sec */
715 /*int rc =*/ mHAL->getRawHostNetworkLoad(mShortName.c_str(), &mRxPrev, &mTxPrev);
716 //AssertRC(rc);
717}
718
719void HostNetworkLoadRaw::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
720{
721 if (RT_FAILURE(mRc))
722 {
723 ComPtr<IHostNetworkInterface> networkInterface;
724 ComPtr<IHost> host = getObject();
725 HRESULT hrc = host->FindHostNetworkInterfaceByName(com::Bstr(mInterfaceName).raw(), networkInterface.asOutParam());
726 if (SUCCEEDED(hrc))
727 {
728 static uint32_t s_tsLogRelLast;
729 uint32_t tsNow = RTTimeProgramSecTS();
730 if ( tsNow < RT_SEC_1HOUR
731 || (tsNow - s_tsLogRelLast >= 60))
732 {
733 s_tsLogRelLast = tsNow;
734 LogRel(("Failed to collect network metrics for %s: %Rrc (%d). Max one msg/min.\n", mInterfaceName.c_str(), mRc, mRc));
735 }
736 mRc = VINF_SUCCESS;
737 }
738 }
739}
740
741void HostNetworkLoadRaw::collect()
742{
743 uint64_t rx = mRxPrev;
744 uint64_t tx = mTxPrev;
745
746 if (RT_UNLIKELY(mSpeed * getPeriod() == 0))
747 {
748 LogFlowThisFunc(("Check cable for %s! speed=%llu period=%d.\n", mShortName.c_str(), mSpeed, getPeriod()));
749 /* We do not collect host network metrics for unplugged interfaces! */
750 return;
751 }
752 mRc = mHAL->getRawHostNetworkLoad(mShortName.c_str(), &rx, &tx);
753 if (RT_SUCCESS(mRc))
754 {
755 uint64_t rxDiff = rx - mRxPrev;
756 uint64_t txDiff = tx - mTxPrev;
757
758 mRx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * rxDiff / (mSpeed * getPeriod())));
759 mTx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * txDiff / (mSpeed * getPeriod())));
760
761 mRxPrev = rx;
762 mTxPrev = tx;
763 }
764 else
765 LogFlowThisFunc(("Failed to collect data: %Rrc (%d)."
766 " Will update the list of interfaces...\n", mRc,mRc));
767}
768#endif /* !VBOX_COLLECTOR_TEST_CASE */
769
770void HostDiskLoadRaw::init(ULONG period, ULONG length)
771{
772 mPeriod = period;
773 mLength = length;
774 mUtil->init(mLength);
775 int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &mDiskPrev, &mTotalPrev);
776 AssertRC(rc);
777}
778
779void HostDiskLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
780{
781 hints.collectHostCpuLoad();
782}
783
784void HostDiskLoadRaw::collect()
785{
786 uint64_t disk, total;
787
788 int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &disk, &total);
789 if (RT_SUCCESS(rc))
790 {
791 uint64_t diskDiff = disk - mDiskPrev;
792 uint64_t totalDiff = total - mTotalPrev;
793
794 if (RT_UNLIKELY(totalDiff == 0))
795 {
796 Assert(totalDiff);
797 LogFlowThisFunc(("Improbable! Less than millisecond passed! Disk=%s\n", mDiskName.c_str()));
798 mUtil->put(0);
799 }
800 else if (diskDiff > totalDiff)
801 {
802 /*
803 * It is possible that the disk spent more time than CPU because
804 * CPU measurements are taken during the pre-collect phase. We try
805 * to compensate for than by adding the extra to the next round of
806 * measurements.
807 */
808 mUtil->put(PM_NETWORK_LOAD_MULTIPLIER);
809 Assert((diskDiff - totalDiff) < mPeriod * 1000);
810 if ((diskDiff - totalDiff) > mPeriod * 1000)
811 {
812 LogRel(("Disk utilization time exceeds CPU time by more"
813 " than the collection period (%llu ms)\n", diskDiff - totalDiff));
814 }
815 else
816 {
817 disk = mDiskPrev + totalDiff;
818 LogFlowThisFunc(("Moved %u milliseconds to the next period.\n", (unsigned)(diskDiff - totalDiff)));
819 }
820 }
821 else
822 {
823 mUtil->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * diskDiff / totalDiff));
824 }
825
826 mDiskPrev = disk;
827 mTotalPrev = total;
828 }
829 else
830 LogFlowThisFunc(("Failed to collect data: %Rrc (%d)\n", rc, rc));
831}
832
833void HostCpuMhz::init(ULONG period, ULONG length)
834{
835 mPeriod = period;
836 mLength = length;
837 mMHz->init(mLength);
838}
839
840void HostCpuMhz::collect()
841{
842 ULONG mhz;
843 int rc = mHAL->getHostCpuMHz(&mhz);
844 if (RT_SUCCESS(rc))
845 mMHz->put(mhz);
846}
847
848void HostRamUsage::init(ULONG period, ULONG length)
849{
850 mPeriod = period;
851 mLength = length;
852 mTotal->init(mLength);
853 mUsed->init(mLength);
854 mAvailable->init(mLength);
855}
856
857void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
858{
859 hints.collectHostRamUsage();
860}
861
862void HostRamUsage::collect()
863{
864 ULONG total, used, available;
865 int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
866 if (RT_SUCCESS(rc))
867 {
868 mTotal->put(total);
869 mUsed->put(used);
870 mAvailable->put(available);
871 }
872}
873
874void HostFilesystemUsage::init(ULONG period, ULONG length)
875{
876 mPeriod = period;
877 mLength = length;
878 mTotal->init(mLength);
879 mUsed->init(mLength);
880 mAvailable->init(mLength);
881}
882
883void HostFilesystemUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
884{
885}
886
887void HostFilesystemUsage::collect()
888{
889 ULONG total, used, available;
890 int rc = mHAL->getHostFilesystemUsage(mFsName.c_str(), &total, &used, &available);
891 if (RT_SUCCESS(rc))
892 {
893 mTotal->put(total);
894 mUsed->put(used);
895 mAvailable->put(available);
896 }
897}
898
899void HostDiskUsage::init(ULONG period, ULONG length)
900{
901 mPeriod = period;
902 mLength = length;
903 mTotal->init(mLength);
904}
905
906void HostDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
907{
908}
909
910void HostDiskUsage::collect()
911{
912 uint64_t total;
913 int rc = mHAL->getHostDiskSize(mDiskName.c_str(), &total);
914 if (RT_SUCCESS(rc))
915 mTotal->put((ULONG)(total / _1M));
916}
917
918#ifndef VBOX_COLLECTOR_TEST_CASE
919void HostRamVmm::init(ULONG period, ULONG length)
920{
921 mPeriod = period;
922 mLength = length;
923 mAllocVMM->init(mLength);
924 mFreeVMM->init(mLength);
925 mBalloonVMM->init(mLength);
926 mSharedVMM->init(mLength);
927}
928
929int HostRamVmm::enable()
930{
931 int rc = S_OK;
932 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
933 if (provider)
934 rc = provider->enable(VMSTATS_VMM_RAM);
935 BaseMetric::enable();
936 return rc;
937}
938
939int HostRamVmm::disable()
940{
941 int rc = S_OK;
942 BaseMetric::disable();
943 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
944 if (provider)
945 rc = provider->disable(VMSTATS_VMM_RAM);
946 return rc;
947}
948
949void HostRamVmm::preCollect(CollectorHints& hints, uint64_t /* iTick */)
950{
951 hints.collectHostRamVmm();
952}
953
954void HostRamVmm::collect()
955{
956 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
957 if (provider)
958 {
959 Log7Func(("{%p}: provider=%p enabled=%RTbool valid=%RTbool...\n",
960 this, provider, provider->isEnabled(), provider->isValid(VMSTATS_VMM_RAM) ));
961 if (provider->isValid(VMSTATS_VMM_RAM))
962 {
963 /* Provider is ready, get updated stats */
964 mAllocCurrent = provider->getAllocVMM();
965 mFreeCurrent = provider->getFreeVMM();
966 mBalloonedCurrent = provider->getBalloonedVMM();
967 mSharedCurrent = provider->getSharedVMM();
968 provider->invalidate(VMSTATS_VMM_RAM);
969 }
970 /*
971 * Note that if there are no new values from the provider we will use
972 * the ones most recently provided instead of zeros, which is probably
973 * a desirable behavior.
974 */
975 }
976 else
977 {
978 mAllocCurrent = 0;
979 mFreeCurrent = 0;
980 mBalloonedCurrent = 0;
981 mSharedCurrent = 0;
982 }
983 Log7Func(("{%p}: mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
984 this, mAllocCurrent, mFreeCurrent, mBalloonedCurrent, mSharedCurrent));
985 mAllocVMM->put(mAllocCurrent);
986 mFreeVMM->put(mFreeCurrent);
987 mBalloonVMM->put(mBalloonedCurrent);
988 mSharedVMM->put(mSharedCurrent);
989}
990#endif /* !VBOX_COLLECTOR_TEST_CASE */
991
992
993
994void MachineCpuLoad::init(ULONG period, ULONG length)
995{
996 mPeriod = period;
997 mLength = length;
998 mUser->init(mLength);
999 mKernel->init(mLength);
1000}
1001
1002void MachineCpuLoad::collect()
1003{
1004 ULONG user, kernel;
1005 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
1006 if (RT_SUCCESS(rc))
1007 {
1008 mUser->put(user);
1009 mKernel->put(kernel);
1010 }
1011}
1012
1013void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1014{
1015 hints.collectProcessCpuLoad(mProcess);
1016}
1017
1018void MachineCpuLoadRaw::collect()
1019{
1020 uint64_t processUser, processKernel, hostTotal;
1021
1022 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
1023 if (RT_SUCCESS(rc))
1024 {
1025 if (hostTotal == mHostTotalPrev)
1026 {
1027 /* Nearly impossible, but... */
1028 mUser->put(0);
1029 mKernel->put(0);
1030 }
1031 else
1032 {
1033 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
1034 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
1035 }
1036
1037 mHostTotalPrev = hostTotal;
1038 mProcessUserPrev = processUser;
1039 mProcessKernelPrev = processKernel;
1040 }
1041}
1042
1043void MachineRamUsage::init(ULONG period, ULONG length)
1044{
1045 mPeriod = period;
1046 mLength = length;
1047 mUsed->init(mLength);
1048}
1049
1050void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1051{
1052 hints.collectProcessRamUsage(mProcess);
1053}
1054
1055void MachineRamUsage::collect()
1056{
1057 ULONG used;
1058 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
1059 if (RT_SUCCESS(rc))
1060 mUsed->put(used);
1061}
1062
1063
1064#ifndef VBOX_COLLECTOR_TEST_CASE
1065void MachineDiskUsage::init(ULONG period, ULONG length)
1066{
1067 mPeriod = period;
1068 mLength = length;
1069 mUsed->init(mLength);
1070}
1071
1072void MachineDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
1073{
1074}
1075
1076void MachineDiskUsage::collect()
1077{
1078 ULONG used = 0;
1079
1080 for (MediaList::iterator it = mDisks.begin(); it != mDisks.end(); ++it)
1081 {
1082 ComObjPtr<Medium> pMedium = *it;
1083
1084 /* just in case */
1085 AssertContinue(!pMedium.isNull());
1086
1087 AutoCaller localAutoCaller(pMedium);
1088 if (FAILED(localAutoCaller.rc())) continue;
1089
1090 AutoReadLock local_alock(pMedium COMMA_LOCKVAL_SRC_POS);
1091
1092 used += (ULONG)(pMedium->i_getSize() / _1M);
1093 }
1094
1095 mUsed->put(used);
1096}
1097
1098void MachineNetRate::init(ULONG period, ULONG length)
1099{
1100 mPeriod = period;
1101 mLength = length;
1102
1103 mRx->init(mLength);
1104 mTx->init(mLength);
1105}
1106
1107void MachineNetRate::collect()
1108{
1109 if (mCGuest->isValid(VMSTATS_NET_RATE))
1110 {
1111 mRx->put(mCGuest->getVmNetRx());
1112 mTx->put(mCGuest->getVmNetTx());
1113 mCGuest->invalidate(VMSTATS_NET_RATE);
1114 }
1115}
1116
1117int MachineNetRate::enable()
1118{
1119 int rc = mCGuest->enable(VMSTATS_NET_RATE);
1120 BaseMetric::enable();
1121 return rc;
1122}
1123
1124int MachineNetRate::disable()
1125{
1126 BaseMetric::disable();
1127 return mCGuest->disable(VMSTATS_NET_RATE);
1128}
1129
1130void MachineNetRate::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1131{
1132 hints.collectGuestStats(mCGuest->getProcess());
1133}
1134
1135void GuestCpuLoad::init(ULONG period, ULONG length)
1136{
1137 mPeriod = period;
1138 mLength = length;
1139
1140 mUser->init(mLength);
1141 mKernel->init(mLength);
1142 mIdle->init(mLength);
1143}
1144
1145void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1146{
1147 hints.collectGuestStats(mCGuest->getProcess());
1148}
1149
1150void GuestCpuLoad::collect()
1151{
1152 if (mCGuest->isValid(VMSTATS_GUEST_CPULOAD))
1153 {
1154 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100);
1155 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100);
1156 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100);
1157 mCGuest->invalidate(VMSTATS_GUEST_CPULOAD);
1158 }
1159}
1160
1161int GuestCpuLoad::enable()
1162{
1163 int rc = mCGuest->enable(VMSTATS_GUEST_CPULOAD);
1164 BaseMetric::enable();
1165 return rc;
1166}
1167
1168int GuestCpuLoad::disable()
1169{
1170 BaseMetric::disable();
1171 return mCGuest->disable(VMSTATS_GUEST_CPULOAD);
1172}
1173
1174void GuestRamUsage::init(ULONG period, ULONG length)
1175{
1176 mPeriod = period;
1177 mLength = length;
1178
1179 mTotal->init(mLength);
1180 mFree->init(mLength);
1181 mBallooned->init(mLength);
1182 mShared->init(mLength);
1183 mCache->init(mLength);
1184 mPagedTotal->init(mLength);
1185}
1186
1187void GuestRamUsage::collect()
1188{
1189 if (mCGuest->isValid(VMSTATS_GUEST_RAMUSAGE))
1190 {
1191 mTotal->put(mCGuest->getMemTotal());
1192 mFree->put(mCGuest->getMemFree());
1193 mBallooned->put(mCGuest->getMemBalloon());
1194 mShared->put(mCGuest->getMemShared());
1195 mCache->put(mCGuest->getMemCache());
1196 mPagedTotal->put(mCGuest->getPageTotal());
1197 mCGuest->invalidate(VMSTATS_GUEST_RAMUSAGE);
1198 }
1199}
1200
1201int GuestRamUsage::enable()
1202{
1203 int rc = mCGuest->enable(VMSTATS_GUEST_RAMUSAGE);
1204 BaseMetric::enable();
1205 return rc;
1206}
1207
1208int GuestRamUsage::disable()
1209{
1210 BaseMetric::disable();
1211 return mCGuest->disable(VMSTATS_GUEST_RAMUSAGE);
1212}
1213
1214void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1215{
1216 hints.collectGuestStats(mCGuest->getProcess());
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