VirtualBox

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

Last change on this file since 41105 was 40568, checked in by vboxsync, 13 years ago

Main/Metrics: Fix for multiple metrics configuration in one call (#6029)

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