VirtualBox

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

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

Main/Metrics: Host network metrics, linux only (#6345)

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