VirtualBox

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

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

PerfAPI: Fixed the problem with passing null/empty arrays to PerfAPI methods.
Enabled attributes of type IUnknown in SOAP, JAX-WS, WSDL, CPP and Python style sheets.
Added PerfCollector python class to shellcommon.py to provide more 'pythonic' way to access metric data.
Added the reference to shellcommon.py to IDL docs as an example of PerfAPI usage in python.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use