VirtualBox

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

Last change on this file since 74897 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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