VirtualBox

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

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

Main/Metrics: Hypervisor and guest metrics re-done (#5566)

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette