VirtualBox

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

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

iprt::MiniString -> RTCString.

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