VirtualBox

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

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

Performance.cpp: patternMatch opt - couldn't stop myself. :-)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 KB
Line 
1/* $Id: Performance.cpp 12802 2008-09-29 14:11:09Z 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 com::SafeArray <INPTR BSTR> nameArray(ComSafeArrayInArg(metricNames));
420
421 if (ComSafeArrayInIsNull(objects))
422 {
423 if (nameArray.size())
424 {
425 for (size_t i = 0; i < nameArray.size(); ++i)
426 processMetricList(std::string(com::Utf8Str(nameArray[i])), ComPtr<IUnknown>());
427 }
428 else
429 processMetricList(std::string("*"), ComPtr<IUnknown>());
430 }
431 else
432 {
433 com::SafeIfaceArray <IUnknown> objectArray(ComSafeArrayInArg(objects));
434
435 for (size_t i = 0; i < objectArray.size(); ++i)
436 switch (nameArray.size())
437 {
438 case 0:
439 processMetricList(std::string("*"), objectArray[i]);
440 break;
441 case 1:
442 processMetricList(std::string(com::Utf8Str(nameArray[0])), objectArray[i]);
443 break;
444 default:
445 processMetricList(std::string(com::Utf8Str(nameArray[i])), objectArray[i]);
446 break;
447 }
448 }
449}
450
451void Filter::processMetricList(const std::string &name, const ComPtr<IUnknown> object)
452{
453 std::string::size_type startPos = 0;
454
455 for (std::string::size_type pos = name.find(",");
456 pos != std::string::npos;
457 pos = name.find(",", startPos))
458 {
459 mElements.push_back(std::make_pair(object, name.substr(startPos, pos - startPos)));
460 startPos = pos + 1;
461 }
462 mElements.push_back(std::make_pair(object, name.substr(startPos)));
463}
464
465/**
466 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
467 * modified to handle the special case of trailing colon in the pattern.
468 *
469 * @returns True if matches, false if not.
470 * @param pszPat Pattern.
471 * @param pszName Name to match against the pattern.
472 * @param fSeenColon Seen colon (':').
473 */
474bool Filter::patternMatch(const char *pszPat, const char *pszName,
475 bool fSeenColon)
476{
477 /* ASSUMES ASCII */
478 for (;;)
479 {
480 char chPat = *pszPat;
481 switch (chPat)
482 {
483 default:
484 if (*pszName != chPat)
485 return false;
486 break;
487
488 case '*':
489 {
490 while ((chPat = *++pszPat) == '*' || chPat == '?')
491 /* nothing */;
492
493 /* Handle a special case, the mask terminating with a colon. */
494 if (chPat == ':')
495 {
496 if (!fSeenColon && !pszPat[1])
497 return !strchr(pszName, ':');
498 fSeenColon = true;
499 }
500
501 for (;;)
502 {
503 char ch = *pszName++;
504 if ( ch == chPat
505 && ( !chPat
506 || patternMatch(pszPat + 1, pszName, fSeenColon)))
507 return true;
508 if (!ch)
509 return false;
510 }
511 /* won't ever get here */
512 break;
513 }
514
515 case '?':
516 if (!*pszName)
517 return false;
518 break;
519
520 /* Handle a special case, the mask terminating with a colon. */
521 case ':':
522 if (!fSeenColon && !pszPat[1])
523 return !*pszName;
524 if (*pszName != ':')
525 return false;
526 fSeenColon = true;
527 break;
528
529 case '\0':
530 return !*pszName;
531 }
532 pszName++;
533 pszPat++;
534 }
535 return true;
536}
537
538bool Filter::match(const ComPtr<IUnknown> object, const std::string &name) const
539{
540 ElementList::const_iterator it;
541
542 LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
543 for (it = mElements.begin(); it != mElements.end(); it++)
544 {
545 LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
546 if ((*it).first.isNull() || (*it).first == object)
547 {
548 // Objects match, compare names
549 if (patternMatch((*it).second.c_str(), name.c_str()))
550 {
551 LogFlowThisFunc(("...found!\n"));
552 return true;
553 }
554 }
555 }
556 LogAleksey(("...no matches!\n"));
557 return false;
558}
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