VirtualBox

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

Last change on this file since 28005 was 28005, checked in by vboxsync, 15 years ago

Metrics updates

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