VirtualBox

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

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

Main/Metrics: Disk and filesystem metrics for Solaris (#6345)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.7 KB
Line 
1/* $Id: Performance.cpp 43831 2012-11-07 13:11:51Z 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)
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 & GUESTSTATS_VMMRAM) == GUESTSTATS_VMMRAM)
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 & GUESTSTATS_VMMRAM) == GUESTSTATS_VMMRAM)
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(GUESTSTATS_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{
363 if ((aValidStats & GUESTSTATS_CPULOAD) == GUESTSTATS_CPULOAD)
364 {
365 mCpuUser = aCpuUser;
366 mCpuKernel = aCpuKernel,
367 mCpuIdle = aCpuIdle;
368 }
369 if ((aValidStats & GUESTSTATS_RAMUSAGE) == GUESTSTATS_RAMUSAGE)
370 {
371 mMemTotal = aMemTotal;
372 mMemFree = aMemFree;
373 mMemBalloon = aMemBalloon;
374 mMemShared = aMemShared;
375 mMemCache = aMemCache;
376 mPageTotal = aPageTotal;
377 }
378 if ((aValidStats & GUESTSTATS_VMMRAM) == GUESTSTATS_VMMRAM)
379 {
380 mAllocVMM = aAllocVMM;
381 mFreeVMM = aFreeVMM;
382 mBalloonedVMM = aBalloonedVMM;
383 mSharedVMM = aSharedVMM;
384 }
385 mValid = aValidStats;
386}
387
388CollectorGuestManager::CollectorGuestManager()
389 : mVMMStatsProvider(NULL), mGuestBeingCalled(NULL)
390{
391 int rc = RTThreadCreate(&mThread, CollectorGuestManager::requestProcessingThread,
392 this, 0, RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE,
393 "CGMgr");
394 NOREF(rc);
395 LogAleksey(("{%p} " LOG_FN_FMT ": RTThreadCreate returned %u (mThread=%p)\n",
396 this, __PRETTY_FUNCTION__, rc));
397}
398
399CollectorGuestManager::~CollectorGuestManager()
400{
401 Assert(mGuests.size() == 0);
402 int rcThread = 0;
403 int rc = enqueueRequest(new CGRQAbort());
404 if (SUCCEEDED(rc))
405 {
406 /* We wait only if we were able to put the abort request to a queue */
407 LogAleksey(("{%p} " LOG_FN_FMT ": Waiting for CGM request processing thread to stop...\n",
408 this, __PRETTY_FUNCTION__));
409 rc = RTThreadWait(mThread, 1000 /* 1 sec */, &rcThread);
410 LogAleksey(("{%p} " LOG_FN_FMT ": RTThreadWait returned %u (thread exit code: %u)\n",
411 this, __PRETTY_FUNCTION__, rc, rcThread));
412 }
413}
414
415void CollectorGuestManager::registerGuest(CollectorGuest* pGuest)
416{
417 pGuest->setManager(this);
418 mGuests.push_back(pGuest);
419 /*
420 * If no VMM stats provider was elected previously than this is our
421 * candidate.
422 */
423 if (!mVMMStatsProvider)
424 mVMMStatsProvider = pGuest;
425 LogAleksey(("{%p} " LOG_FN_FMT ": Registered guest=%p provider=%p\n",
426 this, __PRETTY_FUNCTION__, pGuest, mVMMStatsProvider));
427}
428
429void CollectorGuestManager::unregisterGuest(CollectorGuest* pGuest)
430{
431 int rc = S_OK;
432
433 LogAleksey(("{%p} " LOG_FN_FMT ": About to unregister guest=%p provider=%p\n",
434 this, __PRETTY_FUNCTION__, pGuest, mVMMStatsProvider));
435 //mGuests.remove(pGuest); => destroyUnregistered()
436 pGuest->unregister();
437 if (pGuest == mVMMStatsProvider)
438 {
439 /* This was our VMM stats provider, it is time to re-elect */
440 CollectorGuestList::iterator it;
441 /* Assume that nobody can provide VMM stats */
442 mVMMStatsProvider = NULL;
443
444 for (it = mGuests.begin(); it != mGuests.end(); it++)
445 {
446 /* Skip unregistered as they are about to be destroyed */
447 if ((*it)->isUnregistered())
448 continue;
449
450 if ((*it)->isEnabled())
451 {
452 /* Found the guest already collecting stats, elect it */
453 mVMMStatsProvider = *it;
454 rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(GUESTSTATS_VMMRAM));
455 if (FAILED(rc))
456 {
457 /* This is not a good candidate -- try to find another */
458 mVMMStatsProvider = NULL;
459 continue;
460 }
461 break;
462 }
463 }
464 if (!mVMMStatsProvider)
465 {
466 /* If nobody collects stats, take the first registered */
467 for (it = mGuests.begin(); it != mGuests.end(); it++)
468 {
469 /* Skip unregistered as they are about to be destroyed */
470 if ((*it)->isUnregistered())
471 continue;
472
473 mVMMStatsProvider = *it;
474 //mVMMStatsProvider->enable(GUESTSTATS_VMMRAM);
475 rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(GUESTSTATS_VMMRAM));
476 if (SUCCEEDED(rc))
477 break;
478 /* This was not a good candidate -- try to find another */
479 mVMMStatsProvider = NULL;
480 }
481 }
482 }
483 LogAleksey(("{%p} " LOG_FN_FMT ": LEAVE new provider=%p\n",
484 this, __PRETTY_FUNCTION__, mVMMStatsProvider));
485}
486
487void CollectorGuestManager::destroyUnregistered()
488{
489 CollectorGuestList::iterator it;
490
491 for (it = mGuests.begin(); it != mGuests.end();)
492 if ((*it)->isUnregistered())
493 {
494 delete *it;
495 it = mGuests.erase(it);
496 LogAleksey(("{%p} " LOG_FN_FMT ": Number of guests after erasing unregistered is %d\n",
497 this, __PRETTY_FUNCTION__, mGuests.size()));
498 }
499 else
500 ++it;
501}
502
503int CollectorGuestManager::enqueueRequest(CollectorGuestRequest *aRequest)
504{
505#ifdef DEBUG
506 aRequest->debugPrint(this, __PRETTY_FUNCTION__, "added to CGM queue");
507#endif /* DEBUG */
508 /*
509 * It is very unlikely that we will get high frequency calls to configure
510 * guest metrics collection, so we rely on this fact to detect blocked
511 * guests. If the guest has not finished processing the previous request
512 * after half a second we consider it blocked.
513 */
514 if (aRequest->getGuest() && aRequest->getGuest() == mGuestBeingCalled)
515 {
516 /*
517 * Before we can declare a guest blocked we need to wait for a while
518 * and then check again as it may never had a chance to process
519 * the previous request. Half a second is an eternity for processes
520 * and is barely noticable by humans.
521 */
522 LogAleksey(("{%p} " LOG_FN_FMT ": Suspecting %s is stalled. Waiting for .5 sec...\n",
523 this, __PRETTY_FUNCTION__,
524 aRequest->getGuest()->getVMName().c_str()));
525 RTThreadSleep(500 /* ms */);
526 if (aRequest->getGuest() == mGuestBeingCalled) {
527 LogAleksey(("{%p} " LOG_FN_FMT ": Request processing stalled for %s\n",
528 this, __PRETTY_FUNCTION__,
529 aRequest->getGuest()->getVMName().c_str()));
530 /* Request execution got stalled for this guest -- report an error */
531 return E_FAIL;
532 }
533 }
534 mQueue.push(aRequest);
535 return S_OK;
536}
537
538/* static */
539DECLCALLBACK(int) CollectorGuestManager::requestProcessingThread(RTTHREAD /* aThread */, void *pvUser)
540{
541 CollectorGuestRequest *pReq;
542 CollectorGuestManager *mgr = static_cast<CollectorGuestManager*>(pvUser);
543
544 HRESULT rc = S_OK;
545
546 LogAleksey(("{%p} " LOG_FN_FMT ": Starting request processing loop...\n",
547 mgr, __PRETTY_FUNCTION__));
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 LogAleksey(("{%p} " LOG_FN_FMT ": request::execute returned %u\n",
561 mgr, __PRETTY_FUNCTION__, rc));
562 }
563 LogAleksey(("{%p} " LOG_FN_FMT ": Exiting request processing loop... rc=%u\n",
564 mgr, __PRETTY_FUNCTION__, rc));
565
566 return VINF_SUCCESS;
567}
568
569
570#endif /* !VBOX_COLLECTOR_TEST_CASE */
571
572bool BaseMetric::collectorBeat(uint64_t nowAt)
573{
574 if (isEnabled())
575 {
576 if (mLastSampleTaken == 0)
577 {
578 mLastSampleTaken = nowAt;
579 Log4(("{%p} " LOG_FN_FMT ": Collecting %s for obj(%p)...\n",
580 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
581 return true;
582 }
583 /*
584 * We use low resolution timers which may fire just a little bit early.
585 * We compensate for that by jumping into the future by several
586 * milliseconds (see @bugref{6345}).
587 */
588 if (nowAt - mLastSampleTaken + PM_SAMPLER_PRECISION_MS >= mPeriod * 1000)
589 {
590 /*
591 * We don't want the beat to drift. This is why the timestamp of
592 * the last taken sample is not the actual time but the time we
593 * should have taken the measurement at.
594 */
595 mLastSampleTaken += mPeriod * 1000;
596 Log4(("{%p} " LOG_FN_FMT ": Collecting %s for obj(%p)...\n",
597 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
598 return true;
599 }
600 Log4(("{%p} " LOG_FN_FMT ": Enabled but too early to collect %s for obj(%p)\n",
601 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
602 }
603 return false;
604}
605
606void HostCpuLoad::init(ULONG period, ULONG length)
607{
608 mPeriod = period;
609 mLength = length;
610 mUser->init(mLength);
611 mKernel->init(mLength);
612 mIdle->init(mLength);
613}
614
615void HostCpuLoad::collect()
616{
617 ULONG user, kernel, idle;
618 int rc = mHAL->getHostCpuLoad(&user, &kernel, &idle);
619 if (RT_SUCCESS(rc))
620 {
621 mUser->put(user);
622 mKernel->put(kernel);
623 mIdle->put(idle);
624 }
625}
626
627void HostCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
628{
629 hints.collectHostCpuLoad();
630}
631
632void HostCpuLoadRaw::collect()
633{
634 uint64_t user, kernel, idle;
635 uint64_t userDiff, kernelDiff, idleDiff, totalDiff;
636
637 int rc = mHAL->getRawHostCpuLoad(&user, &kernel, &idle);
638 if (RT_SUCCESS(rc))
639 {
640 userDiff = user - mUserPrev;
641 kernelDiff = kernel - mKernelPrev;
642 idleDiff = idle - mIdlePrev;
643 totalDiff = userDiff + kernelDiff + idleDiff;
644
645 if (totalDiff == 0)
646 {
647 /* This is only possible if none of counters has changed! */
648 LogFlowThisFunc(("Impossible! User, kernel and idle raw "
649 "counters has not changed since last sample.\n" ));
650 mUser->put(0);
651 mKernel->put(0);
652 mIdle->put(0);
653 }
654 else
655 {
656 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff));
657 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff));
658 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff));
659 }
660
661 mUserPrev = user;
662 mKernelPrev = kernel;
663 mIdlePrev = idle;
664 }
665}
666
667void HostNetworkLoadRaw::init(ULONG period, ULONG length)
668{
669 mPeriod = period;
670 mLength = length;
671 mRx->init(mLength);
672 mTx->init(mLength);
673 int rc = mHAL->getRawHostNetworkLoad(mShortName.c_str(), &mRxPrev, &mTxPrev);
674 //AssertRC(rc);
675}
676
677void HostNetworkLoadRaw::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
678{
679 if (RT_FAILURE(mRc))
680 {
681 ComPtr<IHostNetworkInterface> networkInterface;
682 ComPtr<IHost> host = getObject();
683 HRESULT hrc = host->FindHostNetworkInterfaceByName(com::Bstr(mInterfaceName).raw(), networkInterface.asOutParam());
684 if (SUCCEEDED(hrc))
685 {
686 LogRel(("Failed to collect network metrics for %s: %Rrc (%d).\n", mInterfaceName.c_str(), mRc, mRc));
687 mRc = VINF_SUCCESS;
688 }
689 }
690}
691
692void HostNetworkLoadRaw::collect()
693{
694 uint64_t rx, tx;
695
696 mRc = mHAL->getRawHostNetworkLoad(mShortName.c_str(), &rx, &tx);
697 if (RT_SUCCESS(mRc))
698 {
699 uint64_t rxDiff = rx - mRxPrev;
700 uint64_t txDiff = tx - mTxPrev;
701
702 if (RT_UNLIKELY(mSpeed * getPeriod() == 0))
703 {
704 LogFlowThisFunc(("Check cable for %s! speed=%llu period=%d.\n", mShortName.c_str(), mSpeed, getPeriod()));
705 mRx->put(0);
706 mTx->put(0);
707 }
708 else
709 {
710 mRx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * rxDiff / (mSpeed * getPeriod())));
711 mTx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * txDiff / (mSpeed * getPeriod())));
712 }
713
714 mRxPrev = rx;
715 mTxPrev = tx;
716 }
717 else
718 LogFlowThisFunc(("Failed to collect data: %Rrc (%d)."
719 " Will update the list of interfaces...\n", mRc,mRc));
720}
721
722void HostDiskLoadRaw::init(ULONG period, ULONG length)
723{
724 mPeriod = period;
725 mLength = length;
726 mUtil->init(mLength);
727 int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &mDiskPrev, &mTotalPrev);
728 AssertRC(rc);
729}
730
731void HostDiskLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
732{
733 hints.collectHostCpuLoad();
734}
735
736void HostDiskLoadRaw::collect()
737{
738 uint64_t disk, total;
739
740 int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &disk, &total);
741 if (RT_SUCCESS(rc))
742 {
743 uint64_t diskDiff = disk - mDiskPrev;
744 uint64_t totalDiff = total - mTotalPrev;
745
746 if (RT_UNLIKELY(totalDiff == 0))
747 {
748 Assert(totalDiff);
749 LogFlowThisFunc(("Improbable! Less than millisecond passed! Disk=%s\n", mDiskName.c_str()));
750 mUtil->put(0);
751 }
752 else if (diskDiff > totalDiff)
753 {
754 /*
755 * It is possible that the disk spent more time than CPU because
756 * CPU measurements are taken during the pre-collect phase. We try
757 * to compensate for than by adding the extra to the next round of
758 * measurements.
759 */
760 mUtil->put(PM_NETWORK_LOAD_MULTIPLIER);
761 Assert((diskDiff - totalDiff) < mPeriod * 1000);
762 if ((diskDiff - totalDiff) > mPeriod * 1000)
763 {
764 LogRel(("Disk utilization time exceeds CPU time by more"
765 " than the collection period (%llu ms)\n", diskDiff - totalDiff));
766 }
767 else
768 {
769 disk = mDiskPrev + totalDiff;
770 LogFlowThisFunc(("Moved %u milliseconds to the next period.\n", (unsigned)(diskDiff - totalDiff)));
771 }
772 }
773 else
774 {
775 mUtil->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * diskDiff / totalDiff));
776 }
777
778 mDiskPrev = disk;
779 mTotalPrev = total;
780 }
781 else
782 LogFlowThisFunc(("Failed to collect data: %Rrc (%d).\n", rc));
783}
784
785void HostCpuMhz::init(ULONG period, ULONG length)
786{
787 mPeriod = period;
788 mLength = length;
789 mMHz->init(mLength);
790}
791
792void HostCpuMhz::collect()
793{
794 ULONG mhz;
795 int rc = mHAL->getHostCpuMHz(&mhz);
796 if (RT_SUCCESS(rc))
797 mMHz->put(mhz);
798}
799
800void HostRamUsage::init(ULONG period, ULONG length)
801{
802 mPeriod = period;
803 mLength = length;
804 mTotal->init(mLength);
805 mUsed->init(mLength);
806 mAvailable->init(mLength);
807}
808
809void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
810{
811 hints.collectHostRamUsage();
812}
813
814void HostRamUsage::collect()
815{
816 ULONG total, used, available;
817 int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
818 if (RT_SUCCESS(rc))
819 {
820 mTotal->put(total);
821 mUsed->put(used);
822 mAvailable->put(available);
823
824 }
825}
826
827void HostFilesystemUsage::init(ULONG period, ULONG length)
828{
829 mPeriod = period;
830 mLength = length;
831 mTotal->init(mLength);
832 mUsed->init(mLength);
833 mAvailable->init(mLength);
834}
835
836void HostFilesystemUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
837{
838}
839
840void HostFilesystemUsage::collect()
841{
842 ULONG total, used, available;
843 int rc = mHAL->getHostFilesystemUsage(mFsName.c_str(), &total, &used, &available);
844 if (RT_SUCCESS(rc))
845 {
846 mTotal->put(total);
847 mUsed->put(used);
848 mAvailable->put(available);
849
850 }
851}
852
853#ifndef VBOX_COLLECTOR_TEST_CASE
854void HostRamVmm::init(ULONG period, ULONG length)
855{
856 mPeriod = period;
857 mLength = length;
858 mAllocVMM->init(mLength);
859 mFreeVMM->init(mLength);
860 mBalloonVMM->init(mLength);
861 mSharedVMM->init(mLength);
862}
863
864int HostRamVmm::enable()
865{
866 int rc = S_OK;
867 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
868 if (provider)
869 rc = provider->enable(GUESTSTATS_VMMRAM);
870 BaseMetric::enable();
871 return rc;
872}
873
874int HostRamVmm::disable()
875{
876 int rc = S_OK;
877 BaseMetric::disable();
878 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
879 if (provider)
880 rc = provider->disable(GUESTSTATS_VMMRAM);
881 return rc;
882}
883
884void HostRamVmm::preCollect(CollectorHints& hints, uint64_t /* iTick */)
885{
886 hints.collectHostRamVmm();
887}
888
889void HostRamVmm::collect()
890{
891 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
892 if (provider)
893 {
894 LogAleksey(("{%p} " LOG_FN_FMT ": provider=%p enabled=%s valid=%s...\n",
895 this, __PRETTY_FUNCTION__, provider, provider->isEnabled()?"y":"n",
896 provider->isValid(GUESTSTATS_VMMRAM)?"y":"n"));
897 if (provider->isValid(GUESTSTATS_VMMRAM))
898 {
899 /* Provider is ready, get updated stats */
900 mAllocCurrent = provider->getAllocVMM();
901 mFreeCurrent = provider->getFreeVMM();
902 mBalloonedCurrent = provider->getBalloonedVMM();
903 mSharedCurrent = provider->getSharedVMM();
904 provider->invalidate(GUESTSTATS_VMMRAM);
905 }
906 /*
907 * Note that if there are no new values from the provider we will use
908 * the ones most recently provided instead of zeros, which is probably
909 * a desirable behavior.
910 */
911 }
912 else
913 {
914 mAllocCurrent = 0;
915 mFreeCurrent = 0;
916 mBalloonedCurrent = 0;
917 mSharedCurrent = 0;
918 }
919 LogAleksey(("{%p} " LOG_FN_FMT ": mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
920 this, __PRETTY_FUNCTION__,
921 mAllocCurrent, mFreeCurrent, mBalloonedCurrent, mSharedCurrent));
922 mAllocVMM->put(mAllocCurrent);
923 mFreeVMM->put(mFreeCurrent);
924 mBalloonVMM->put(mBalloonedCurrent);
925 mSharedVMM->put(mSharedCurrent);
926}
927#endif /* !VBOX_COLLECTOR_TEST_CASE */
928
929
930
931void MachineCpuLoad::init(ULONG period, ULONG length)
932{
933 mPeriod = period;
934 mLength = length;
935 mUser->init(mLength);
936 mKernel->init(mLength);
937}
938
939void MachineCpuLoad::collect()
940{
941 ULONG user, kernel;
942 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
943 if (RT_SUCCESS(rc))
944 {
945 mUser->put(user);
946 mKernel->put(kernel);
947 }
948}
949
950void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
951{
952 hints.collectProcessCpuLoad(mProcess);
953}
954
955void MachineCpuLoadRaw::collect()
956{
957 uint64_t processUser, processKernel, hostTotal;
958
959 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
960 if (RT_SUCCESS(rc))
961 {
962 if (hostTotal == mHostTotalPrev)
963 {
964 /* Nearly impossible, but... */
965 mUser->put(0);
966 mKernel->put(0);
967 }
968 else
969 {
970 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
971 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
972 }
973
974 mHostTotalPrev = hostTotal;
975 mProcessUserPrev = processUser;
976 mProcessKernelPrev = processKernel;
977 }
978}
979
980void MachineRamUsage::init(ULONG period, ULONG length)
981{
982 mPeriod = period;
983 mLength = length;
984 mUsed->init(mLength);
985}
986
987void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
988{
989 hints.collectProcessRamUsage(mProcess);
990}
991
992void MachineRamUsage::collect()
993{
994 ULONG used;
995 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
996 if (RT_SUCCESS(rc))
997 mUsed->put(used);
998}
999
1000
1001#ifndef VBOX_COLLECTOR_TEST_CASE
1002void GuestCpuLoad::init(ULONG period, ULONG length)
1003{
1004 mPeriod = period;
1005 mLength = length;
1006
1007 mUser->init(mLength);
1008 mKernel->init(mLength);
1009 mIdle->init(mLength);
1010}
1011
1012void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1013{
1014 hints.collectGuestStats(mCGuest->getProcess());
1015}
1016
1017void GuestCpuLoad::collect()
1018{
1019 if (mCGuest->isValid(GUESTSTATS_CPULOAD))
1020 {
1021 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100);
1022 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100);
1023 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100);
1024 mCGuest->invalidate(GUESTSTATS_CPULOAD);
1025 }
1026}
1027
1028int GuestCpuLoad::enable()
1029{
1030 int rc = mCGuest->enable(GUESTSTATS_CPULOAD);
1031 BaseMetric::enable();
1032 return rc;
1033}
1034
1035int GuestCpuLoad::disable()
1036{
1037 BaseMetric::disable();
1038 return mCGuest->disable(GUESTSTATS_CPULOAD);
1039}
1040
1041void GuestRamUsage::init(ULONG period, ULONG length)
1042{
1043 mPeriod = period;
1044 mLength = length;
1045
1046 mTotal->init(mLength);
1047 mFree->init(mLength);
1048 mBallooned->init(mLength);
1049 mShared->init(mLength);
1050 mCache->init(mLength);
1051 mPagedTotal->init(mLength);
1052}
1053
1054void GuestRamUsage::collect()
1055{
1056 if (mCGuest->isValid(GUESTSTATS_RAMUSAGE))
1057 {
1058 mTotal->put(mCGuest->getMemTotal());
1059 mFree->put(mCGuest->getMemFree());
1060 mBallooned->put(mCGuest->getMemBalloon());
1061 mShared->put(mCGuest->getMemShared());
1062 mCache->put(mCGuest->getMemCache());
1063 mPagedTotal->put(mCGuest->getPageTotal());
1064 mCGuest->invalidate(GUESTSTATS_RAMUSAGE);
1065 }
1066}
1067
1068int GuestRamUsage::enable()
1069{
1070 int rc = mCGuest->enable(GUESTSTATS_RAMUSAGE);
1071 BaseMetric::enable();
1072 return rc;
1073}
1074
1075int GuestRamUsage::disable()
1076{
1077 BaseMetric::disable();
1078 return mCGuest->disable(GUESTSTATS_RAMUSAGE);
1079}
1080
1081void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1082{
1083 hints.collectGuestStats(mCGuest->getProcess());
1084}
1085#endif /* !VBOX_COLLECTOR_TEST_CASE */
1086
1087void CircularBuffer::init(ULONG ulLength)
1088{
1089 if (mData)
1090 RTMemFree(mData);
1091 mLength = ulLength;
1092 if (mLength)
1093 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
1094 else
1095 mData = NULL;
1096 mWrapped = false;
1097 mEnd = 0;
1098 mSequenceNumber = 0;
1099}
1100
1101ULONG CircularBuffer::length()
1102{
1103 return mWrapped ? mLength : mEnd;
1104}
1105
1106void CircularBuffer::put(ULONG value)
1107{
1108 if (mData)
1109 {
1110 mData[mEnd++] = value;
1111 if (mEnd >= mLength)
1112 {
1113 mEnd = 0;
1114 mWrapped = true;
1115 }
1116 ++mSequenceNumber;
1117 }
1118}
1119
1120void CircularBuffer::copyTo(ULONG *data)
1121{
1122 if (mWrapped)
1123 {
1124 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
1125 // Copy the wrapped part
1126 if (mEnd)
1127 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
1128 }
1129 else
1130 memcpy(data, mData, mEnd * sizeof(ULONG));
1131}
1132
1133void SubMetric::query(ULONG *data)
1134{
1135 copyTo(data);
1136}
1137
1138void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
1139{
1140 ULONG length;
1141 ULONG *tmpData;
1142
1143 length = mSubMetric->length();
1144 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
1145 if (length)
1146 {
1147 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
1148 mSubMetric->query(tmpData);
1149 if (mAggregate)
1150 {
1151 *count = 1;
1152 *data = (ULONG*)RTMemAlloc(sizeof(**data));
1153 **data = mAggregate->compute(tmpData, length);
1154 RTMemFree(tmpData);
1155 }
1156 else
1157 {
1158 *count = length;
1159 *data = tmpData;
1160 }
1161 }
1162 else
1163 {
1164 *count = 0;
1165 *data = 0;
1166 }
1167}
1168
1169ULONG AggregateAvg::compute(ULONG *data, ULONG length)
1170{
1171 uint64_t tmp = 0;
1172 for (ULONG i = 0; i < length; ++i)
1173 tmp += data[i];
1174 return (ULONG)(tmp / length);
1175}
1176
1177const char * AggregateAvg::getName()
1178{
1179 return "avg";
1180}
1181
1182ULONG AggregateMin::compute(ULONG *data, ULONG length)
1183{
1184 ULONG tmp = *data;
1185 for (ULONG i = 0; i < length; ++i)
1186 if (data[i] < tmp)
1187 tmp = data[i];
1188 return tmp;
1189}
1190
1191const char * AggregateMin::getName()
1192{
1193 return "min";
1194}
1195
1196ULONG AggregateMax::compute(ULONG *data, ULONG length)
1197{
1198 ULONG tmp = *data;
1199 for (ULONG i = 0; i < length; ++i)
1200 if (data[i] > tmp)
1201 tmp = data[i];
1202 return tmp;
1203}
1204
1205const char * AggregateMax::getName()
1206{
1207 return "max";
1208}
1209
1210Filter::Filter(ComSafeArrayIn(IN_BSTR, metricNames),
1211 ComSafeArrayIn(IUnknown *, objects))
1212{
1213 /*
1214 * Let's work around null/empty safe array mess. I am not sure there is
1215 * a way to pass null arrays via webservice, I haven't found one. So I
1216 * guess the users will be forced to use empty arrays instead. Constructing
1217 * an empty SafeArray is a bit awkward, so what we do in this method is
1218 * actually convert null arrays to empty arrays and pass them down to
1219 * init() method. If someone knows how to do it better, please be my guest,
1220 * fix it.
1221 */
1222 if (ComSafeArrayInIsNull(metricNames))
1223 {
1224 com::SafeArray<BSTR> nameArray;
1225 if (ComSafeArrayInIsNull(objects))
1226 {
1227 com::SafeIfaceArray<IUnknown> objectArray;
1228 objectArray.reset(0);
1229 init(ComSafeArrayAsInParam(nameArray),
1230 ComSafeArrayAsInParam(objectArray));
1231 }
1232 else
1233 {
1234 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1235 init(ComSafeArrayAsInParam(nameArray),
1236 ComSafeArrayAsInParam(objectArray));
1237 }
1238 }
1239 else
1240 {
1241 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1242 if (ComSafeArrayInIsNull(objects))
1243 {
1244 com::SafeIfaceArray<IUnknown> objectArray;
1245 objectArray.reset(0);
1246 init(ComSafeArrayAsInParam(nameArray),
1247 ComSafeArrayAsInParam(objectArray));
1248 }
1249 else
1250 {
1251 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1252 init(ComSafeArrayAsInParam(nameArray),
1253 ComSafeArrayAsInParam(objectArray));
1254 }
1255 }
1256}
1257
1258Filter::Filter(const com::Utf8Str name, const ComPtr<IUnknown> &aObject)
1259{
1260 processMetricList(name, aObject);
1261}
1262
1263void Filter::init(ComSafeArrayIn(IN_BSTR, metricNames),
1264 ComSafeArrayIn(IUnknown *, objects))
1265{
1266 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1267 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1268
1269 if (!objectArray.size())
1270 {
1271 if (nameArray.size())
1272 {
1273 for (size_t i = 0; i < nameArray.size(); ++i)
1274 processMetricList(com::Utf8Str(nameArray[i]), ComPtr<IUnknown>());
1275 }
1276 else
1277 processMetricList("*", ComPtr<IUnknown>());
1278 }
1279 else
1280 {
1281 for (size_t i = 0; i < objectArray.size(); ++i)
1282 switch (nameArray.size())
1283 {
1284 case 0:
1285 processMetricList("*", objectArray[i]);
1286 break;
1287 case 1:
1288 processMetricList(com::Utf8Str(nameArray[0]), objectArray[i]);
1289 break;
1290 default:
1291 processMetricList(com::Utf8Str(nameArray[i]), objectArray[i]);
1292 break;
1293 }
1294 }
1295}
1296
1297void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
1298{
1299 size_t startPos = 0;
1300
1301 for (size_t pos = name.find(",");
1302 pos != com::Utf8Str::npos;
1303 pos = name.find(",", startPos))
1304 {
1305 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos, pos - startPos).c_str())));
1306 startPos = pos + 1;
1307 }
1308 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos).c_str())));
1309}
1310
1311/**
1312 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
1313 * modified to handle the special case of trailing colon in the pattern.
1314 *
1315 * @returns True if matches, false if not.
1316 * @param pszPat Pattern.
1317 * @param pszName Name to match against the pattern.
1318 * @param fSeenColon Seen colon (':').
1319 */
1320bool Filter::patternMatch(const char *pszPat, const char *pszName,
1321 bool fSeenColon)
1322{
1323 /* ASSUMES ASCII */
1324 for (;;)
1325 {
1326 char chPat = *pszPat;
1327 switch (chPat)
1328 {
1329 default:
1330 if (*pszName != chPat)
1331 return false;
1332 break;
1333
1334 case '*':
1335 {
1336 while ((chPat = *++pszPat) == '*' || chPat == '?')
1337 /* nothing */;
1338
1339 /* Handle a special case, the mask terminating with a colon. */
1340 if (chPat == ':')
1341 {
1342 if (!fSeenColon && !pszPat[1])
1343 return !strchr(pszName, ':');
1344 fSeenColon = true;
1345 }
1346
1347 for (;;)
1348 {
1349 char ch = *pszName++;
1350 if ( ch == chPat
1351 && ( !chPat
1352 || patternMatch(pszPat + 1, pszName, fSeenColon)))
1353 return true;
1354 if (!ch)
1355 return false;
1356 }
1357 /* won't ever get here */
1358 break;
1359 }
1360
1361 case '?':
1362 if (!*pszName)
1363 return false;
1364 break;
1365
1366 /* Handle a special case, the mask terminating with a colon. */
1367 case ':':
1368 if (!fSeenColon && !pszPat[1])
1369 return !*pszName;
1370 if (*pszName != ':')
1371 return false;
1372 fSeenColon = true;
1373 break;
1374
1375 case '\0':
1376 return !*pszName;
1377 }
1378 pszName++;
1379 pszPat++;
1380 }
1381 return true;
1382}
1383
1384bool Filter::match(const ComPtr<IUnknown> object, const RTCString &name) const
1385{
1386 ElementList::const_iterator it;
1387
1388 //LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
1389 for (it = mElements.begin(); it != mElements.end(); it++)
1390 {
1391 //LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
1392 if ((*it).first.isNull() || (*it).first == object)
1393 {
1394 // Objects match, compare names
1395 if (patternMatch((*it).second.c_str(), name.c_str()))
1396 {
1397 //LogFlowThisFunc(("...found!\n"));
1398 return true;
1399 }
1400 }
1401 }
1402 //LogAleksey(("...no matches!\n"));
1403 return false;
1404}
1405/* 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