VirtualBox

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

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

Removed guest statistics interface; to be merged with metrics.

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