VirtualBox

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

Last change on this file since 44098 was 44031, checked in by vboxsync, 12 years ago

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