VirtualBox

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

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

Main/Metrics: VM network rate metrics (#6345)

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette