VirtualBox

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

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

Metrics: ugly fix for VMM metrics

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.7 KB
Line 
1/* $Id: Performance.cpp 36070 2011-02-24 14:13:20Z 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
78int CollectorHAL::enable()
79{
80 return E_NOTIMPL;
81}
82
83int CollectorHAL::disable()
84{
85 return E_NOTIMPL;
86}
87
88/* Generic implementations */
89
90int CollectorHAL::getHostCpuMHz(ULONG *mhz)
91{
92 unsigned cCpus = 0;
93 uint64_t u64TotalMHz = 0;
94 RTCPUSET OnlineSet;
95 RTMpGetOnlineSet(&OnlineSet);
96 for (RTCPUID iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
97 {
98 LogAleksey(("{%p} " LOG_FN_FMT ": Checking if CPU %d is member of online set...\n",
99 this, __PRETTY_FUNCTION__, (int)iCpu));
100 if (RTCpuSetIsMemberByIndex(&OnlineSet, iCpu))
101 {
102 LogAleksey(("{%p} " LOG_FN_FMT ": Getting frequency for CPU %d...\n",
103 this, __PRETTY_FUNCTION__, (int)iCpu));
104 uint32_t uMHz = RTMpGetCurFrequency(RTMpCpuIdFromSetIndex(iCpu));
105 if (uMHz != 0)
106 {
107 LogAleksey(("{%p} " LOG_FN_FMT ": CPU %d %u MHz\n",
108 this, __PRETTY_FUNCTION__, (int)iCpu, uMHz));
109 u64TotalMHz += uMHz;
110 cCpus++;
111 }
112 }
113 }
114
115 AssertReturn(cCpus, VERR_NOT_IMPLEMENTED);
116 *mhz = (ULONG)(u64TotalMHz / cCpus);
117
118 return VINF_SUCCESS;
119}
120
121#ifndef VBOX_COLLECTOR_TEST_CASE
122
123uint32_t CollectorGuestHAL::cVMsEnabled = 0;
124
125CollectorGuestHAL::CollectorGuestHAL(Machine *machine, CollectorHAL *hostHAL)
126 : CollectorHAL(), cEnabled(0), mMachine(machine), mConsole(NULL),
127 mGuest(NULL), mLastTick(0), mHostHAL(hostHAL), mCpuUser(0),
128 mCpuKernel(0), mCpuIdle(0), mMemTotal(0), mMemFree(0),
129 mMemBalloon(0), mMemShared(0), mMemCache(0), mPageTotal(0)
130{
131 Assert(mMachine);
132 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
133 mMachine->AddRef();
134}
135
136CollectorGuestHAL::~CollectorGuestHAL()
137{
138 /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
139 mMachine->Release();
140 Assert(!cEnabled);
141}
142
143int CollectorGuestHAL::enable()
144{
145 /* Must make sure that the machine object does not get uninitialized
146 * in the middle of enabling this collector. Causes timing-related
147 * behavior otherwise, which we don't want. In particular the
148 * GetRemoteConsole call below can hang if the VM didn't completely
149 * terminate (the VM processes stop processing events shortly before
150 * closing the session). This avoids the hang. */
151 AutoCaller autoCaller(mMachine);
152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
153
154 HRESULT ret = S_OK;
155
156 if (ASMAtomicIncU32(&cEnabled) == 1)
157 {
158 ASMAtomicIncU32(&cVMsEnabled);
159 ComPtr<IInternalSessionControl> directControl;
160
161 ret = mMachine->getDirectControl(&directControl);
162 if (ret != S_OK)
163 return ret;
164
165 /* get the associated console; this is a remote call (!) */
166 ret = directControl->GetRemoteConsole(mConsole.asOutParam());
167 if (ret != S_OK)
168 return ret;
169
170 ret = mConsole->COMGETTER(Guest)(mGuest.asOutParam());
171 if (ret == S_OK)
172 mGuest->COMSETTER(StatisticsUpdateInterval)(1 /* 1 sec */);
173 }
174 return ret;
175}
176
177int CollectorGuestHAL::disable()
178{
179 if (ASMAtomicDecU32(&cEnabled) == 0)
180 {
181 if (ASMAtomicDecU32(&cVMsEnabled) == 0)
182 {
183 if (mHostHAL)
184 mHostHAL->setMemHypervisorStats(0 /* ulMemAllocTotal */, 0 /* ulMemFreeTotal */, 0 /* ulMemBalloonTotal */, 0 /* ulMemSharedTotal */);
185 }
186 Assert(mGuest && mConsole);
187 mGuest->COMSETTER(StatisticsUpdateInterval)(0 /* off */);
188 }
189 return S_OK;
190}
191
192int CollectorGuestHAL::preCollect(const CollectorHints& /* hints */, uint64_t iTick)
193{
194 if ( mGuest
195 && iTick != mLastTick)
196 {
197 ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
198
199 mGuest->InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
200 &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
201 &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal, &ulMemBalloonTotal, &ulMemSharedTotal);
202
203 if (mHostHAL)
204 mHostHAL->setMemHypervisorStats(ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
205
206 mLastTick = iTick;
207 }
208 return S_OK;
209}
210
211#endif /* !VBOX_COLLECTOR_TEST_CASE */
212
213bool BaseMetric::collectorBeat(uint64_t nowAt)
214{
215 if (isEnabled())
216 {
217 if (nowAt - mLastSampleTaken >= mPeriod * 1000)
218 {
219 mLastSampleTaken = nowAt;
220 Log4(("{%p} " LOG_FN_FMT ": Collecting %s for obj(%p)...\n",
221 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
222 return true;
223 }
224 }
225 return false;
226}
227
228/*bool BaseMetric::associatedWith(ComPtr<IUnknown> object)
229{
230 LogFlowThisFunc(("mObject(%p) == object(%p) is %s.\n", mObject, object, mObject == object ? "true" : "false"));
231 return mObject == object;
232}*/
233
234void HostCpuLoad::init(ULONG period, ULONG length)
235{
236 mPeriod = period;
237 mLength = length;
238 mUser->init(mLength);
239 mKernel->init(mLength);
240 mIdle->init(mLength);
241}
242
243void HostCpuLoad::collect()
244{
245 ULONG user, kernel, idle;
246 int rc = mHAL->getHostCpuLoad(&user, &kernel, &idle);
247 if (RT_SUCCESS(rc))
248 {
249 mUser->put(user);
250 mKernel->put(kernel);
251 mIdle->put(idle);
252 }
253}
254
255void HostCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
256{
257 hints.collectHostCpuLoad();
258}
259
260void HostCpuLoadRaw::collect()
261{
262 uint64_t user, kernel, idle;
263 uint64_t userDiff, kernelDiff, idleDiff, totalDiff;
264
265 int rc = mHAL->getRawHostCpuLoad(&user, &kernel, &idle);
266 if (RT_SUCCESS(rc))
267 {
268 userDiff = user - mUserPrev;
269 kernelDiff = kernel - mKernelPrev;
270 idleDiff = idle - mIdlePrev;
271 totalDiff = userDiff + kernelDiff + idleDiff;
272
273 if (totalDiff == 0)
274 {
275 /* This is only possible if none of counters has changed! */
276 LogFlowThisFunc(("Impossible! User, kernel and idle raw "
277 "counters has not changed since last sample.\n" ));
278 mUser->put(0);
279 mKernel->put(0);
280 mIdle->put(0);
281 }
282 else
283 {
284 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff));
285 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff));
286 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff));
287 }
288
289 mUserPrev = user;
290 mKernelPrev = kernel;
291 mIdlePrev = idle;
292 }
293}
294
295void HostCpuMhz::init(ULONG period, ULONG length)
296{
297 mPeriod = period;
298 mLength = length;
299 mMHz->init(mLength);
300}
301
302void HostCpuMhz::collect()
303{
304 ULONG mhz;
305 int rc = mHAL->getHostCpuMHz(&mhz);
306 if (RT_SUCCESS(rc))
307 mMHz->put(mhz);
308}
309
310void HostRamUsage::init(ULONG period, ULONG length)
311{
312 mPeriod = period;
313 mLength = length;
314 mTotal->init(mLength);
315 mUsed->init(mLength);
316 mAvailable->init(mLength);
317}
318
319void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
320{
321 hints.collectHostRamUsage();
322}
323
324void HostRamUsage::collect()
325{
326 ULONG total, used, available;
327 int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
328 if (RT_SUCCESS(rc))
329 {
330 mTotal->put(total);
331 mUsed->put(used);
332 mAvailable->put(available);
333
334 }
335}
336
337void HostRamVmm::init(ULONG period, ULONG length)
338{
339 mPeriod = period;
340 mLength = length;
341 mAllocVMM->init(mLength);
342 mFreeVMM->init(mLength);
343 mBalloonVMM->init(mLength);
344 mSharedVMM->init(mLength);
345}
346
347void HostRamVmm::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
348{
349 /*
350 * This is an ugly ugly hack to force VMM metrics to 0s if no VM is
351 * running. The reason it should work is that the VMM stats are
352 * stored in CollectorHAL in preCollect methods of guest base metrics
353 * which are always added after HostRamVmm. So each pass of collector
354 * first clears the metrics then gets new values.
355 */
356 mHAL->setMemHypervisorStats(0 /* ulMemAllocTotal */, 0 /* ulMemFreeTotal */, 0 /* ulMemBalloonTotal */, 0 /* ulMemSharedTotal */);
357}
358
359void HostRamVmm::collect()
360{
361 ULONG allocVMM, freeVMM, balloonVMM, sharedVMM;
362
363 mHAL->getMemHypervisorStats(&allocVMM, &freeVMM, &balloonVMM, &sharedVMM);
364 mAllocVMM->put(allocVMM);
365 mFreeVMM->put(freeVMM);
366 mBalloonVMM->put(balloonVMM);
367 mSharedVMM->put(sharedVMM);
368}
369
370
371
372void MachineCpuLoad::init(ULONG period, ULONG length)
373{
374 mPeriod = period;
375 mLength = length;
376 mUser->init(mLength);
377 mKernel->init(mLength);
378}
379
380void MachineCpuLoad::collect()
381{
382 ULONG user, kernel;
383 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
384 if (RT_SUCCESS(rc))
385 {
386 mUser->put(user);
387 mKernel->put(kernel);
388 }
389}
390
391void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
392{
393 hints.collectProcessCpuLoad(mProcess);
394}
395
396void MachineCpuLoadRaw::collect()
397{
398 uint64_t processUser, processKernel, hostTotal;
399
400 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
401 if (RT_SUCCESS(rc))
402 {
403 if (hostTotal == mHostTotalPrev)
404 {
405 /* Nearly impossible, but... */
406 mUser->put(0);
407 mKernel->put(0);
408 }
409 else
410 {
411 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
412 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
413 }
414
415 mHostTotalPrev = hostTotal;
416 mProcessUserPrev = processUser;
417 mProcessKernelPrev = processKernel;
418 }
419}
420
421void MachineRamUsage::init(ULONG period, ULONG length)
422{
423 mPeriod = period;
424 mLength = length;
425 mUsed->init(mLength);
426}
427
428void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
429{
430 hints.collectProcessRamUsage(mProcess);
431}
432
433void MachineRamUsage::collect()
434{
435 ULONG used;
436 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
437 if (RT_SUCCESS(rc))
438 mUsed->put(used);
439}
440
441
442void GuestCpuLoad::init(ULONG period, ULONG length)
443{
444 mPeriod = period;
445 mLength = length;
446
447 mUser->init(mLength);
448 mKernel->init(mLength);
449 mIdle->init(mLength);
450}
451
452void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t iTick)
453{
454 mHAL->preCollect(hints, iTick);
455}
456
457void GuestCpuLoad::collect()
458{
459 ULONG CpuUser = 0, CpuKernel = 0, CpuIdle = 0;
460
461 mGuestHAL->getGuestCpuLoad(&CpuUser, &CpuKernel, &CpuIdle);
462 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * CpuUser) / 100);
463 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * CpuKernel) / 100);
464 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * CpuIdle) / 100);
465}
466
467void GuestRamUsage::init(ULONG period, ULONG length)
468{
469 mPeriod = period;
470 mLength = length;
471
472 mTotal->init(mLength);
473 mFree->init(mLength);
474 mBallooned->init(mLength);
475 mShared->init(mLength);
476 mCache->init(mLength);
477 mPagedTotal->init(mLength);
478}
479
480void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t iTick)
481{
482 mHAL->preCollect(hints, iTick);
483}
484
485void GuestRamUsage::collect()
486{
487 ULONG ulMemTotal = 0, ulMemFree = 0, ulMemBalloon = 0, ulMemShared = 0, ulMemCache = 0, ulPageTotal = 0;
488
489 mGuestHAL->getGuestMemLoad(&ulMemTotal, &ulMemFree, &ulMemBalloon, &ulMemShared, &ulMemCache, &ulPageTotal);
490 mTotal->put(ulMemTotal);
491 mFree->put(ulMemFree);
492 mBallooned->put(ulMemBalloon);
493 mShared->put(ulMemShared);
494 mCache->put(ulMemCache);
495 mPagedTotal->put(ulPageTotal);
496}
497
498void CircularBuffer::init(ULONG ulLength)
499{
500 if (mData)
501 RTMemFree(mData);
502 mLength = ulLength;
503 if (mLength)
504 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
505 else
506 mData = NULL;
507 mWrapped = false;
508 mEnd = 0;
509 mSequenceNumber = 0;
510}
511
512ULONG CircularBuffer::length()
513{
514 return mWrapped ? mLength : mEnd;
515}
516
517void CircularBuffer::put(ULONG value)
518{
519 if (mData)
520 {
521 mData[mEnd++] = value;
522 if (mEnd >= mLength)
523 {
524 mEnd = 0;
525 mWrapped = true;
526 }
527 ++mSequenceNumber;
528 }
529}
530
531void CircularBuffer::copyTo(ULONG *data)
532{
533 if (mWrapped)
534 {
535 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
536 // Copy the wrapped part
537 if (mEnd)
538 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
539 }
540 else
541 memcpy(data, mData, mEnd * sizeof(ULONG));
542}
543
544void SubMetric::query(ULONG *data)
545{
546 copyTo(data);
547}
548
549void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
550{
551 ULONG length;
552 ULONG *tmpData;
553
554 length = mSubMetric->length();
555 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
556 if (length)
557 {
558 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
559 mSubMetric->query(tmpData);
560 if (mAggregate)
561 {
562 *count = 1;
563 *data = (ULONG*)RTMemAlloc(sizeof(**data));
564 **data = mAggregate->compute(tmpData, length);
565 RTMemFree(tmpData);
566 }
567 else
568 {
569 *count = length;
570 *data = tmpData;
571 }
572 }
573 else
574 {
575 *count = 0;
576 *data = 0;
577 }
578}
579
580ULONG AggregateAvg::compute(ULONG *data, ULONG length)
581{
582 uint64_t tmp = 0;
583 for (ULONG i = 0; i < length; ++i)
584 tmp += data[i];
585 return (ULONG)(tmp / length);
586}
587
588const char * AggregateAvg::getName()
589{
590 return "avg";
591}
592
593ULONG AggregateMin::compute(ULONG *data, ULONG length)
594{
595 ULONG tmp = *data;
596 for (ULONG i = 0; i < length; ++i)
597 if (data[i] < tmp)
598 tmp = data[i];
599 return tmp;
600}
601
602const char * AggregateMin::getName()
603{
604 return "min";
605}
606
607ULONG AggregateMax::compute(ULONG *data, ULONG length)
608{
609 ULONG tmp = *data;
610 for (ULONG i = 0; i < length; ++i)
611 if (data[i] > tmp)
612 tmp = data[i];
613 return tmp;
614}
615
616const char * AggregateMax::getName()
617{
618 return "max";
619}
620
621Filter::Filter(ComSafeArrayIn(IN_BSTR, metricNames),
622 ComSafeArrayIn(IUnknown *, objects))
623{
624 /*
625 * Let's work around null/empty safe array mess. I am not sure there is
626 * a way to pass null arrays via webservice, I haven't found one. So I
627 * guess the users will be forced to use empty arrays instead. Constructing
628 * an empty SafeArray is a bit awkward, so what we do in this method is
629 * actually convert null arrays to empty arrays and pass them down to
630 * init() method. If someone knows how to do it better, please be my guest,
631 * fix it.
632 */
633 if (ComSafeArrayInIsNull(metricNames))
634 {
635 com::SafeArray<BSTR> nameArray;
636 if (ComSafeArrayInIsNull(objects))
637 {
638 com::SafeIfaceArray<IUnknown> objectArray;
639 objectArray.reset(0);
640 init(ComSafeArrayAsInParam(nameArray),
641 ComSafeArrayAsInParam(objectArray));
642 }
643 else
644 {
645 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
646 init(ComSafeArrayAsInParam(nameArray),
647 ComSafeArrayAsInParam(objectArray));
648 }
649 }
650 else
651 {
652 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
653 if (ComSafeArrayInIsNull(objects))
654 {
655 com::SafeIfaceArray<IUnknown> objectArray;
656 objectArray.reset(0);
657 init(ComSafeArrayAsInParam(nameArray),
658 ComSafeArrayAsInParam(objectArray));
659 }
660 else
661 {
662 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
663 init(ComSafeArrayAsInParam(nameArray),
664 ComSafeArrayAsInParam(objectArray));
665 }
666 }
667}
668
669void Filter::init(ComSafeArrayIn(IN_BSTR, metricNames),
670 ComSafeArrayIn(IUnknown *, objects))
671{
672 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
673 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
674
675 if (!objectArray.size())
676 {
677 if (nameArray.size())
678 {
679 for (size_t i = 0; i < nameArray.size(); ++i)
680 processMetricList(com::Utf8Str(nameArray[i]), ComPtr<IUnknown>());
681 }
682 else
683 processMetricList("*", ComPtr<IUnknown>());
684 }
685 else
686 {
687 for (size_t i = 0; i < objectArray.size(); ++i)
688 switch (nameArray.size())
689 {
690 case 0:
691 processMetricList("*", objectArray[i]);
692 break;
693 case 1:
694 processMetricList(com::Utf8Str(nameArray[0]), objectArray[i]);
695 break;
696 default:
697 processMetricList(com::Utf8Str(nameArray[i]), objectArray[i]);
698 break;
699 }
700 }
701}
702
703void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
704{
705 size_t startPos = 0;
706
707 for (size_t pos = name.find(",");
708 pos != com::Utf8Str::npos;
709 pos = name.find(",", startPos))
710 {
711 mElements.push_back(std::make_pair(object, iprt::MiniString(name.substr(startPos, pos - startPos).c_str())));
712 startPos = pos + 1;
713 }
714 mElements.push_back(std::make_pair(object, iprt::MiniString(name.substr(startPos).c_str())));
715}
716
717/**
718 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
719 * modified to handle the special case of trailing colon in the pattern.
720 *
721 * @returns True if matches, false if not.
722 * @param pszPat Pattern.
723 * @param pszName Name to match against the pattern.
724 * @param fSeenColon Seen colon (':').
725 */
726bool Filter::patternMatch(const char *pszPat, const char *pszName,
727 bool fSeenColon)
728{
729 /* ASSUMES ASCII */
730 for (;;)
731 {
732 char chPat = *pszPat;
733 switch (chPat)
734 {
735 default:
736 if (*pszName != chPat)
737 return false;
738 break;
739
740 case '*':
741 {
742 while ((chPat = *++pszPat) == '*' || chPat == '?')
743 /* nothing */;
744
745 /* Handle a special case, the mask terminating with a colon. */
746 if (chPat == ':')
747 {
748 if (!fSeenColon && !pszPat[1])
749 return !strchr(pszName, ':');
750 fSeenColon = true;
751 }
752
753 for (;;)
754 {
755 char ch = *pszName++;
756 if ( ch == chPat
757 && ( !chPat
758 || patternMatch(pszPat + 1, pszName, fSeenColon)))
759 return true;
760 if (!ch)
761 return false;
762 }
763 /* won't ever get here */
764 break;
765 }
766
767 case '?':
768 if (!*pszName)
769 return false;
770 break;
771
772 /* Handle a special case, the mask terminating with a colon. */
773 case ':':
774 if (!fSeenColon && !pszPat[1])
775 return !*pszName;
776 if (*pszName != ':')
777 return false;
778 fSeenColon = true;
779 break;
780
781 case '\0':
782 return !*pszName;
783 }
784 pszName++;
785 pszPat++;
786 }
787 return true;
788}
789
790bool Filter::match(const ComPtr<IUnknown> object, const iprt::MiniString &name) const
791{
792 ElementList::const_iterator it;
793
794 LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
795 for (it = mElements.begin(); it != mElements.end(); it++)
796 {
797 LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
798 if ((*it).first.isNull() || (*it).first == object)
799 {
800 // Objects match, compare names
801 if (patternMatch((*it).second.c_str(), name.c_str()))
802 {
803 LogFlowThisFunc(("...found!\n"));
804 return true;
805 }
806 }
807 }
808 LogAleksey(("...no matches!\n"));
809 return false;
810}
811/* 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