VirtualBox

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

Last change on this file since 36964 was 36839, checked in by vboxsync, 14 years ago

Main/Metrics: Locking revised to prevent lockups on VM shutdown (#5637)

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