VirtualBox

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

Last change on this file since 14515 was 13915, checked in by vboxsync, 16 years ago

Main: fix mac burns

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
1/* $Id: Performance.cpp 13915 2008-11-06 13:29: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#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}
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