Performance.cpp revision 7ffe7543b32c5dfd3aede1d7f2c1332c7508f6cc
/* $Id$ */
/** @file
* VBox Performance Classes implementation.
*/
/*
* Copyright (C) 2008-2015 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*
* @todo list:
*
* 1) Detection of erroneous metric names
*/
#ifndef VBOX_COLLECTOR_TEST_CASE
#include "VirtualBoxImpl.h"
#include "MachineImpl.h"
#include "MediumImpl.h"
#include "AutoCaller.h"
#endif
#include "Performance.h"
#include "HostNetworkInterfaceImpl.h"
#include "netif.h"
#include <algorithm>
#include "Logging.h"
using namespace pm;
// Stubs for non-pure virtual methods
{
return VERR_NOT_IMPLEMENTED;
}
int CollectorHAL::getProcessCpuLoad(RTPROCESS /* process */, ULONG * /* user */, ULONG * /* kernel */)
{
return VERR_NOT_IMPLEMENTED;
}
int CollectorHAL::getRawHostCpuLoad(uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* idle */)
{
return VERR_NOT_IMPLEMENTED;
}
int CollectorHAL::getRawHostNetworkLoad(const char * /* name */, uint64_t * /* rx */, uint64_t * /* tx */)
{
return VERR_NOT_IMPLEMENTED;
}
int CollectorHAL::getRawHostDiskLoad(const char * /* name */, uint64_t * /* disk_ms */, uint64_t * /* total_ms */)
{
return VERR_NOT_IMPLEMENTED;
}
{
return VERR_NOT_IMPLEMENTED;
}
int CollectorHAL::getHostMemoryUsage(ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
{
return VERR_NOT_IMPLEMENTED;
}
int CollectorHAL::getHostFilesystemUsage(const char * /* name */, ULONG * /* total */, ULONG * /* used */,
ULONG * /* available */)
{
return VERR_NOT_IMPLEMENTED;
}
{
return VERR_NOT_IMPLEMENTED;
}
{
return VERR_NOT_IMPLEMENTED;
}
int CollectorHAL::getDiskListByFs(const char * /* name */, DiskList& /* listUsage */, DiskList& /* listLoad */)
{
return VERR_NOT_IMPLEMENTED;
}
/* Generic implementations */
{
unsigned cCpus = 0;
uint64_t u64TotalMHz = 0;
{
this, __PRETTY_FUNCTION__, (int)iCpu));
{
this, __PRETTY_FUNCTION__, (int)iCpu));
if (uMHz != 0)
{
u64TotalMHz += uMHz;
cCpus++;
}
}
}
return VINF_SUCCESS;
}
#ifndef VBOX_COLLECTOR_TEST_CASE
{
}
{
}
{
}
{
int rc = VINF_SUCCESS;
do
{
{
{
}
}
if (rq)
return rq;
else
}
while (RT_SUCCESS(rc));
return NULL;
}
{
}
{
}
{
}
{
}
{
return E_ABORT;
}
{
}
{
/* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
}
{
/* cannot use ComObjPtr<Machine> in Performance.h, do it manually */
// Assert(!cEnabled); why?
}
{
if (mGuest)
{
/* @todo: replace this with a direct call to mGuest in trunk! */
return ret;
/* enable statistics collection; this is a remote call (!) */
}
return ret;
}
{
}
{
}
{
return E_UNEXPECTED;
if (!mEnabled)
{
/* Must make sure that the machine object does not get uninitialized
* in the middle of enabling this collector. Causes timing-related
* behavior otherwise, which we don't want. In particular the
* GetRemoteConsole call below can hang if the VM didn't completely
* terminate (the VM processes stop processing events shortly before
* closing the session). This avoids the hang. */
return ret;
/* get the associated console; this is a remote call (!) */
return ret;
{
}
}
enableVMMStats(true);
return ret;
}
{
return E_UNEXPECTED;
enableVMMStats(false);
if (!mEnabled)
{
}
return S_OK;
}
{
if (mManager)
{
}
this, __PRETTY_FUNCTION__));
return E_POINTER;
}
{
{
}
{
}
{
}
{
}
}
{
this, 0, RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE,
"CGMgr");
this, __PRETTY_FUNCTION__, rc));
}
{
int rcThread = 0;
{
/* We wait only if we were able to put the abort request to a queue */
this, __PRETTY_FUNCTION__));
}
}
{
pGuest->setManager(this);
/*
* If no VMM stats provider was elected previously than this is our
* candidate.
*/
if (!mVMMStatsProvider)
}
{
//mGuests.remove(pGuest); => destroyUnregistered()
pGuest->unregister();
if (pGuest == mVMMStatsProvider)
{
/* This was our VMM stats provider, it is time to re-elect */
/* Assume that nobody can provide VMM stats */
{
/* Skip unregistered as they are about to be destroyed */
if ((*it)->isUnregistered())
continue;
{
/* Found the guest already collecting stats, elect it */
mVMMStatsProvider = *it;
{
/* This is not a good candidate -- try to find another */
continue;
}
break;
}
}
if (!mVMMStatsProvider)
{
/* If nobody collects stats, take the first registered */
{
/* Skip unregistered as they are about to be destroyed */
if ((*it)->isUnregistered())
continue;
mVMMStatsProvider = *it;
//mVMMStatsProvider->enable(VMSTATS_VMM_RAM);
break;
/* This was not a good candidate -- try to find another */
}
}
}
this, __PRETTY_FUNCTION__, mVMMStatsProvider));
}
{
if ((*it)->isUnregistered())
{
delete *it;
}
else
++it;
}
{
#ifdef DEBUG
#endif /* DEBUG */
/*
* It is very unlikely that we will get high frequency calls to configure
* guest metrics collection, so we rely on this fact to detect blocked
* guests. If the guest has not finished processing the previous request
* after half a second we consider it blocked.
*/
{
/*
* Before we can declare a guest blocked we need to wait for a while
* and then check again as it may never had a chance to process
* the previous request. Half a second is an eternity for processes
* and is barely noticable by humans.
*/
this, __PRETTY_FUNCTION__,
this, __PRETTY_FUNCTION__,
/* Request execution got stalled for this guest -- report an error */
return E_FAIL;
}
}
return S_OK;
}
/* static */
DECLCALLBACK(int) CollectorGuestManager::requestProcessingThread(RTTHREAD /* aThread */, void *pvUser)
{
{
#ifdef DEBUG
#endif /* DEBUG */
delete pReq;
break;
}
return VINF_SUCCESS;
}
#endif /* !VBOX_COLLECTOR_TEST_CASE */
{
if (isEnabled())
{
if (mLastSampleTaken == 0)
{
return true;
}
/*
* We use low resolution timers which may fire just a little bit early.
* We compensate for that by jumping into the future by several
* milliseconds (see @bugref{6345}).
*/
{
/*
* We don't want the beat to drift. This is why the timestamp of
* the last taken sample is not the actual time but the time we
* should have taken the measurement at.
*/
return true;
}
}
return false;
}
{
}
void HostCpuLoad::collect()
{
if (RT_SUCCESS(rc))
{
}
}
{
}
{
}
void HostCpuLoadRaw::collect()
{
if (RT_SUCCESS(rc))
{
if (totalDiff == 0)
{
/* This is only possible if none of counters has changed! */
LogFlowThisFunc(("Impossible! User, kernel and idle raw "
"counters has not changed since last sample.\n" ));
}
else
{
}
}
}
#ifndef VBOX_COLLECTOR_TEST_CASE
{
if (RT_FAILURE(rc))
return false;
if (enmState != NETIF_S_UP)
*pSpeed = 0;
else
{
if (RT_FAILURE(rc))
return false;
}
return true;
}
{
/*
* Retrieve the link speed now as it may be wrong if the metric was
* registered at boot (see @bugref{6613}).
*/
}
{
/*
* Retrieve the link speed now as it may be wrong if the metric was
* registered at boot (see @bugref{6613}).
*/
//AssertRC(rc);
}
{
if (RT_FAILURE(mRc))
{
HRESULT hrc = host->FindHostNetworkInterfaceByName(com::Bstr(mInterfaceName).raw(), networkInterface.asOutParam());
{
LogRel(("Failed to collect network metrics for %s: %Rrc (%d).\n", mInterfaceName.c_str(), mRc, mRc));
mRc = VINF_SUCCESS;
}
}
}
void HostNetworkLoadRaw::collect()
{
{
LogFlowThisFunc(("Check cable for %s! speed=%llu period=%d.\n", mShortName.c_str(), mSpeed, getPeriod()));
/* We do not collect host network metrics for unplugged interfaces! */
return;
}
if (RT_SUCCESS(mRc))
{
}
else
LogFlowThisFunc(("Failed to collect data: %Rrc (%d)."
}
#endif /* !VBOX_COLLECTOR_TEST_CASE */
{
}
{
}
void HostDiskLoadRaw::collect()
{
if (RT_SUCCESS(rc))
{
if (RT_UNLIKELY(totalDiff == 0))
{
}
{
/*
* It is possible that the disk spent more time than CPU because
* CPU measurements are taken during the pre-collect phase. We try
* to compensate for than by adding the extra to the next round of
* measurements.
*/
{
LogRel(("Disk utilization time exceeds CPU time by more"
}
else
{
}
}
else
{
}
mTotalPrev = total;
}
else
}
{
}
void HostCpuMhz::collect()
{
if (RT_SUCCESS(rc))
}
{
}
{
}
void HostRamUsage::collect()
{
if (RT_SUCCESS(rc))
{
}
}
{
}
{
}
void HostFilesystemUsage::collect()
{
if (RT_SUCCESS(rc))
{
}
}
{
}
{
}
void HostDiskUsage::collect()
{
if (RT_SUCCESS(rc))
}
#ifndef VBOX_COLLECTOR_TEST_CASE
{
}
int HostRamVmm::enable()
{
if (provider)
BaseMetric::enable();
return rc;
}
int HostRamVmm::disable()
{
BaseMetric::disable();
if (provider)
return rc;
}
{
}
void HostRamVmm::collect()
{
if (provider)
{
{
/* Provider is ready, get updated stats */
}
/*
* Note that if there are no new values from the provider we will use
* the ones most recently provided instead of zeros, which is probably
* a desirable behavior.
*/
}
else
{
mAllocCurrent = 0;
mFreeCurrent = 0;
mBalloonedCurrent = 0;
mSharedCurrent = 0;
}
LogAleksey(("{%p} " LOG_FN_FMT ": mAllocCurrent=%u mFreeCurrent=%u mBalloonedCurrent=%u mSharedCurrent=%u\n",
this, __PRETTY_FUNCTION__,
}
#endif /* !VBOX_COLLECTOR_TEST_CASE */
{
}
void MachineCpuLoad::collect()
{
if (RT_SUCCESS(rc))
{
}
}
{
}
void MachineCpuLoadRaw::collect()
{
if (RT_SUCCESS(rc))
{
if (hostTotal == mHostTotalPrev)
{
/* Nearly impossible, but... */
}
else
{
mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
}
}
}
{
}
{
}
void MachineRamUsage::collect()
{
if (RT_SUCCESS(rc))
}
#ifndef VBOX_COLLECTOR_TEST_CASE
{
}
{
}
void MachineDiskUsage::collect()
{
{
/* just in case */
}
}
{
}
void MachineNetRate::collect()
{
{
}
}
int MachineNetRate::enable()
{
BaseMetric::enable();
return rc;
}
int MachineNetRate::disable()
{
BaseMetric::disable();
}
{
}
{
}
{
}
void GuestCpuLoad::collect()
{
{
}
}
int GuestCpuLoad::enable()
{
BaseMetric::enable();
return rc;
}
int GuestCpuLoad::disable()
{
BaseMetric::disable();
}
{
}
void GuestRamUsage::collect()
{
{
}
}
int GuestRamUsage::enable()
{
BaseMetric::enable();
return rc;
}
int GuestRamUsage::disable()
{
BaseMetric::disable();
}
{
}
#endif /* !VBOX_COLLECTOR_TEST_CASE */
{
if (mData)
if (mLength)
else
mWrapped = false;
mEnd = 0;
mSequenceNumber = 0;
}
{
}
{
if (mData)
{
{
mEnd = 0;
mWrapped = true;
}
}
}
{
if (mWrapped)
{
// Copy the wrapped part
if (mEnd)
}
else
}
{
}
{
if (length)
{
if (mAggregate)
{
*count = 1;
}
else
{
}
}
else
{
*count = 0;
*data = 0;
}
}
{
}
const char * AggregateAvg::getName()
{
return "avg";
}
{
return tmp;
}
const char * AggregateMin::getName()
{
return "min";
}
{
return tmp;
}
const char * AggregateMax::getName()
{
return "max";
}
{
/*
* a way to pass null arrays via webservice, I haven't found one. So I
* guess the users will be forced to use empty arrays instead. Constructing
* an empty SafeArray is a bit awkward, so what we do in this method is
* actually convert null arrays to empty arrays and pass them down to
* init() method. If someone knows how to do it better, please be my guest,
* fix it.
*/
{
if (ComSafeArrayInIsNull(objects))
{
objectArray.reset(0);
}
else
{
}
}
else
{
if (ComSafeArrayInIsNull(objects))
{
objectArray.reset(0);
}
else
{
}
}
}
{
}
{
if (!objectArray.size())
{
{
}
else
}
else
{
{
case 0:
break;
case 1:
break;
default:
break;
}
}
}
{
{
mElements.push_back(std::make_pair(object, RTCString(name.substr(startPos, pos - startPos).c_str())));
}
}
/**
* modified to handle the special case of trailing colon in the pattern.
*
* @returns True if matches, false if not.
* @param pszPat Pattern.
* @param pszName Name to match against the pattern.
* @param fSeenColon Seen colon (':').
*/
bool fSeenColon)
{
/* ASSUMES ASCII */
for (;;)
{
switch (chPat)
{
default:
return false;
break;
case '*':
{
/* nothing */;
/* Handle a special case, the mask terminating with a colon. */
if (chPat == ':')
{
fSeenColon = true;
}
for (;;)
{
&& ( !chPat
return true;
if (!ch)
return false;
}
/* won't ever get here */
break;
}
case '?':
if (!*pszName)
return false;
break;
/* Handle a special case, the mask terminating with a colon. */
case ':':
return !*pszName;
if (*pszName != ':')
return false;
fSeenColon = true;
break;
case '\0':
return !*pszName;
}
pszName++;
pszPat++;
}
return true;
}
{
//LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
{
//LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
{
// Objects match, compare names
{
//LogFlowThisFunc(("...found!\n"));
return true;
}
}
}
//LogAleksey(("...no matches!\n"));
return false;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */