VirtualBox

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

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

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