VirtualBox

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

Last change on this file since 43547 was 43507, checked in by vboxsync, 12 years ago

Main/Metrics: Alternative way to get link speed for old kernels (#6345)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.8 KB
Line 
1/* $Id: Performance.cpp 43507 2012-10-02 13:22:31Z 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 */)
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 int rc = mHAL->getRawHostNetworkLoad(mInterfaceName.c_str(), &mRxPrev, &mTxPrev);
659 AssertRC(rc);
660}
661
662void HostNetworkLoadRaw::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
663{
664 if (RT_FAILURE(mRc))
665 {
666 ComPtr<IHostNetworkInterface> networkInterface;
667 ComPtr<IHost> host = getObject();
668 HRESULT hrc = host->FindHostNetworkInterfaceByName(com::Bstr(mInterfaceName).raw(), networkInterface.asOutParam());
669 if (SUCCEEDED(hrc))
670 {
671 LogRel(("Failed to collect network metrics for %s: %Rrc (%d).\n", mInterfaceName.c_str(), mRc, mRc));
672 mRc = VINF_SUCCESS;
673 }
674 }
675}
676
677void HostNetworkLoadRaw::collect()
678{
679 uint64_t rx, tx;
680
681 mRc = mHAL->getRawHostNetworkLoad(mInterfaceName.c_str(), &rx, &tx);
682 if (RT_SUCCESS(mRc))
683 {
684 uint64_t rxDiff = rx - mRxPrev;
685 uint64_t txDiff = tx - mTxPrev;
686
687 if (RT_UNLIKELY(mSpeed * getPeriod() == 0))
688 {
689 Assert(mSpeed * getPeriod());
690 LogFlowThisFunc(("Impossible! speed=%llu period=%d.\n", mSpeed, getPeriod()));
691 mRx->put(0);
692 mTx->put(0);
693 }
694 else
695 {
696 mRx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * rxDiff / (mSpeed * getPeriod())));
697 mTx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * txDiff / (mSpeed * getPeriod())));
698 }
699
700 mRxPrev = rx;
701 mTxPrev = tx;
702 }
703 else
704 LogFlowThisFunc(("Failed to collect data: %Rrc (%d)."
705 " Will update the list of interfaces...\n", mRc,mRc));
706}
707
708void HostCpuMhz::init(ULONG period, ULONG length)
709{
710 mPeriod = period;
711 mLength = length;
712 mMHz->init(mLength);
713}
714
715void HostCpuMhz::collect()
716{
717 ULONG mhz;
718 int rc = mHAL->getHostCpuMHz(&mhz);
719 if (RT_SUCCESS(rc))
720 mMHz->put(mhz);
721}
722
723void HostRamUsage::init(ULONG period, ULONG length)
724{
725 mPeriod = period;
726 mLength = length;
727 mTotal->init(mLength);
728 mUsed->init(mLength);
729 mAvailable->init(mLength);
730}
731
732void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
733{
734 hints.collectHostRamUsage();
735}
736
737void HostRamUsage::collect()
738{
739 ULONG total, used, available;
740 int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
741 if (RT_SUCCESS(rc))
742 {
743 mTotal->put(total);
744 mUsed->put(used);
745 mAvailable->put(available);
746
747 }
748}
749
750#ifndef VBOX_COLLECTOR_TEST_CASE
751void HostRamVmm::init(ULONG period, ULONG length)
752{
753 mPeriod = period;
754 mLength = length;
755 mAllocVMM->init(mLength);
756 mFreeVMM->init(mLength);
757 mBalloonVMM->init(mLength);
758 mSharedVMM->init(mLength);
759}
760
761int HostRamVmm::enable()
762{
763 int rc = S_OK;
764 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
765 if (provider)
766 rc = provider->enable(GUESTSTATS_VMMRAM);
767 BaseMetric::enable();
768 return rc;
769}
770
771int HostRamVmm::disable()
772{
773 int rc = S_OK;
774 BaseMetric::disable();
775 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
776 if (provider)
777 rc = provider->disable(GUESTSTATS_VMMRAM);
778 return rc;
779}
780
781void HostRamVmm::preCollect(CollectorHints& hints, uint64_t /* iTick */)
782{
783 hints.collectHostRamVmm();
784}
785
786void HostRamVmm::collect()
787{
788 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
789 if (provider)
790 {
791 LogAleksey(("{%p} " LOG_FN_FMT ": provider=%p enabled=%s valid=%s...\n",
792 this, __PRETTY_FUNCTION__, provider, provider->isEnabled()?"y":"n",
793 provider->isValid(GUESTSTATS_VMMRAM)?"y":"n"));
794 if (provider->isValid(GUESTSTATS_VMMRAM))
795 {
796 /* Provider is ready, get updated stats */
797 mAllocCurrent = provider->getAllocVMM();
798 mFreeCurrent = provider->getFreeVMM();
799 mBalloonedCurrent = provider->getBalloonedVMM();
800 mSharedCurrent = provider->getSharedVMM();
801 provider->invalidate(GUESTSTATS_VMMRAM);
802 }
803 /*
804 * Note that if there are no new values from the provider we will use
805 * the ones most recently provided instead of zeros, which is probably
806 * a desirable behavior.
807 */
808 }
809 else
810 {
811 mAllocCurrent = 0;
812 mFreeCurrent = 0;
813 mBalloonedCurrent = 0;
814 mSharedCurrent = 0;
815 }
816 LogAleksey(("{%p} " LOG_FN_FMT ": mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
817 this, __PRETTY_FUNCTION__,
818 mAllocCurrent, mFreeCurrent, mBalloonedCurrent, mSharedCurrent));
819 mAllocVMM->put(mAllocCurrent);
820 mFreeVMM->put(mFreeCurrent);
821 mBalloonVMM->put(mBalloonedCurrent);
822 mSharedVMM->put(mSharedCurrent);
823}
824#endif /* !VBOX_COLLECTOR_TEST_CASE */
825
826
827
828void MachineCpuLoad::init(ULONG period, ULONG length)
829{
830 mPeriod = period;
831 mLength = length;
832 mUser->init(mLength);
833 mKernel->init(mLength);
834}
835
836void MachineCpuLoad::collect()
837{
838 ULONG user, kernel;
839 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
840 if (RT_SUCCESS(rc))
841 {
842 mUser->put(user);
843 mKernel->put(kernel);
844 }
845}
846
847void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
848{
849 hints.collectProcessCpuLoad(mProcess);
850}
851
852void MachineCpuLoadRaw::collect()
853{
854 uint64_t processUser, processKernel, hostTotal;
855
856 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
857 if (RT_SUCCESS(rc))
858 {
859 if (hostTotal == mHostTotalPrev)
860 {
861 /* Nearly impossible, but... */
862 mUser->put(0);
863 mKernel->put(0);
864 }
865 else
866 {
867 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
868 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
869 }
870
871 mHostTotalPrev = hostTotal;
872 mProcessUserPrev = processUser;
873 mProcessKernelPrev = processKernel;
874 }
875}
876
877void MachineRamUsage::init(ULONG period, ULONG length)
878{
879 mPeriod = period;
880 mLength = length;
881 mUsed->init(mLength);
882}
883
884void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
885{
886 hints.collectProcessRamUsage(mProcess);
887}
888
889void MachineRamUsage::collect()
890{
891 ULONG used;
892 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
893 if (RT_SUCCESS(rc))
894 mUsed->put(used);
895}
896
897
898#ifndef VBOX_COLLECTOR_TEST_CASE
899void GuestCpuLoad::init(ULONG period, ULONG length)
900{
901 mPeriod = period;
902 mLength = length;
903
904 mUser->init(mLength);
905 mKernel->init(mLength);
906 mIdle->init(mLength);
907}
908
909void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */)
910{
911 hints.collectGuestStats(mCGuest->getProcess());
912}
913
914void GuestCpuLoad::collect()
915{
916 if (mCGuest->isValid(GUESTSTATS_CPULOAD))
917 {
918 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100);
919 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100);
920 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100);
921 mCGuest->invalidate(GUESTSTATS_CPULOAD);
922 }
923}
924
925int GuestCpuLoad::enable()
926{
927 int rc = mCGuest->enable(GUESTSTATS_CPULOAD);
928 BaseMetric::enable();
929 return rc;
930}
931
932int GuestCpuLoad::disable()
933{
934 BaseMetric::disable();
935 return mCGuest->disable(GUESTSTATS_CPULOAD);
936}
937
938void GuestRamUsage::init(ULONG period, ULONG length)
939{
940 mPeriod = period;
941 mLength = length;
942
943 mTotal->init(mLength);
944 mFree->init(mLength);
945 mBallooned->init(mLength);
946 mShared->init(mLength);
947 mCache->init(mLength);
948 mPagedTotal->init(mLength);
949}
950
951void GuestRamUsage::collect()
952{
953 if (mCGuest->isValid(GUESTSTATS_RAMUSAGE))
954 {
955 mTotal->put(mCGuest->getMemTotal());
956 mFree->put(mCGuest->getMemFree());
957 mBallooned->put(mCGuest->getMemBalloon());
958 mShared->put(mCGuest->getMemShared());
959 mCache->put(mCGuest->getMemCache());
960 mPagedTotal->put(mCGuest->getPageTotal());
961 mCGuest->invalidate(GUESTSTATS_RAMUSAGE);
962 }
963}
964
965int GuestRamUsage::enable()
966{
967 int rc = mCGuest->enable(GUESTSTATS_RAMUSAGE);
968 BaseMetric::enable();
969 return rc;
970}
971
972int GuestRamUsage::disable()
973{
974 BaseMetric::disable();
975 return mCGuest->disable(GUESTSTATS_RAMUSAGE);
976}
977
978void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
979{
980 hints.collectGuestStats(mCGuest->getProcess());
981}
982#endif /* !VBOX_COLLECTOR_TEST_CASE */
983
984void CircularBuffer::init(ULONG ulLength)
985{
986 if (mData)
987 RTMemFree(mData);
988 mLength = ulLength;
989 if (mLength)
990 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
991 else
992 mData = NULL;
993 mWrapped = false;
994 mEnd = 0;
995 mSequenceNumber = 0;
996}
997
998ULONG CircularBuffer::length()
999{
1000 return mWrapped ? mLength : mEnd;
1001}
1002
1003void CircularBuffer::put(ULONG value)
1004{
1005 if (mData)
1006 {
1007 mData[mEnd++] = value;
1008 if (mEnd >= mLength)
1009 {
1010 mEnd = 0;
1011 mWrapped = true;
1012 }
1013 ++mSequenceNumber;
1014 }
1015}
1016
1017void CircularBuffer::copyTo(ULONG *data)
1018{
1019 if (mWrapped)
1020 {
1021 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
1022 // Copy the wrapped part
1023 if (mEnd)
1024 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
1025 }
1026 else
1027 memcpy(data, mData, mEnd * sizeof(ULONG));
1028}
1029
1030void SubMetric::query(ULONG *data)
1031{
1032 copyTo(data);
1033}
1034
1035void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
1036{
1037 ULONG length;
1038 ULONG *tmpData;
1039
1040 length = mSubMetric->length();
1041 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
1042 if (length)
1043 {
1044 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
1045 mSubMetric->query(tmpData);
1046 if (mAggregate)
1047 {
1048 *count = 1;
1049 *data = (ULONG*)RTMemAlloc(sizeof(**data));
1050 **data = mAggregate->compute(tmpData, length);
1051 RTMemFree(tmpData);
1052 }
1053 else
1054 {
1055 *count = length;
1056 *data = tmpData;
1057 }
1058 }
1059 else
1060 {
1061 *count = 0;
1062 *data = 0;
1063 }
1064}
1065
1066ULONG AggregateAvg::compute(ULONG *data, ULONG length)
1067{
1068 uint64_t tmp = 0;
1069 for (ULONG i = 0; i < length; ++i)
1070 tmp += data[i];
1071 return (ULONG)(tmp / length);
1072}
1073
1074const char * AggregateAvg::getName()
1075{
1076 return "avg";
1077}
1078
1079ULONG AggregateMin::compute(ULONG *data, ULONG length)
1080{
1081 ULONG tmp = *data;
1082 for (ULONG i = 0; i < length; ++i)
1083 if (data[i] < tmp)
1084 tmp = data[i];
1085 return tmp;
1086}
1087
1088const char * AggregateMin::getName()
1089{
1090 return "min";
1091}
1092
1093ULONG AggregateMax::compute(ULONG *data, ULONG length)
1094{
1095 ULONG tmp = *data;
1096 for (ULONG i = 0; i < length; ++i)
1097 if (data[i] > tmp)
1098 tmp = data[i];
1099 return tmp;
1100}
1101
1102const char * AggregateMax::getName()
1103{
1104 return "max";
1105}
1106
1107Filter::Filter(ComSafeArrayIn(IN_BSTR, metricNames),
1108 ComSafeArrayIn(IUnknown *, objects))
1109{
1110 /*
1111 * Let's work around null/empty safe array mess. I am not sure there is
1112 * a way to pass null arrays via webservice, I haven't found one. So I
1113 * guess the users will be forced to use empty arrays instead. Constructing
1114 * an empty SafeArray is a bit awkward, so what we do in this method is
1115 * actually convert null arrays to empty arrays and pass them down to
1116 * init() method. If someone knows how to do it better, please be my guest,
1117 * fix it.
1118 */
1119 if (ComSafeArrayInIsNull(metricNames))
1120 {
1121 com::SafeArray<BSTR> nameArray;
1122 if (ComSafeArrayInIsNull(objects))
1123 {
1124 com::SafeIfaceArray<IUnknown> objectArray;
1125 objectArray.reset(0);
1126 init(ComSafeArrayAsInParam(nameArray),
1127 ComSafeArrayAsInParam(objectArray));
1128 }
1129 else
1130 {
1131 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1132 init(ComSafeArrayAsInParam(nameArray),
1133 ComSafeArrayAsInParam(objectArray));
1134 }
1135 }
1136 else
1137 {
1138 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1139 if (ComSafeArrayInIsNull(objects))
1140 {
1141 com::SafeIfaceArray<IUnknown> objectArray;
1142 objectArray.reset(0);
1143 init(ComSafeArrayAsInParam(nameArray),
1144 ComSafeArrayAsInParam(objectArray));
1145 }
1146 else
1147 {
1148 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1149 init(ComSafeArrayAsInParam(nameArray),
1150 ComSafeArrayAsInParam(objectArray));
1151 }
1152 }
1153}
1154
1155Filter::Filter(const com::Utf8Str name, const ComPtr<IUnknown> &aObject)
1156{
1157 processMetricList(name, aObject);
1158}
1159
1160void Filter::init(ComSafeArrayIn(IN_BSTR, metricNames),
1161 ComSafeArrayIn(IUnknown *, objects))
1162{
1163 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
1164 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
1165
1166 if (!objectArray.size())
1167 {
1168 if (nameArray.size())
1169 {
1170 for (size_t i = 0; i < nameArray.size(); ++i)
1171 processMetricList(com::Utf8Str(nameArray[i]), ComPtr<IUnknown>());
1172 }
1173 else
1174 processMetricList("*", ComPtr<IUnknown>());
1175 }
1176 else
1177 {
1178 for (size_t i = 0; i < objectArray.size(); ++i)
1179 switch (nameArray.size())
1180 {
1181 case 0:
1182 processMetricList("*", objectArray[i]);
1183 break;
1184 case 1:
1185 processMetricList(com::Utf8Str(nameArray[0]), objectArray[i]);
1186 break;
1187 default:
1188 processMetricList(com::Utf8Str(nameArray[i]), objectArray[i]);
1189 break;
1190 }
1191 }
1192}
1193
1194void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
1195{
1196 size_t startPos = 0;
1197
1198 for (size_t pos = name.find(",");
1199 pos != com::Utf8Str::npos;
1200 pos = name.find(",", startPos))
1201 {
1202 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos, pos - startPos).c_str())));
1203 startPos = pos + 1;
1204 }
1205 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos).c_str())));
1206}
1207
1208/**
1209 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
1210 * modified to handle the special case of trailing colon in the pattern.
1211 *
1212 * @returns True if matches, false if not.
1213 * @param pszPat Pattern.
1214 * @param pszName Name to match against the pattern.
1215 * @param fSeenColon Seen colon (':').
1216 */
1217bool Filter::patternMatch(const char *pszPat, const char *pszName,
1218 bool fSeenColon)
1219{
1220 /* ASSUMES ASCII */
1221 for (;;)
1222 {
1223 char chPat = *pszPat;
1224 switch (chPat)
1225 {
1226 default:
1227 if (*pszName != chPat)
1228 return false;
1229 break;
1230
1231 case '*':
1232 {
1233 while ((chPat = *++pszPat) == '*' || chPat == '?')
1234 /* nothing */;
1235
1236 /* Handle a special case, the mask terminating with a colon. */
1237 if (chPat == ':')
1238 {
1239 if (!fSeenColon && !pszPat[1])
1240 return !strchr(pszName, ':');
1241 fSeenColon = true;
1242 }
1243
1244 for (;;)
1245 {
1246 char ch = *pszName++;
1247 if ( ch == chPat
1248 && ( !chPat
1249 || patternMatch(pszPat + 1, pszName, fSeenColon)))
1250 return true;
1251 if (!ch)
1252 return false;
1253 }
1254 /* won't ever get here */
1255 break;
1256 }
1257
1258 case '?':
1259 if (!*pszName)
1260 return false;
1261 break;
1262
1263 /* Handle a special case, the mask terminating with a colon. */
1264 case ':':
1265 if (!fSeenColon && !pszPat[1])
1266 return !*pszName;
1267 if (*pszName != ':')
1268 return false;
1269 fSeenColon = true;
1270 break;
1271
1272 case '\0':
1273 return !*pszName;
1274 }
1275 pszName++;
1276 pszPat++;
1277 }
1278 return true;
1279}
1280
1281bool Filter::match(const ComPtr<IUnknown> object, const RTCString &name) const
1282{
1283 ElementList::const_iterator it;
1284
1285 //LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
1286 for (it = mElements.begin(); it != mElements.end(); it++)
1287 {
1288 //LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
1289 if ((*it).first.isNull() || (*it).first == object)
1290 {
1291 // Objects match, compare names
1292 if (patternMatch((*it).second.c_str(), name.c_str()))
1293 {
1294 //LogFlowThisFunc(("...found!\n"));
1295 return true;
1296 }
1297 }
1298 }
1299 //LogAleksey(("...no matches!\n"));
1300 return false;
1301}
1302/* 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