VirtualBox

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

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

'VBoxManage metrics collect' command

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