VirtualBox

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

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

PerfAPI: New method queryMetricsDataEx()

  • 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 12973 2008-10-03 17:46:19Z 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 mSequenceNumber = 0;
308}
309
310ULONG CircularBuffer::length()
311{
312 return mWrapped ? mLength : mEnd;
313}
314
315void CircularBuffer::put(ULONG value)
316{
317 if (mData)
318 {
319 mData[mEnd++] = value;
320 if (mEnd >= mLength)
321 {
322 mEnd = 0;
323 mWrapped = true;
324 }
325 ++mSequenceNumber;
326 }
327}
328
329void CircularBuffer::copyTo(ULONG *data)
330{
331 if (mWrapped)
332 {
333 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
334 // Copy the wrapped part
335 if (mEnd)
336 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
337 }
338 else
339 memcpy(data, mData, mEnd * sizeof(ULONG));
340}
341
342void SubMetric::query(ULONG *data)
343{
344 copyTo(data);
345}
346
347void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
348{
349 ULONG length;
350 ULONG *tmpData;
351
352 length = mSubMetric->length();
353 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
354 if (length)
355 {
356 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
357 mSubMetric->query(tmpData);
358 if (mAggregate)
359 {
360 *count = 1;
361 *data = (ULONG*)RTMemAlloc(sizeof(**data));
362 **data = mAggregate->compute(tmpData, length);
363 RTMemFree(tmpData);
364 }
365 else
366 {
367 *count = length;
368 *data = tmpData;
369 }
370 }
371 else
372 {
373 *count = 0;
374 *data = 0;
375 }
376}
377
378ULONG AggregateAvg::compute(ULONG *data, ULONG length)
379{
380 uint64_t tmp = 0;
381 for (ULONG i = 0; i < length; ++i)
382 tmp += data[i];
383 return (ULONG)(tmp / length);
384}
385
386const char * AggregateAvg::getName()
387{
388 return "avg";
389}
390
391ULONG AggregateMin::compute(ULONG *data, ULONG length)
392{
393 ULONG tmp = *data;
394 for (ULONG i = 0; i < length; ++i)
395 if (data[i] < tmp)
396 tmp = data[i];
397 return tmp;
398}
399
400const char * AggregateMin::getName()
401{
402 return "min";
403}
404
405ULONG AggregateMax::compute(ULONG *data, ULONG length)
406{
407 ULONG tmp = *data;
408 for (ULONG i = 0; i < length; ++i)
409 if (data[i] > tmp)
410 tmp = data[i];
411 return tmp;
412}
413
414const char * AggregateMax::getName()
415{
416 return "max";
417}
418
419Filter::Filter(ComSafeArrayIn(INPTR BSTR, metricNames),
420 ComSafeArrayIn(IUnknown *, objects))
421{
422 /*
423 * Let's work around null/empty safe array mess. I am not sure there is
424 * a way to pass null arrays via webservice, I haven't found one. So I
425 * guess the users will be forced to use empty arrays instead. Constructing
426 * an empty SafeArray is a bit awkward, so what we do in this method is
427 * actually convert null arrays to empty arrays and pass them down to
428 * init() method. If someone knows how to do it better, please be my guest,
429 * fix it.
430 */
431 if (ComSafeArrayInIsNull(metricNames))
432 {
433 com::SafeArray <BSTR> nameArray;
434 if (ComSafeArrayInIsNull(objects))
435 {
436 com::SafeIfaceArray <IUnknown> objectArray;
437 objectArray.reset(0);
438 init(ComSafeArrayAsInParam(nameArray),
439 ComSafeArrayAsInParam(objectArray));
440 }
441 else
442 {
443 com::SafeIfaceArray <IUnknown> objectArray(ComSafeArrayInArg(objects));
444 init(ComSafeArrayAsInParam(nameArray),
445 ComSafeArrayAsInParam(objectArray));
446 }
447 }
448 else
449 {
450 com::SafeArray <INPTR BSTR> nameArray(ComSafeArrayInArg(metricNames));
451 if (ComSafeArrayInIsNull(objects))
452 {
453 com::SafeIfaceArray <IUnknown> objectArray;
454 objectArray.reset(0);
455 init(ComSafeArrayAsInParam(nameArray),
456 ComSafeArrayAsInParam(objectArray));
457 }
458 else
459 {
460 com::SafeIfaceArray <IUnknown> objectArray(ComSafeArrayInArg(objects));
461 init(ComSafeArrayAsInParam(nameArray),
462 ComSafeArrayAsInParam(objectArray));
463 }
464 }
465}
466
467void Filter::init(ComSafeArrayIn(INPTR BSTR, metricNames),
468 ComSafeArrayIn(IUnknown *, objects))
469{
470 com::SafeArray <INPTR BSTR> nameArray(ComSafeArrayInArg(metricNames));
471 com::SafeIfaceArray <IUnknown> objectArray(ComSafeArrayInArg(objects));
472
473 if (!objectArray.size())
474 {
475 if (nameArray.size())
476 {
477 for (size_t i = 0; i < nameArray.size(); ++i)
478 processMetricList(std::string(com::Utf8Str(nameArray[i])), ComPtr<IUnknown>());
479 }
480 else
481 processMetricList(std::string("*"), ComPtr<IUnknown>());
482 }
483 else
484 {
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}
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