VirtualBox

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

Last change on this file since 94251 was 94088, checked in by vboxsync, 3 years ago

Main/Performace.cpp: Don't assert in CollectorHAL::getHostCpuMHz() on darwin, as RTMpGetCurFrequency isn't implemented and probably won't be any time soon. bugref:9898

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