VirtualBox

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

Last change on this file since 15828 was 15051, checked in by vboxsync, 16 years ago

Main: Cleaned up the long standing const BSTR = const (OLECHAR *) on WIn32 vs (const PRunichar) * on XPCOM clash. Cleaned up BSTR/GUID macros (IN_BSTR replaces INPTR BSTR, IN_GUID replaces INPTR GUIDPARAM, OUT_GUID replaces GUIDPARAMOUT).

  • 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 15051 2008-12-05 17:20:00Z 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(IN_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 <IN_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(IN_BSTR, metricNames),
469 ComSafeArrayIn(IUnknown *, objects))
470{
471 com::SafeArray <IN_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 for (size_t i = 0; i < objectArray.size(); ++i)
487 switch (nameArray.size())
488 {
489 case 0:
490 processMetricList(std::string("*"), objectArray[i]);
491 break;
492 case 1:
493 processMetricList(std::string(com::Utf8Str(nameArray[0])), objectArray[i]);
494 break;
495 default:
496 processMetricList(std::string(com::Utf8Str(nameArray[i])), objectArray[i]);
497 break;
498 }
499 }
500}
501
502void Filter::processMetricList(const std::string &name, const ComPtr<IUnknown> object)
503{
504 std::string::size_type startPos = 0;
505
506 for (std::string::size_type pos = name.find(",");
507 pos != std::string::npos;
508 pos = name.find(",", startPos))
509 {
510 mElements.push_back(std::make_pair(object, name.substr(startPos, pos - startPos)));
511 startPos = pos + 1;
512 }
513 mElements.push_back(std::make_pair(object, name.substr(startPos)));
514}
515
516/**
517 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
518 * modified to handle the special case of trailing colon in the pattern.
519 *
520 * @returns True if matches, false if not.
521 * @param pszPat Pattern.
522 * @param pszName Name to match against the pattern.
523 * @param fSeenColon Seen colon (':').
524 */
525bool Filter::patternMatch(const char *pszPat, const char *pszName,
526 bool fSeenColon)
527{
528 /* ASSUMES ASCII */
529 for (;;)
530 {
531 char chPat = *pszPat;
532 switch (chPat)
533 {
534 default:
535 if (*pszName != chPat)
536 return false;
537 break;
538
539 case '*':
540 {
541 while ((chPat = *++pszPat) == '*' || chPat == '?')
542 /* nothing */;
543
544 /* Handle a special case, the mask terminating with a colon. */
545 if (chPat == ':')
546 {
547 if (!fSeenColon && !pszPat[1])
548 return !strchr(pszName, ':');
549 fSeenColon = true;
550 }
551
552 for (;;)
553 {
554 char ch = *pszName++;
555 if ( ch == chPat
556 && ( !chPat
557 || patternMatch(pszPat + 1, pszName, fSeenColon)))
558 return true;
559 if (!ch)
560 return false;
561 }
562 /* won't ever get here */
563 break;
564 }
565
566 case '?':
567 if (!*pszName)
568 return false;
569 break;
570
571 /* Handle a special case, the mask terminating with a colon. */
572 case ':':
573 if (!fSeenColon && !pszPat[1])
574 return !*pszName;
575 if (*pszName != ':')
576 return false;
577 fSeenColon = true;
578 break;
579
580 case '\0':
581 return !*pszName;
582 }
583 pszName++;
584 pszPat++;
585 }
586 return true;
587}
588
589bool Filter::match(const ComPtr<IUnknown> object, const std::string &name) const
590{
591 ElementList::const_iterator it;
592
593 LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
594 for (it = mElements.begin(); it != mElements.end(); it++)
595 {
596 LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
597 if ((*it).first.isNull() || (*it).first == object)
598 {
599 // Objects match, compare names
600 if (patternMatch((*it).second.c_str(), name.c_str()))
601 {
602 LogFlowThisFunc(("...found!\n"));
603 return true;
604 }
605 }
606 }
607 LogAleksey(("...no matches!\n"));
608 return false;
609}
610/* 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