VirtualBox

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

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

metrics update

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