VirtualBox

source: vbox/trunk/src/VBox/Main/Performance.cpp@ 29620

最後變更 在這個檔案從29620是 29620,由 vboxsync 提交於 15 年 前

Statistics for shared pages

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

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