VirtualBox

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

Last change on this file was 98292, checked in by vboxsync, 16 months ago

Main/src-server: rc -> hrc/vrc. Enabled scm rc checks. bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use