VirtualBox

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

Last change on this file since 55747 was 55214, checked in by vboxsync, 10 years ago

Main/Console+Machine+Session+Snapshot: move the save state and snapshot related methods from IConsole to IMachine, with lots of unavoidable code restructuring and cleanup. Also define two new machine states (so that the "Saving" one is specifically for saving state now) which requires more changes everywhere
Frontends: necessary adjustments
doc/SDK: document the changes

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