VirtualBox

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

Last change on this file since 43978 was 43958, checked in by vboxsync, 12 years ago

Main/Metrics: Host disk size, linux only (#6345)

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