VirtualBox

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

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

*: scm cleans up whitespace and adds a new line at the end of ApplianceimplPrivate.h.

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