VirtualBox

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

Last change on this file since 59090 was 56994, checked in by vboxsync, 9 years ago

Main: Log and assert formatting fixes.

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