VirtualBox

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

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

Main/Metrics: Linux fs/disk metrics, VBoxManage filtering + minor fixes (#6345)

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