VirtualBox

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

Last change on this file since 86714 was 85269, checked in by vboxsync, 4 years ago

Main/Performance.cpp/h: A whole bunch of int/HRESULT mixups. [noref fix] bugref:9790

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