VirtualBox

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

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

compiler warning

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