VirtualBox

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

Last change on this file since 43470 was 43456, checked in by vboxsync, 13 years ago

Main/Metrics: Missing interface detection (#6345)

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette