VirtualBox

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

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

Metrics: work in progress

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