VirtualBox

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

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

Main: Compile fixes when VBOX_WITH_HOSTNETIF_API is not defined.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.1 KB
Line 
1/* $Id: Performance.cpp 94722 2022-04-27 13:42:50Z 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# ifdef VBOX_WITH_HOSTNETIF_API
681 NETIFSTATUS enmState = NETIF_S_UNKNOWN;
682 int vrc = NetIfGetState(szShortName, &enmState);
683 if (RT_FAILURE(vrc))
684 return false;
685 if (enmState != NETIF_S_UP)
686 *pSpeed = 0;
687 else
688 {
689 vrc = NetIfGetLinkSpeed(szShortName, pSpeed);
690 if (RT_FAILURE(vrc))
691 return false;
692 }
693 return true;
694# else /* !VBOX_WITH_HOSTNETIF_API */
695 RT_NOREF(szShortName, pSpeed);
696 return false;
697# endif /* VBOX_WITH_HOSTNETIF_API */
698}
699
700void HostNetworkSpeed::init(ULONG period, ULONG length)
701{
702 mPeriod = period;
703 mLength = length;
704 mLinkSpeed->init(length);
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 getLinkSpeed(mShortName.c_str(), &mSpeed);
710}
711
712void HostNetworkLoadRaw::init(ULONG period, ULONG length)
713{
714 mPeriod = period;
715 mLength = length;
716 mRx->init(mLength);
717 mTx->init(mLength);
718 /*
719 * Retrieve the link speed now as it may be wrong if the metric was
720 * registered at boot (see @bugref{6613}).
721 */
722 uint32_t uSpeedMbit = 65535;
723 if (getLinkSpeed(mShortName.c_str(), &uSpeedMbit))
724 mSpeed = (uint64_t)uSpeedMbit * (1000000/8); /* Convert to bytes/sec */
725 /*int vrc =*/ mHAL->getRawHostNetworkLoad(mShortName.c_str(), &mRxPrev, &mTxPrev);
726 //AssertRC(vrc);
727}
728
729void HostNetworkLoadRaw::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
730{
731 if (RT_FAILURE(mRc))
732 {
733 ComPtr<IHostNetworkInterface> networkInterface;
734 ComPtr<IHost> host = getObject();
735 HRESULT hrc = host->FindHostNetworkInterfaceByName(com::Bstr(mInterfaceName).raw(), networkInterface.asOutParam());
736 if (SUCCEEDED(hrc))
737 {
738 static uint32_t s_tsLogRelLast;
739 uint32_t tsNow = RTTimeProgramSecTS();
740 if ( tsNow < RT_SEC_1HOUR
741 || (tsNow - s_tsLogRelLast >= 60))
742 {
743 s_tsLogRelLast = tsNow;
744 LogRel(("Failed to collect network metrics for %s: %Rrc (%d). Max one msg/min.\n", mInterfaceName.c_str(), mRc, mRc));
745 }
746 mRc = VINF_SUCCESS;
747 }
748 }
749}
750
751void HostNetworkLoadRaw::collect()
752{
753 uint64_t rx = mRxPrev;
754 uint64_t tx = mTxPrev;
755
756 if (RT_UNLIKELY(mSpeed * getPeriod() == 0))
757 {
758 LogFlowThisFunc(("Check cable for %s! speed=%llu period=%d.\n", mShortName.c_str(), mSpeed, getPeriod()));
759 /* We do not collect host network metrics for unplugged interfaces! */
760 return;
761 }
762 mRc = mHAL->getRawHostNetworkLoad(mShortName.c_str(), &rx, &tx);
763 if (RT_SUCCESS(mRc))
764 {
765 uint64_t rxDiff = rx - mRxPrev;
766 uint64_t txDiff = tx - mTxPrev;
767
768 mRx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * rxDiff / (mSpeed * getPeriod())));
769 mTx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * txDiff / (mSpeed * getPeriod())));
770
771 mRxPrev = rx;
772 mTxPrev = tx;
773 }
774 else
775 LogFlowThisFunc(("Failed to collect data: %Rrc (%d)."
776 " Will update the list of interfaces...\n", mRc,mRc));
777}
778#endif /* !VBOX_COLLECTOR_TEST_CASE */
779
780void HostDiskLoadRaw::init(ULONG period, ULONG length)
781{
782 mPeriod = period;
783 mLength = length;
784 mUtil->init(mLength);
785 int vrc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &mDiskPrev, &mTotalPrev);
786 AssertRC(vrc);
787}
788
789void HostDiskLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
790{
791 hints.collectHostCpuLoad();
792}
793
794void HostDiskLoadRaw::collect()
795{
796 uint64_t disk, total;
797
798 int vrc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &disk, &total);
799 if (RT_SUCCESS(vrc))
800 {
801 uint64_t diskDiff = disk - mDiskPrev;
802 uint64_t totalDiff = total - mTotalPrev;
803
804 if (RT_UNLIKELY(totalDiff == 0))
805 {
806 Assert(totalDiff);
807 LogFlowThisFunc(("Improbable! Less than millisecond passed! Disk=%s\n", mDiskName.c_str()));
808 mUtil->put(0);
809 }
810 else if (diskDiff > totalDiff)
811 {
812 /*
813 * It is possible that the disk spent more time than CPU because
814 * CPU measurements are taken during the pre-collect phase. We try
815 * to compensate for than by adding the extra to the next round of
816 * measurements.
817 */
818 mUtil->put(PM_NETWORK_LOAD_MULTIPLIER);
819 Assert((diskDiff - totalDiff) < mPeriod * 1000);
820 if ((diskDiff - totalDiff) > mPeriod * 1000)
821 {
822 LogRel(("Disk utilization time exceeds CPU time by more"
823 " than the collection period (%llu ms)\n", diskDiff - totalDiff));
824 }
825 else
826 {
827 disk = mDiskPrev + totalDiff;
828 LogFlowThisFunc(("Moved %u milliseconds to the next period.\n", (unsigned)(diskDiff - totalDiff)));
829 }
830 }
831 else
832 {
833 mUtil->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * diskDiff / totalDiff));
834 }
835
836 mDiskPrev = disk;
837 mTotalPrev = total;
838 }
839 else
840 LogFlowThisFunc(("Failed to collect data: %Rrc (%d)\n", vrc, vrc));
841}
842
843void HostCpuMhz::init(ULONG period, ULONG length)
844{
845 mPeriod = period;
846 mLength = length;
847 mMHz->init(mLength);
848}
849
850void HostCpuMhz::collect()
851{
852 ULONG mhz;
853 int vrc = mHAL->getHostCpuMHz(&mhz);
854 if (RT_SUCCESS(vrc))
855 mMHz->put(mhz);
856}
857
858void HostRamUsage::init(ULONG period, ULONG length)
859{
860 mPeriod = period;
861 mLength = length;
862 mTotal->init(mLength);
863 mUsed->init(mLength);
864 mAvailable->init(mLength);
865}
866
867void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
868{
869 hints.collectHostRamUsage();
870}
871
872void HostRamUsage::collect()
873{
874 ULONG total, used, available;
875 int vrc = mHAL->getHostMemoryUsage(&total, &used, &available);
876 if (RT_SUCCESS(vrc))
877 {
878 mTotal->put(total);
879 mUsed->put(used);
880 mAvailable->put(available);
881 }
882}
883
884void HostFilesystemUsage::init(ULONG period, ULONG length)
885{
886 mPeriod = period;
887 mLength = length;
888 mTotal->init(mLength);
889 mUsed->init(mLength);
890 mAvailable->init(mLength);
891}
892
893void HostFilesystemUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
894{
895}
896
897void HostFilesystemUsage::collect()
898{
899 ULONG total, used, available;
900 int vrc = mHAL->getHostFilesystemUsage(mFsName.c_str(), &total, &used, &available);
901 if (RT_SUCCESS(vrc))
902 {
903 mTotal->put(total);
904 mUsed->put(used);
905 mAvailable->put(available);
906 }
907}
908
909void HostDiskUsage::init(ULONG period, ULONG length)
910{
911 mPeriod = period;
912 mLength = length;
913 mTotal->init(mLength);
914}
915
916void HostDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
917{
918}
919
920void HostDiskUsage::collect()
921{
922 uint64_t total;
923 int vrc = mHAL->getHostDiskSize(mDiskName.c_str(), &total);
924 if (RT_SUCCESS(vrc))
925 mTotal->put((ULONG)(total / _1M));
926}
927
928#ifndef VBOX_COLLECTOR_TEST_CASE
929
930void HostRamVmm::init(ULONG period, ULONG length)
931{
932 mPeriod = period;
933 mLength = length;
934 mAllocVMM->init(mLength);
935 mFreeVMM->init(mLength);
936 mBalloonVMM->init(mLength);
937 mSharedVMM->init(mLength);
938}
939
940HRESULT HostRamVmm::enable()
941{
942 HRESULT hrc = S_OK;
943 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
944 if (provider)
945 hrc = provider->enable(VMSTATS_VMM_RAM);
946 BaseMetric::enable();
947 return hrc;
948}
949
950HRESULT HostRamVmm::disable()
951{
952 HRESULT rc = S_OK;
953 BaseMetric::disable();
954 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
955 if (provider)
956 rc = provider->disable(VMSTATS_VMM_RAM);
957 return rc;
958}
959
960void HostRamVmm::preCollect(CollectorHints& hints, uint64_t /* iTick */)
961{
962 hints.collectHostRamVmm();
963}
964
965void HostRamVmm::collect()
966{
967 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
968 if (provider)
969 {
970 Log7Func(("{%p}: provider=%p enabled=%RTbool valid=%RTbool...\n",
971 this, provider, provider->isEnabled(), provider->isValid(VMSTATS_VMM_RAM) ));
972 if (provider->isValid(VMSTATS_VMM_RAM))
973 {
974 /* Provider is ready, get updated stats */
975 mAllocCurrent = provider->getAllocVMM();
976 mFreeCurrent = provider->getFreeVMM();
977 mBalloonedCurrent = provider->getBalloonedVMM();
978 mSharedCurrent = provider->getSharedVMM();
979 provider->invalidate(VMSTATS_VMM_RAM);
980 }
981 /*
982 * Note that if there are no new values from the provider we will use
983 * the ones most recently provided instead of zeros, which is probably
984 * a desirable behavior.
985 */
986 }
987 else
988 {
989 mAllocCurrent = 0;
990 mFreeCurrent = 0;
991 mBalloonedCurrent = 0;
992 mSharedCurrent = 0;
993 }
994 Log7Func(("{%p}: mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
995 this, mAllocCurrent, mFreeCurrent, mBalloonedCurrent, mSharedCurrent));
996 mAllocVMM->put(mAllocCurrent);
997 mFreeVMM->put(mFreeCurrent);
998 mBalloonVMM->put(mBalloonedCurrent);
999 mSharedVMM->put(mSharedCurrent);
1000}
1001
1002#endif /* !VBOX_COLLECTOR_TEST_CASE */
1003
1004
1005
1006void MachineCpuLoad::init(ULONG period, ULONG length)
1007{
1008 mPeriod = period;
1009 mLength = length;
1010 mUser->init(mLength);
1011 mKernel->init(mLength);
1012}
1013
1014void MachineCpuLoad::collect()
1015{
1016 ULONG user, kernel;
1017 int vrc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
1018 if (RT_SUCCESS(vrc))
1019 {
1020 mUser->put(user);
1021 mKernel->put(kernel);
1022 }
1023}
1024
1025void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1026{
1027 hints.collectProcessCpuLoad(mProcess);
1028}
1029
1030void MachineCpuLoadRaw::collect()
1031{
1032 uint64_t processUser, processKernel, hostTotal;
1033
1034 int vrc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
1035 if (RT_SUCCESS(vrc))
1036 {
1037 if (hostTotal == mHostTotalPrev)
1038 {
1039 /* Nearly impossible, but... */
1040 mUser->put(0);
1041 mKernel->put(0);
1042 }
1043 else
1044 {
1045 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
1046 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
1047 }
1048
1049 mHostTotalPrev = hostTotal;
1050 mProcessUserPrev = processUser;
1051 mProcessKernelPrev = processKernel;
1052 }
1053}
1054
1055void MachineRamUsage::init(ULONG period, ULONG length)
1056{
1057 mPeriod = period;
1058 mLength = length;
1059 mUsed->init(mLength);
1060}
1061
1062void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1063{
1064 hints.collectProcessRamUsage(mProcess);
1065}
1066
1067void MachineRamUsage::collect()
1068{
1069 ULONG used;
1070 int vrc = mHAL->getProcessMemoryUsage(mProcess, &used);
1071 if (RT_SUCCESS(vrc))
1072 mUsed->put(used);
1073}
1074
1075
1076#ifndef VBOX_COLLECTOR_TEST_CASE
1077
1078void MachineDiskUsage::init(ULONG period, ULONG length)
1079{
1080 mPeriod = period;
1081 mLength = length;
1082 mUsed->init(mLength);
1083}
1084
1085void MachineDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
1086{
1087}
1088
1089void MachineDiskUsage::collect()
1090{
1091 ULONG used = 0;
1092
1093 for (MediaList::iterator it = mDisks.begin(); it != mDisks.end(); ++it)
1094 {
1095 ComObjPtr<Medium> pMedium = *it;
1096
1097 /* just in case */
1098 AssertContinue(!pMedium.isNull());
1099
1100 AutoCaller localAutoCaller(pMedium);
1101 if (FAILED(localAutoCaller.rc())) continue;
1102
1103 AutoReadLock local_alock(pMedium COMMA_LOCKVAL_SRC_POS);
1104
1105 used += (ULONG)(pMedium->i_getSize() / _1M);
1106 }
1107
1108 mUsed->put(used);
1109}
1110
1111void MachineNetRate::init(ULONG period, ULONG length)
1112{
1113 mPeriod = period;
1114 mLength = length;
1115
1116 mRx->init(mLength);
1117 mTx->init(mLength);
1118}
1119
1120void MachineNetRate::collect()
1121{
1122 if (mCGuest->isValid(VMSTATS_NET_RATE))
1123 {
1124 mRx->put(mCGuest->getVmNetRx());
1125 mTx->put(mCGuest->getVmNetTx());
1126 mCGuest->invalidate(VMSTATS_NET_RATE);
1127 }
1128}
1129
1130HRESULT MachineNetRate::enable()
1131{
1132 HRESULT rc = mCGuest->enable(VMSTATS_NET_RATE);
1133 BaseMetric::enable();
1134 return rc;
1135}
1136
1137HRESULT MachineNetRate::disable()
1138{
1139 BaseMetric::disable();
1140 return mCGuest->disable(VMSTATS_NET_RATE);
1141}
1142
1143void MachineNetRate::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1144{
1145 hints.collectGuestStats(mCGuest->getProcess());
1146}
1147
1148void GuestCpuLoad::init(ULONG period, ULONG length)
1149{
1150 mPeriod = period;
1151 mLength = length;
1152
1153 mUser->init(mLength);
1154 mKernel->init(mLength);
1155 mIdle->init(mLength);
1156}
1157
1158void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1159{
1160 hints.collectGuestStats(mCGuest->getProcess());
1161}
1162
1163void GuestCpuLoad::collect()
1164{
1165 if (mCGuest->isValid(VMSTATS_GUEST_CPULOAD))
1166 {
1167 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100);
1168 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100);
1169 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100);
1170 mCGuest->invalidate(VMSTATS_GUEST_CPULOAD);
1171 }
1172}
1173
1174HRESULT GuestCpuLoad::enable()
1175{
1176 HRESULT rc = mCGuest->enable(VMSTATS_GUEST_CPULOAD);
1177 BaseMetric::enable();
1178 return rc;
1179}
1180
1181HRESULT GuestCpuLoad::disable()
1182{
1183 BaseMetric::disable();
1184 return mCGuest->disable(VMSTATS_GUEST_CPULOAD);
1185}
1186
1187void GuestRamUsage::init(ULONG period, ULONG length)
1188{
1189 mPeriod = period;
1190 mLength = length;
1191
1192 mTotal->init(mLength);
1193 mFree->init(mLength);
1194 mBallooned->init(mLength);
1195 mShared->init(mLength);
1196 mCache->init(mLength);
1197 mPagedTotal->init(mLength);
1198}
1199
1200void GuestRamUsage::collect()
1201{
1202 if (mCGuest->isValid(VMSTATS_GUEST_RAMUSAGE))
1203 {
1204 mTotal->put(mCGuest->getMemTotal());
1205 mFree->put(mCGuest->getMemFree());
1206 mBallooned->put(mCGuest->getMemBalloon());
1207 mShared->put(mCGuest->getMemShared());
1208 mCache->put(mCGuest->getMemCache());
1209 mPagedTotal->put(mCGuest->getPageTotal());
1210 mCGuest->invalidate(VMSTATS_GUEST_RAMUSAGE);
1211 }
1212}
1213
1214HRESULT GuestRamUsage::enable()
1215{
1216 HRESULT rc = mCGuest->enable(VMSTATS_GUEST_RAMUSAGE);
1217 BaseMetric::enable();
1218 return rc;
1219}
1220
1221HRESULT GuestRamUsage::disable()
1222{
1223 BaseMetric::disable();
1224 return mCGuest->disable(VMSTATS_GUEST_RAMUSAGE);
1225}
1226
1227void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
1228{
1229 hints.collectGuestStats(mCGuest->getProcess());
1230}
1231
1232#endif /* !VBOX_COLLECTOR_TEST_CASE */
1233
1234void CircularBuffer::init(ULONG ulLength)
1235{
1236 if (mData)
1237 RTMemFree(mData);
1238 mLength = ulLength;
1239 if (mLength)
1240 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
1241 else
1242 mData = NULL;
1243 mWrapped = false;
1244 mEnd = 0;
1245 mSequenceNumber = 0;
1246}
1247
1248ULONG CircularBuffer::length()
1249{
1250 return mWrapped ? mLength : mEnd;
1251}
1252
1253void CircularBuffer::put(ULONG value)
1254{
1255 if (mData)
1256 {
1257 mData[mEnd++] = value;
1258 if (mEnd >= mLength)
1259 {
1260 mEnd = 0;
1261 mWrapped = true;
1262 }
1263 ++mSequenceNumber;
1264 }
1265}
1266
1267void CircularBuffer::copyTo(ULONG *data)
1268{
1269 if (mWrapped)
1270 {
1271 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
1272 // Copy the wrapped part
1273 if (mEnd)
1274 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
1275 }
1276 else
1277 memcpy(data, mData, mEnd * sizeof(ULONG));
1278}
1279
1280void SubMetric::query(ULONG *data)
1281{
1282 copyTo(data);
1283}
1284
1285void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
1286{
1287 ULONG length;
1288 ULONG *tmpData;
1289
1290 length = mSubMetric->length();
1291 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
1292 if (length)
1293 {
1294 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
1295 mSubMetric->query(tmpData);
1296 if (mAggregate)
1297 {
1298 *count = 1;
1299 *data = (ULONG*)RTMemAlloc(sizeof(**data));
1300 **data = mAggregate->compute(tmpData, length);
1301 RTMemFree(tmpData);
1302 }
1303 else
1304 {
1305 *count = length;
1306 *data = tmpData;
1307 }
1308 }
1309 else
1310 {
1311 *count = 0;
1312 *data = 0;
1313 }
1314}
1315
1316ULONG AggregateAvg::compute(ULONG *data, ULONG length)
1317{
1318 uint64_t tmp = 0;
1319 for (ULONG i = 0; i < length; ++i)
1320 tmp += data[i];
1321 return (ULONG)(tmp / length);
1322}
1323
1324const char * AggregateAvg::getName()
1325{
1326 return "avg";
1327}
1328
1329ULONG AggregateMin::compute(ULONG *data, ULONG length)
1330{
1331 ULONG tmp = *data;
1332 for (ULONG i = 0; i < length; ++i)
1333 if (data[i] < tmp)
1334 tmp = data[i];
1335 return tmp;
1336}
1337
1338const char * AggregateMin::getName()
1339{
1340 return "min";
1341}
1342
1343ULONG AggregateMax::compute(ULONG *data, ULONG length)
1344{
1345 ULONG tmp = *data;
1346 for (ULONG i = 0; i < length; ++i)
1347 if (data[i] > tmp)
1348 tmp = data[i];
1349 return tmp;
1350}
1351
1352const char * AggregateMax::getName()
1353{
1354 return "max";
1355}
1356
1357Filter::Filter(const std::vector<com::Utf8Str> &metricNames,
1358 const std::vector<ComPtr<IUnknown> > &objects)
1359{
1360 if (!objects.size())
1361 {
1362 if (metricNames.size())
1363 {
1364 for (size_t i = 0; i < metricNames.size(); ++i)
1365 processMetricList(metricNames[i], ComPtr<IUnknown>());
1366 }
1367 else
1368 processMetricList("*", ComPtr<IUnknown>());
1369 }
1370 else
1371 {
1372 for (size_t i = 0; i < objects.size(); ++i)
1373 switch (metricNames.size())
1374 {
1375 case 0:
1376 processMetricList("*", objects[i]);
1377 break;
1378 case 1:
1379 processMetricList(metricNames[0], objects[i]);
1380 break;
1381 default:
1382 processMetricList(metricNames[i], objects[i]);
1383 break;
1384 }
1385 }
1386}
1387
1388Filter::Filter(const com::Utf8Str &name, const ComPtr<IUnknown> &aObject)
1389{
1390 processMetricList(name, aObject);
1391}
1392
1393void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
1394{
1395 size_t startPos = 0;
1396
1397 for (size_t pos = name.find(",");
1398 pos != com::Utf8Str::npos;
1399 pos = name.find(",", startPos))
1400 {
1401 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos, pos - startPos).c_str())));
1402 startPos = pos + 1;
1403 }
1404 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos).c_str())));
1405}
1406
1407/**
1408 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
1409 * modified to handle the special case of trailing colon in the pattern.
1410 *
1411 * @returns True if matches, false if not.
1412 * @param pszPat Pattern.
1413 * @param pszName Name to match against the pattern.
1414 * @param fSeenColon Seen colon (':').
1415 */
1416bool Filter::patternMatch(const char *pszPat, const char *pszName,
1417 bool fSeenColon)
1418{
1419 /* ASSUMES ASCII */
1420 for (;;)
1421 {
1422 char chPat = *pszPat;
1423 switch (chPat)
1424 {
1425 default:
1426 if (*pszName != chPat)
1427 return false;
1428 break;
1429
1430 case '*':
1431 {
1432 while ((chPat = *++pszPat) == '*' || chPat == '?')
1433 /* nothing */;
1434
1435 /* Handle a special case, the mask terminating with a colon. */
1436 if (chPat == ':')
1437 {
1438 if (!fSeenColon && !pszPat[1])
1439 return !strchr(pszName, ':');
1440 fSeenColon = true;
1441 }
1442
1443 for (;;)
1444 {
1445 char ch = *pszName++;
1446 if ( ch == chPat
1447 && ( !chPat
1448 || patternMatch(pszPat + 1, pszName, fSeenColon)))
1449 return true;
1450 if (!ch)
1451 return false;
1452 }
1453 /* won't ever get here */
1454 break;
1455 }
1456
1457 case '?':
1458 if (!*pszName)
1459 return false;
1460 break;
1461
1462 /* Handle a special case, the mask terminating with a colon. */
1463 case ':':
1464 if (!fSeenColon && !pszPat[1])
1465 return !*pszName;
1466 if (*pszName != ':')
1467 return false;
1468 fSeenColon = true;
1469 break;
1470
1471 case '\0':
1472 return !*pszName;
1473 }
1474 pszName++;
1475 pszPat++;
1476 }
1477 /* not reached */
1478}
1479
1480bool Filter::match(const ComPtr<IUnknown> object, const RTCString &name) const
1481{
1482 ElementList::const_iterator it;
1483
1484 //Log7(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
1485 for (it = mElements.begin(); it != mElements.end(); ++it)
1486 {
1487 //Log7(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
1488 if ((*it).first.isNull() || (*it).first == object)
1489 {
1490 // Objects match, compare names
1491 if (patternMatch((*it).second.c_str(), name.c_str()))
1492 {
1493 //LogFlowThisFunc(("...found!\n"));
1494 return true;
1495 }
1496 }
1497 }
1498 //Log7(("...no matches!\n"));
1499 return false;
1500}
1501/* 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