VirtualBox

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

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

PerfAPI: Handling of trailing colon in filter patterns

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: Performance.cpp 12678 2008-09-24 07:28:21Z 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/* The following method was borrowed from VMM/STAM.cpp and modified to handle
466 * the special case of trailing colon in the pattern.
467 */
468bool Filter::patternMatch(const char *pszPat, const char *pszName,
469 bool fSeenColon)
470{
471 bool seenColon = fSeenColon;
472 /* ASSUMES ASCII */
473 for (;;)
474 {
475 char chPat = *pszPat;
476 switch (chPat)
477 {
478 default:
479 /* Handle a special case, the mask terminating with a colon */
480 if (chPat == ':')
481 {
482 if (!seenColon && !*(pszPat + 1))
483 return !*pszName;
484 seenColon = true;
485 }
486 if (*pszName != chPat)
487 return false;
488 break;
489
490 case '*':
491 {
492 while ((chPat = *++pszPat) == '*' || chPat == '?')
493 /* nothing */;
494
495 /* Handle a special case, the mask terminating with a colon */
496 if (chPat == ':')
497 {
498 if (!seenColon && !*(pszPat + 1))
499 return !strchr(pszName, ':');
500 seenColon = true;
501 }
502 for (;;)
503 {
504 char ch = *pszName++;
505 if ( ch == chPat
506 && ( !chPat
507 || patternMatch(pszPat + 1, pszName, seenColon)))
508 return true;
509 if (!ch)
510 return false;
511 }
512 /* won't ever get here */
513 break;
514 }
515
516 case '?':
517 if (!*pszName)
518 return false;
519 break;
520
521 case '\0':
522 return !*pszName;
523 }
524 pszName++;
525 pszPat++;
526 }
527 return true;
528}
529
530bool Filter::match(const ComPtr<IUnknown> object, const std::string &name) const
531{
532 ElementList::const_iterator it;
533
534 LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
535 for (it = mElements.begin(); it != mElements.end(); it++)
536 {
537 LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
538 if ((*it).first.isNull() || (*it).first == object)
539 {
540 // Objects match, compare names
541 if (patternMatch((*it).second.c_str(), name.c_str()))
542 {
543 LogFlowThisFunc(("...found!\n"));
544 return true;
545 }
546 }
547 }
548 LogAleksey(("...no matches!\n"));
549 return false;
550}
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