VirtualBox

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

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

Main/Metrics: Guests now push collected metrics to VBoxSVC instead of being polled (#6029)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.1 KB
Line 
1/* $Id: Performance.cpp 40084 2012-02-12 14:01:47Z 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
111CollectorGuest::CollectorGuest(Machine *machine, RTPROCESS process) :
112 mUnregistered(false), mEnabled(false), mValid(false), mMachine(machine), mProcess(process),
113 mCpuUser(0), mCpuKernel(0), mCpuIdle(0),
114 mMemTotal(0), mMemFree(0), mMemBalloon(0), mMemShared(0), mMemCache(0), mPageTotal(0),
115 mAllocVMM(0), mFreeVMM(0), mBalloonedVMM(0), mSharedVMM(0)
116{
117 Assert(mMachine);
118 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
119 mMachine->AddRef();
120}
121
122CollectorGuest::~CollectorGuest()
123{
124 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
125 mMachine->Release();
126 // Assert(!cEnabled); why?
127}
128
129int CollectorGuest::enableVMMStats(bool mCollectVMMStats)
130{
131 HRESULT ret = S_OK;
132
133 if (mGuest)
134 {
135 /* @todo: replace this with a direct call to mGuest in trunk! */
136 AutoCaller autoCaller(mMachine);
137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
138
139 ComPtr<IInternalSessionControl> directControl;
140
141 ret = mMachine->getDirectControl(&directControl);
142 if (ret != S_OK)
143 return ret;
144
145 /* enable statistics collection; this is a remote call (!) */
146 ret = directControl->EnableVMMStatistics(mCollectVMMStats);
147 LogAleksey(("{%p} " LOG_FN_FMT ": %sable VMM stats (%s)\n",
148 this, __PRETTY_FUNCTION__, mCollectVMMStats?"En":"Dis",
149 SUCCEEDED(ret)?"success":"failed"));
150 }
151
152 return ret;
153}
154
155int CollectorGuest::enable()
156{
157 mEnabled = true;
158 /* Must make sure that the machine object does not get uninitialized
159 * in the middle of enabling this collector. Causes timing-related
160 * behavior otherwise, which we don't want. In particular the
161 * GetRemoteConsole call below can hang if the VM didn't completely
162 * terminate (the VM processes stop processing events shortly before
163 * closing the session). This avoids the hang. */
164 AutoCaller autoCaller(mMachine);
165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
166
167 HRESULT ret = S_OK;
168
169 ComPtr<IInternalSessionControl> directControl;
170
171 ret = mMachine->getDirectControl(&directControl);
172 if (ret != S_OK)
173 return ret;
174
175 /* get the associated console; this is a remote call (!) */
176 ret = directControl->GetRemoteConsole(mConsole.asOutParam());
177 if (ret != S_OK)
178 return ret;
179
180 ret = mConsole->COMGETTER(Guest)(mGuest.asOutParam());
181 if (ret == S_OK)
182 {
183 ret = mGuest->COMSETTER(StatisticsUpdateInterval)(1 /* 1 sec */);
184 LogAleksey(("{%p} " LOG_FN_FMT ": Set guest statistics update interval to 1 sec (%s)\n",
185 this, __PRETTY_FUNCTION__, SUCCEEDED(ret)?"success":"failed"));
186 }
187
188 return ret;
189}
190
191int CollectorGuest::disable()
192{
193 mEnabled = false;
194 Assert(mGuest && mConsole);
195 HRESULT ret = mGuest->COMSETTER(StatisticsUpdateInterval)(0 /* off */);
196 NOREF(ret);
197 LogAleksey(("{%p} " LOG_FN_FMT ": Set guest statistics update interval to 0 sec (%s)\n",
198 this, __PRETTY_FUNCTION__, SUCCEEDED(ret)?"success":"failed"));
199 invalidateStats();
200
201 return S_OK;
202}
203
204void CollectorGuest::updateStats(ULONG aValidStats, ULONG aCpuUser,
205 ULONG aCpuKernel, ULONG aCpuIdle,
206 ULONG aMemTotal, ULONG aMemFree,
207 ULONG aMemBalloon, ULONG aMemShared,
208 ULONG aMemCache, ULONG aPageTotal,
209 ULONG aAllocVMM, ULONG aFreeVMM,
210 ULONG aBalloonedVMM, ULONG aSharedVMM)
211{
212 if ((aValidStats & GUESTSTATS_CPULOAD) == GUESTSTATS_CPULOAD)
213 {
214 mCpuUser = aCpuUser;
215 mCpuKernel = aCpuKernel,
216 mCpuIdle = aCpuIdle;
217 }
218 if ((aValidStats & GUESTSTATS_RAMUSAGE) == GUESTSTATS_RAMUSAGE)
219 {
220 mMemTotal = aMemTotal;
221 mMemFree = aMemFree;
222 mMemBalloon = aMemBalloon;
223 mMemShared = aMemShared;
224 mMemCache = aMemCache;
225 mPageTotal = aPageTotal;
226 }
227 if ((aValidStats & GUESTSTATS_VMMRAM) == GUESTSTATS_VMMRAM)
228 {
229 mAllocVMM = aAllocVMM;
230 mFreeVMM = aFreeVMM;
231 mBalloonedVMM = aBalloonedVMM;
232 mSharedVMM = aSharedVMM;
233 }
234 mValid = aValidStats;
235}
236
237void CollectorGuestManager::preCollect(CollectorHints& hints, uint64_t /* iTick */)
238{
239 /*
240 * Since we are running without a lock the value of mVMMStatsProvider
241 * can change at any moment. In the worst case we won't collect any data.
242 */
243 CollectorGuestList::iterator it;
244
245 LogAleksey(("{%p} " LOG_FN_FMT ": provider=%p ramvmm=%s\n",
246 this, __PRETTY_FUNCTION__, mVMMStatsProvider, hints.isHostRamVmmCollected()?"y":"n"));
247 for (it = mGuests.begin(); it != mGuests.end(); it++)
248 {
249 LogAleksey(("{%p} " LOG_FN_FMT ": it=%p pid=%d gueststats=%s...\n",
250 this, __PRETTY_FUNCTION__, *it, (*it)->getProcess(),
251 hints.isGuestStatsCollected((*it)->getProcess())?"y":"n"));
252 if ((*it)->isUnregistered())
253 continue;
254 if ( (hints.isHostRamVmmCollected() && *it == mVMMStatsProvider)
255 || hints.isGuestStatsCollected((*it)->getProcess()))
256 {
257 /* Guest stats collection needs to be enabled */
258 if ((*it)->isEnabled())
259 {
260 /* Already enabled, collect the data */
261 /*
262 * Actually the data will be pushed by Guest object, so
263 * we don't need to do anything here.
264 */
265 //(*it)->updateStats();
266 }
267 else
268 {
269 (*it)->invalidateStats();
270 (*it)->enable();
271 (*it)->enableVMMStats(*it == mVMMStatsProvider);
272 }
273 }
274 else
275 {
276 /* Guest stats collection needs to be disabled */
277 if ((*it)->isEnabled())
278 (*it)->disable();
279 }
280 }
281}
282
283void CollectorGuestManager::registerGuest(CollectorGuest* pGuest)
284{
285 mGuests.push_back(pGuest);
286 /*
287 * If no VMM stats provider was elected previously than this is our
288 * candidate.
289 */
290 if (!mVMMStatsProvider)
291 mVMMStatsProvider = pGuest;
292 LogAleksey(("{%p} " LOG_FN_FMT ": Registered guest=%p provider=%p\n",
293 this, __PRETTY_FUNCTION__, pGuest, mVMMStatsProvider));
294}
295
296void CollectorGuestManager::unregisterGuest(CollectorGuest* pGuest)
297{
298 LogAleksey(("{%p} " LOG_FN_FMT ": About to unregister guest=%p provider=%p\n",
299 this, __PRETTY_FUNCTION__, pGuest, mVMMStatsProvider));
300 //mGuests.remove(pGuest); => destroyUnregistered()
301 pGuest->unregister();
302 if (pGuest == mVMMStatsProvider)
303 {
304 /* This was our VMM stats provider, it is time to re-elect */
305 CollectorGuestList::iterator it;
306 /* Assume that nobody can provide VMM stats */
307 mVMMStatsProvider = NULL;
308
309 for (it = mGuests.begin(); it != mGuests.end(); it++)
310 {
311 /* Skip unregistered as they are about to be destroyed */
312 if ((*it)->isUnregistered())
313 continue;
314
315 if ((*it)->isEnabled())
316 {
317 /* Found the guest already collecting stats, elect it */
318 mVMMStatsProvider = *it;
319 mVMMStatsProvider->enableVMMStats(true);
320 break;
321 }
322 else if (!mVMMStatsProvider)
323 {
324 /* If nobody collects stats, take the first registered */
325 mVMMStatsProvider = *it;
326 /*
327 * No need to notify the guest at this point as it will be
328 * done in CollectorGuestManager::preCollect when it gets
329 * enabled.
330 */
331 }
332 }
333 }
334 LogAleksey(("{%p} " LOG_FN_FMT ": LEAVE new provider=%p\n",
335 this, __PRETTY_FUNCTION__, mVMMStatsProvider));
336}
337
338void CollectorGuestManager::destroyUnregistered()
339{
340 CollectorGuestList::iterator it;
341
342 for (it = mGuests.begin(); it != mGuests.end();)
343 if ((*it)->isUnregistered())
344 {
345 delete *it;
346 it = mGuests.erase(it);
347 LogAleksey(("{%p} " LOG_FN_FMT ": Number of guests after erasing unregistered is %d\n",
348 this, __PRETTY_FUNCTION__, mGuests.size()));
349 }
350 else
351 ++it;
352}
353
354#endif /* !VBOX_COLLECTOR_TEST_CASE */
355
356bool BaseMetric::collectorBeat(uint64_t nowAt)
357{
358 if (isEnabled())
359 {
360 if (nowAt - mLastSampleTaken >= mPeriod * 1000)
361 {
362 mLastSampleTaken = nowAt;
363 Log4(("{%p} " LOG_FN_FMT ": Collecting %s for obj(%p)...\n",
364 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
365 return true;
366 }
367 }
368 return false;
369}
370
371void HostCpuLoad::init(ULONG period, ULONG length)
372{
373 mPeriod = period;
374 mLength = length;
375 mUser->init(mLength);
376 mKernel->init(mLength);
377 mIdle->init(mLength);
378}
379
380void HostCpuLoad::collect()
381{
382 ULONG user, kernel, idle;
383 int rc = mHAL->getHostCpuLoad(&user, &kernel, &idle);
384 if (RT_SUCCESS(rc))
385 {
386 mUser->put(user);
387 mKernel->put(kernel);
388 mIdle->put(idle);
389 }
390}
391
392void HostCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
393{
394 hints.collectHostCpuLoad();
395}
396
397void HostCpuLoadRaw::collect()
398{
399 uint64_t user, kernel, idle;
400 uint64_t userDiff, kernelDiff, idleDiff, totalDiff;
401
402 int rc = mHAL->getRawHostCpuLoad(&user, &kernel, &idle);
403 if (RT_SUCCESS(rc))
404 {
405 userDiff = user - mUserPrev;
406 kernelDiff = kernel - mKernelPrev;
407 idleDiff = idle - mIdlePrev;
408 totalDiff = userDiff + kernelDiff + idleDiff;
409
410 if (totalDiff == 0)
411 {
412 /* This is only possible if none of counters has changed! */
413 LogFlowThisFunc(("Impossible! User, kernel and idle raw "
414 "counters has not changed since last sample.\n" ));
415 mUser->put(0);
416 mKernel->put(0);
417 mIdle->put(0);
418 }
419 else
420 {
421 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff));
422 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff));
423 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff));
424 }
425
426 mUserPrev = user;
427 mKernelPrev = kernel;
428 mIdlePrev = idle;
429 }
430}
431
432void HostCpuMhz::init(ULONG period, ULONG length)
433{
434 mPeriod = period;
435 mLength = length;
436 mMHz->init(mLength);
437}
438
439void HostCpuMhz::collect()
440{
441 ULONG mhz;
442 int rc = mHAL->getHostCpuMHz(&mhz);
443 if (RT_SUCCESS(rc))
444 mMHz->put(mhz);
445}
446
447void HostRamUsage::init(ULONG period, ULONG length)
448{
449 mPeriod = period;
450 mLength = length;
451 mTotal->init(mLength);
452 mUsed->init(mLength);
453 mAvailable->init(mLength);
454}
455
456void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
457{
458 hints.collectHostRamUsage();
459}
460
461void HostRamUsage::collect()
462{
463 ULONG total, used, available;
464 int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
465 if (RT_SUCCESS(rc))
466 {
467 mTotal->put(total);
468 mUsed->put(used);
469 mAvailable->put(available);
470
471 }
472}
473
474void HostRamVmm::init(ULONG period, ULONG length)
475{
476 mPeriod = period;
477 mLength = length;
478 mAllocVMM->init(mLength);
479 mFreeVMM->init(mLength);
480 mBalloonVMM->init(mLength);
481 mSharedVMM->init(mLength);
482}
483
484void HostRamVmm::preCollect(CollectorHints& hints, uint64_t /* iTick */)
485{
486 hints.collectHostRamVmm();
487}
488
489void HostRamVmm::collect()
490{
491 CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider();
492 if (provider)
493 {
494 LogAleksey(("{%p} " LOG_FN_FMT ": provider=%p enabled=%s valid=%s...\n",
495 this, __PRETTY_FUNCTION__, provider, provider->isEnabled()?"y":"n",
496 provider->isValid(GUESTSTATS_VMMRAM)?"y":"n"));
497 if (provider->isValid(GUESTSTATS_VMMRAM))
498 {
499 /* Provider is ready, get updated stats */
500 mAllocCurrent = provider->getAllocVMM();
501 mFreeCurrent = provider->getFreeVMM();
502 mBalloonedCurrent = provider->getBalloonedVMM();
503 mSharedCurrent = provider->getSharedVMM();
504 }
505 }
506 else
507 {
508 mAllocCurrent = 0;
509 mFreeCurrent = 0;
510 mBalloonedCurrent = 0;
511 mSharedCurrent = 0;
512 }
513 LogAleksey(("{%p} " LOG_FN_FMT ": mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
514 this, __PRETTY_FUNCTION__,
515 mAllocCurrent, mFreeCurrent, mBalloonedCurrent, mSharedCurrent));
516 mAllocVMM->put(mAllocCurrent);
517 mFreeVMM->put(mFreeCurrent);
518 mBalloonVMM->put(mBalloonedCurrent);
519 mSharedVMM->put(mSharedCurrent);
520}
521
522
523
524void MachineCpuLoad::init(ULONG period, ULONG length)
525{
526 mPeriod = period;
527 mLength = length;
528 mUser->init(mLength);
529 mKernel->init(mLength);
530}
531
532void MachineCpuLoad::collect()
533{
534 ULONG user, kernel;
535 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
536 if (RT_SUCCESS(rc))
537 {
538 mUser->put(user);
539 mKernel->put(kernel);
540 }
541}
542
543void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
544{
545 hints.collectProcessCpuLoad(mProcess);
546}
547
548void MachineCpuLoadRaw::collect()
549{
550 uint64_t processUser, processKernel, hostTotal;
551
552 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
553 if (RT_SUCCESS(rc))
554 {
555 if (hostTotal == mHostTotalPrev)
556 {
557 /* Nearly impossible, but... */
558 mUser->put(0);
559 mKernel->put(0);
560 }
561 else
562 {
563 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
564 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
565 }
566
567 mHostTotalPrev = hostTotal;
568 mProcessUserPrev = processUser;
569 mProcessKernelPrev = processKernel;
570 }
571}
572
573void MachineRamUsage::init(ULONG period, ULONG length)
574{
575 mPeriod = period;
576 mLength = length;
577 mUsed->init(mLength);
578}
579
580void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
581{
582 hints.collectProcessRamUsage(mProcess);
583}
584
585void MachineRamUsage::collect()
586{
587 ULONG used;
588 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
589 if (RT_SUCCESS(rc))
590 mUsed->put(used);
591}
592
593
594void GuestCpuLoad::init(ULONG period, ULONG length)
595{
596 mPeriod = period;
597 mLength = length;
598
599 mUser->init(mLength);
600 mKernel->init(mLength);
601 mIdle->init(mLength);
602}
603
604void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */)
605{
606 hints.collectGuestStats(mCGuest->getProcess());
607}
608
609void GuestCpuLoad::collect()
610{
611 if (mCGuest->isValid(GUESTSTATS_CPULOAD))
612 {
613 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100);
614 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100);
615 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100);
616 }
617}
618
619void GuestRamUsage::init(ULONG period, ULONG length)
620{
621 mPeriod = period;
622 mLength = length;
623
624 mTotal->init(mLength);
625 mFree->init(mLength);
626 mBallooned->init(mLength);
627 mShared->init(mLength);
628 mCache->init(mLength);
629 mPagedTotal->init(mLength);
630}
631
632void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
633{
634 hints.collectGuestStats(mCGuest->getProcess());
635}
636
637void GuestRamUsage::collect()
638{
639 if (mCGuest->isValid(GUESTSTATS_RAMUSAGE))
640 {
641 mTotal->put(mCGuest->getMemTotal());
642 mFree->put(mCGuest->getMemFree());
643 mBallooned->put(mCGuest->getMemBalloon());
644 mShared->put(mCGuest->getMemShared());
645 mCache->put(mCGuest->getMemCache());
646 mPagedTotal->put(mCGuest->getPageTotal());
647 }
648}
649
650void CircularBuffer::init(ULONG ulLength)
651{
652 if (mData)
653 RTMemFree(mData);
654 mLength = ulLength;
655 if (mLength)
656 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
657 else
658 mData = NULL;
659 mWrapped = false;
660 mEnd = 0;
661 mSequenceNumber = 0;
662}
663
664ULONG CircularBuffer::length()
665{
666 return mWrapped ? mLength : mEnd;
667}
668
669void CircularBuffer::put(ULONG value)
670{
671 if (mData)
672 {
673 mData[mEnd++] = value;
674 if (mEnd >= mLength)
675 {
676 mEnd = 0;
677 mWrapped = true;
678 }
679 ++mSequenceNumber;
680 }
681}
682
683void CircularBuffer::copyTo(ULONG *data)
684{
685 if (mWrapped)
686 {
687 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
688 // Copy the wrapped part
689 if (mEnd)
690 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
691 }
692 else
693 memcpy(data, mData, mEnd * sizeof(ULONG));
694}
695
696void SubMetric::query(ULONG *data)
697{
698 copyTo(data);
699}
700
701void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
702{
703 ULONG length;
704 ULONG *tmpData;
705
706 length = mSubMetric->length();
707 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
708 if (length)
709 {
710 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
711 mSubMetric->query(tmpData);
712 if (mAggregate)
713 {
714 *count = 1;
715 *data = (ULONG*)RTMemAlloc(sizeof(**data));
716 **data = mAggregate->compute(tmpData, length);
717 RTMemFree(tmpData);
718 }
719 else
720 {
721 *count = length;
722 *data = tmpData;
723 }
724 }
725 else
726 {
727 *count = 0;
728 *data = 0;
729 }
730}
731
732ULONG AggregateAvg::compute(ULONG *data, ULONG length)
733{
734 uint64_t tmp = 0;
735 for (ULONG i = 0; i < length; ++i)
736 tmp += data[i];
737 return (ULONG)(tmp / length);
738}
739
740const char * AggregateAvg::getName()
741{
742 return "avg";
743}
744
745ULONG AggregateMin::compute(ULONG *data, ULONG length)
746{
747 ULONG tmp = *data;
748 for (ULONG i = 0; i < length; ++i)
749 if (data[i] < tmp)
750 tmp = data[i];
751 return tmp;
752}
753
754const char * AggregateMin::getName()
755{
756 return "min";
757}
758
759ULONG AggregateMax::compute(ULONG *data, ULONG length)
760{
761 ULONG tmp = *data;
762 for (ULONG i = 0; i < length; ++i)
763 if (data[i] > tmp)
764 tmp = data[i];
765 return tmp;
766}
767
768const char * AggregateMax::getName()
769{
770 return "max";
771}
772
773Filter::Filter(ComSafeArrayIn(IN_BSTR, metricNames),
774 ComSafeArrayIn(IUnknown *, objects))
775{
776 /*
777 * Let's work around null/empty safe array mess. I am not sure there is
778 * a way to pass null arrays via webservice, I haven't found one. So I
779 * guess the users will be forced to use empty arrays instead. Constructing
780 * an empty SafeArray is a bit awkward, so what we do in this method is
781 * actually convert null arrays to empty arrays and pass them down to
782 * init() method. If someone knows how to do it better, please be my guest,
783 * fix it.
784 */
785 if (ComSafeArrayInIsNull(metricNames))
786 {
787 com::SafeArray<BSTR> nameArray;
788 if (ComSafeArrayInIsNull(objects))
789 {
790 com::SafeIfaceArray<IUnknown> objectArray;
791 objectArray.reset(0);
792 init(ComSafeArrayAsInParam(nameArray),
793 ComSafeArrayAsInParam(objectArray));
794 }
795 else
796 {
797 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
798 init(ComSafeArrayAsInParam(nameArray),
799 ComSafeArrayAsInParam(objectArray));
800 }
801 }
802 else
803 {
804 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
805 if (ComSafeArrayInIsNull(objects))
806 {
807 com::SafeIfaceArray<IUnknown> objectArray;
808 objectArray.reset(0);
809 init(ComSafeArrayAsInParam(nameArray),
810 ComSafeArrayAsInParam(objectArray));
811 }
812 else
813 {
814 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
815 init(ComSafeArrayAsInParam(nameArray),
816 ComSafeArrayAsInParam(objectArray));
817 }
818 }
819}
820
821void Filter::init(ComSafeArrayIn(IN_BSTR, metricNames),
822 ComSafeArrayIn(IUnknown *, objects))
823{
824 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
825 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
826
827 if (!objectArray.size())
828 {
829 if (nameArray.size())
830 {
831 for (size_t i = 0; i < nameArray.size(); ++i)
832 processMetricList(com::Utf8Str(nameArray[i]), ComPtr<IUnknown>());
833 }
834 else
835 processMetricList("*", ComPtr<IUnknown>());
836 }
837 else
838 {
839 for (size_t i = 0; i < objectArray.size(); ++i)
840 switch (nameArray.size())
841 {
842 case 0:
843 processMetricList("*", objectArray[i]);
844 break;
845 case 1:
846 processMetricList(com::Utf8Str(nameArray[0]), objectArray[i]);
847 break;
848 default:
849 processMetricList(com::Utf8Str(nameArray[i]), objectArray[i]);
850 break;
851 }
852 }
853}
854
855void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
856{
857 size_t startPos = 0;
858
859 for (size_t pos = name.find(",");
860 pos != com::Utf8Str::npos;
861 pos = name.find(",", startPos))
862 {
863 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos, pos - startPos).c_str())));
864 startPos = pos + 1;
865 }
866 mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos).c_str())));
867}
868
869/**
870 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
871 * modified to handle the special case of trailing colon in the pattern.
872 *
873 * @returns True if matches, false if not.
874 * @param pszPat Pattern.
875 * @param pszName Name to match against the pattern.
876 * @param fSeenColon Seen colon (':').
877 */
878bool Filter::patternMatch(const char *pszPat, const char *pszName,
879 bool fSeenColon)
880{
881 /* ASSUMES ASCII */
882 for (;;)
883 {
884 char chPat = *pszPat;
885 switch (chPat)
886 {
887 default:
888 if (*pszName != chPat)
889 return false;
890 break;
891
892 case '*':
893 {
894 while ((chPat = *++pszPat) == '*' || chPat == '?')
895 /* nothing */;
896
897 /* Handle a special case, the mask terminating with a colon. */
898 if (chPat == ':')
899 {
900 if (!fSeenColon && !pszPat[1])
901 return !strchr(pszName, ':');
902 fSeenColon = true;
903 }
904
905 for (;;)
906 {
907 char ch = *pszName++;
908 if ( ch == chPat
909 && ( !chPat
910 || patternMatch(pszPat + 1, pszName, fSeenColon)))
911 return true;
912 if (!ch)
913 return false;
914 }
915 /* won't ever get here */
916 break;
917 }
918
919 case '?':
920 if (!*pszName)
921 return false;
922 break;
923
924 /* Handle a special case, the mask terminating with a colon. */
925 case ':':
926 if (!fSeenColon && !pszPat[1])
927 return !*pszName;
928 if (*pszName != ':')
929 return false;
930 fSeenColon = true;
931 break;
932
933 case '\0':
934 return !*pszName;
935 }
936 pszName++;
937 pszPat++;
938 }
939 return true;
940}
941
942bool Filter::match(const ComPtr<IUnknown> object, const RTCString &name) const
943{
944 ElementList::const_iterator it;
945
946 //LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
947 for (it = mElements.begin(); it != mElements.end(); it++)
948 {
949 //LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
950 if ((*it).first.isNull() || (*it).first == object)
951 {
952 // Objects match, compare names
953 if (patternMatch((*it).second.c_str(), name.c_str()))
954 {
955 LogFlowThisFunc(("...found!\n"));
956 return true;
957 }
958 }
959 }
960 //LogAleksey(("...no matches!\n"));
961 return false;
962}
963/* 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