VirtualBox

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

Last change on this file since 14928 was 14772, checked in by vboxsync, 16 years ago

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

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