diff options
Diffstat (limited to 'src/VBox/Main/src-client/GuestImpl.cpp')
-rw-r--r-- | src/VBox/Main/src-client/GuestImpl.cpp | 324 |
1 files changed, 222 insertions, 102 deletions
diff --git a/src/VBox/Main/src-client/GuestImpl.cpp b/src/VBox/Main/src-client/GuestImpl.cpp index 4f096e4e..41469b0c 100644 --- a/src/VBox/Main/src-client/GuestImpl.cpp +++ b/src/VBox/Main/src-client/GuestImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -29,9 +29,12 @@ #include "AutoCaller.h" #include "Logging.h" #include "Performance.h" +#include "VBoxEvents.h" #include <VBox/VMMDev.h> #include <iprt/cpp/utils.h> +#include <iprt/ctype.h> +#include <iprt/stream.h> #include <iprt/timer.h> #include <VBox/vmm/pgm.h> #include <VBox/version.h> @@ -77,31 +80,41 @@ HRESULT Guest::init(Console *aParent) autoInitSpan.setSucceeded(); ULONG aMemoryBalloonSize; - HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize); - if (ret == S_OK) + HRESULT hr = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize); + if (hr == S_OK) /** @todo r=andy SUCCEEDED? */ mMemoryBalloonSize = aMemoryBalloonSize; else - mMemoryBalloonSize = 0; /* Default is no ballooning */ + mMemoryBalloonSize = 0; /* Default is no ballooning */ BOOL fPageFusionEnabled; - ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled); - if (ret == S_OK) + hr = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled); + if (hr == S_OK) /** @todo r=andy SUCCEEDED? */ mfPageFusionEnabled = fPageFusionEnabled; else - mfPageFusionEnabled = false; /* Default is no page fusion*/ + mfPageFusionEnabled = false; /* Default is no page fusion*/ - mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */ + mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */ mCollectVMMStats = false; /* Clear statistics. */ + mNetStatRx = mNetStatTx = 0; + mNetStatLastTs = RTTimeNanoTS(); for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++) mCurrentGuestStat[i] = 0; - mGuestValidStats = pm::GUESTSTATMASK_NONE; + mVmValidStats = pm::VMSTATMASK_NONE; mMagic = GUEST_MAGIC; int vrc = RTTimerLRCreate(&mStatTimer, 1000 /* ms */, &Guest::staticUpdateStats, this); - AssertMsgRC(vrc, ("Failed to create guest statistics update timer(%Rra)\n", vrc)); + AssertMsgRC(vrc, ("Failed to create guest statistics update timer (%Rrc)\n", vrc)); + +#ifdef VBOX_WITH_GUEST_CONTROL + hr = unconst(mEventSource).createObject(); + if (SUCCEEDED(hr)) + hr = mEventSource->init(); +#else + hr = S_OK; +#endif try { @@ -112,10 +125,10 @@ HRESULT Guest::init(Console *aParent) } catch(std::bad_alloc &) { - return E_OUTOFMEMORY; + hr = E_OUTOFMEMORY; } - return S_OK; + return hr; } /** @@ -145,7 +158,7 @@ void Guest::uninit() { #ifdef DEBUG ULONG cRefs = itSessions->second->AddRef(); - LogFlowThisFunc(("pSession=%p, cRefs=%RU32\n", (GuestSession *)itSessions->second, cRefs > 0 ? cRefs - 1 : 0)); + LogFlowThisFunc(("sessionID=%RU32, cRefs=%RU32\n", itSessions->first, cRefs > 1 ? cRefs - 1 : 0)); itSessions->second->Release(); #endif itSessions->second->uninit(); @@ -162,13 +175,16 @@ void Guest::uninit() } #endif +#ifdef VBOX_WITH_GUEST_CONTROL + unconst(mEventSource).setNull(); +#endif unconst(mParent) = NULL; LogFlowFuncLeave(); } /* static */ -void Guest::staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick) +DECLCALLBACK(void) Guest::staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick) { AssertReturnVoid(pvUser != NULL); Guest *guest = static_cast<Guest *>(pvUser); @@ -179,84 +195,155 @@ void Guest::staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick) NOREF(hTimerLR); } +/* static */ +int Guest::staticEnumStatsCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, + STAMVISIBILITY enmVisiblity, const char *pszDesc, void *pvUser) +{ + AssertLogRelMsgReturn(enmType == STAMTYPE_COUNTER, ("Unexpected sample type %d ('%s')\n", enmType, pszName), VINF_SUCCESS); + AssertLogRelMsgReturn(enmUnit == STAMUNIT_BYTES, ("Unexpected sample unit %d ('%s')\n", enmUnit, pszName), VINF_SUCCESS); + + /* Get the base name w/ slash. */ + const char *pszLastSlash = strrchr(pszName, '/'); + AssertLogRelMsgReturn(pszLastSlash, ("Unexpected sample '%s'\n", pszName), VINF_SUCCESS); + + /* Receive or transmit? */ + bool fRx; + if (!strcmp(pszLastSlash, "/BytesReceived")) + fRx = true; + else if (!strcmp(pszLastSlash, "/BytesTransmitted")) + fRx = false; + else + AssertLogRelMsgFailedReturn(("Unexpected sample '%s'\n", pszName), VINF_SUCCESS); + +#if 0 /* not used for anything, so don't bother parsing it. */ + /* Find start of instance number. ASSUMES '/Public/Net/Name<Instance digits>/Bytes...' */ + do + --pszLastSlash; + while (pszLastSlash > pszName && RT_C_IS_DIGIT(*pszLastSlash)); + pszLastSlash++; + + uint8_t uInstance; + int rc = RTStrToUInt8Ex(pszLastSlash, NULL, 10, &uInstance); + AssertLogRelMsgReturn(RT_SUCCESS(rc) && rc != VWRN_NUMBER_TOO_BIG && rc != VWRN_NEGATIVE_UNSIGNED, + ("%Rrc '%s'\n", rc, pszName), VINF_SUCCESS) +#endif + + /* Add the bytes to our counters. */ + PSTAMCOUNTER pCnt = (PSTAMCOUNTER)pvSample; + Guest *pGuest = (Guest *)pvUser; + uint64_t cb = pCnt->c; +#if 0 + LogFlowFunc(("%s i=%u d=%s %llu bytes\n", pszName, uInstance, fRx ? "RX" : "TX", cb)); +#else + LogFlowFunc(("%s d=%s %llu bytes\n", pszName, fRx ? "RX" : "TX", cb)); +#endif + if (fRx) + pGuest->mNetStatRx += cb; + else + pGuest->mNetStatTx += cb; + + return VINF_SUCCESS; +} + void Guest::updateStats(uint64_t iTick) { - uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal; - uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem; + uint64_t cbFreeTotal = 0; + uint64_t cbAllocTotal = 0; + uint64_t cbBalloonedTotal = 0; + uint64_t cbSharedTotal = 0; + uint64_t cbSharedMem = 0; + ULONG uNetStatRx = 0; + ULONG uNetStatTx = 0; + ULONG aGuestStats[GUESTSTATTYPE_MAX]; + RT_ZERO(aGuestStats); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - ULONG aGuestStats[GUESTSTATTYPE_MAX]; - RT_ZERO(aGuestStats); - ULONG validStats = mGuestValidStats; + ULONG validStats = mVmValidStats; /* Check if we have anything to report */ if (validStats) { - mGuestValidStats = pm::GUESTSTATMASK_NONE; + mVmValidStats = pm::VMSTATMASK_NONE; memcpy(aGuestStats, mCurrentGuestStat, sizeof(aGuestStats)); } alock.release(); + /* * Calling SessionMachine may take time as the object resides in VBoxSVC * process. This is why we took a snapshot of currently collected stats * and released the lock. */ - uFreeTotal = 0; - uAllocTotal = 0; - uBalloonedTotal = 0; - uSharedTotal = 0; - uTotalMem = 0; - uPrivateMem = 0; - uSharedMem = 0; - uZeroMem = 0; - - Console::SafeVMPtr pVM(mParent); - if (pVM.isOk()) + Console::SafeVMPtrQuiet ptrVM(mParent); + if (ptrVM.isOk()) { int rc; /* * There is no point in collecting VM shared memory if other memory - * statistics are not available yet. Or is it? + * statistics are not available yet. Or is there? */ if (validStats) { /* Query the missing per-VM memory statistics. */ - rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem); + uint64_t cbTotalMemIgn, cbPrivateMemIgn, cbZeroMemIgn; + rc = PGMR3QueryMemoryStats(ptrVM.rawUVM(), &cbTotalMemIgn, &cbPrivateMemIgn, &cbSharedMem, &cbZeroMemIgn); if (rc == VINF_SUCCESS) - { - validStats |= pm::GUESTSTATMASK_MEMSHARED; - } + validStats |= pm::VMSTATMASK_GUEST_MEMSHARED; } if (mCollectVMMStats) { - rc = PGMR3QueryGlobalMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal); + rc = PGMR3QueryGlobalMemoryStats(ptrVM.rawUVM(), &cbAllocTotal, &cbFreeTotal, &cbBalloonedTotal, &cbSharedTotal); AssertRC(rc); if (rc == VINF_SUCCESS) - { - validStats |= pm::GUESTSTATMASK_ALLOCVMM|pm::GUESTSTATMASK_FREEVMM| - pm::GUESTSTATMASK_BALOONVMM|pm::GUESTSTATMASK_SHAREDVMM; - } + validStats |= pm::VMSTATMASK_VMM_ALLOC | pm::VMSTATMASK_VMM_FREE + | pm::VMSTATMASK_VMM_BALOON | pm::VMSTATMASK_VMM_SHARED; } + uint64_t uRxPrev = mNetStatRx; + uint64_t uTxPrev = mNetStatTx; + mNetStatRx = mNetStatTx = 0; + rc = STAMR3Enum(ptrVM.rawUVM(), "/Public/Net/*/Bytes*", staticEnumStatsCallback, this); + AssertRC(rc); + + uint64_t uTsNow = RTTimeNanoTS(); + uint64_t cNsPassed = uTsNow - mNetStatLastTs; + if (cNsPassed >= 1000) + { + mNetStatLastTs = uTsNow; + + uNetStatRx = (ULONG)((mNetStatRx - uRxPrev) * 1000000 / (cNsPassed / 1000)); /* in bytes per second */ + uNetStatTx = (ULONG)((mNetStatTx - uTxPrev) * 1000000 / (cNsPassed / 1000)); /* in bytes per second */ + validStats |= pm::VMSTATMASK_NET_RX | pm::VMSTATMASK_NET_TX; + LogFlowThisFunc(("Net Rx=%llu Tx=%llu Ts=%llu Delta=%llu\n", mNetStatRx, mNetStatTx, uTsNow, cNsPassed)); + } + else + { + /* Can happen on resume or if we're using a non-monotonic clock + source for the timer and the time is adjusted. */ + mNetStatRx = uRxPrev; + mNetStatTx = uTxPrev; + LogThisFunc(("Net Ts=%llu cNsPassed=%llu - too small interval\n", uTsNow, cNsPassed)); + } } - mParent->reportGuestStatistics(validStats, - aGuestStats[GUESTSTATTYPE_CPUUSER], - aGuestStats[GUESTSTATTYPE_CPUKERNEL], - aGuestStats[GUESTSTATTYPE_CPUIDLE], - /* Convert the units for RAM usage stats: page (4K) -> 1KB units */ - mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K), - mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K), - mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K), - (ULONG)(uSharedMem / _1K), /* bytes -> KB */ - mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K), - mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K), - (ULONG)(uAllocTotal / _1K), /* bytes -> KB */ - (ULONG)(uFreeTotal / _1K), - (ULONG)(uBalloonedTotal / _1K), - (ULONG)(uSharedTotal / _1K)); + mParent->reportVmStatistics(validStats, + aGuestStats[GUESTSTATTYPE_CPUUSER], + aGuestStats[GUESTSTATTYPE_CPUKERNEL], + aGuestStats[GUESTSTATTYPE_CPUIDLE], + /* Convert the units for RAM usage stats: page (4K) -> 1KB units */ + mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K), + mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K), + mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K), + (ULONG)(cbSharedMem / _1K), /* bytes -> KB */ + mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K), + mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K), + (ULONG)(cbAllocTotal / _1K), /* bytes -> KB */ + (ULONG)(cbFreeTotal / _1K), + (ULONG)(cbBalloonedTotal / _1K), + (ULONG)(cbSharedTotal / _1K), + uNetStatRx, + uNetStatTx); } // IGuest properties @@ -407,6 +494,26 @@ STDMETHODIMP Guest::COMGETTER(AdditionsRevision)(ULONG *a_puAdditionsRevision) return hrc; } +STDMETHODIMP Guest::COMGETTER(EventSource)(IEventSource ** aEventSource) +{ +#ifndef VBOX_WITH_GUEST_CONTROL + ReturnComNotImplemented(); +#else + LogFlowThisFuncEnter(); + + CheckComArgOutPointerValid(aEventSource); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + // no need to lock - lifetime constant + mEventSource.queryInterfaceTo(aEventSource); + + LogFlowFuncLeaveRC(S_OK); + return S_OK; +#endif /* VBOX_WITH_GUEST_CONTROL */ +} + STDMETHODIMP Guest::COMGETTER(Facilities)(ComSafeArrayOut(IAdditionsFacility *, aFacilities)) { CheckComArgOutSafeArrayPointerValid(aFacilities); @@ -570,47 +677,35 @@ STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, UL *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */ *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */ + /* Play safe or smth? */ + *aMemAllocTotal = 0; + *aMemFreeTotal = 0; + *aMemBalloonTotal = 0; + *aMemSharedTotal = 0; + *aMemShared = 0; + /* MUST release all locks before calling any PGM statistics queries, * as they are executed by EMT and that might deadlock us by VMM device * activity which waits for the Guest object lock. */ alock.release(); - Console::SafeVMPtr pVM (mParent); - if (pVM.isOk()) - { - uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal; - *aMemFreeTotal = 0; - int rc = PGMR3QueryGlobalMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal); - AssertRC(rc); - if (rc == VINF_SUCCESS) - { - *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */ - *aMemFreeTotal = (ULONG)(uFreeTotal / _1K); - *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K); - *aMemSharedTotal = (ULONG)(uSharedTotal / _1K); - } - else - return E_FAIL; - - /* Query the missing per-VM memory statistics. */ - *aMemShared = 0; - uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem; - rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem); - if (rc == VINF_SUCCESS) - { - *aMemShared = (ULONG)(uSharedMem / _1K); - } - else - return E_FAIL; - } - else - { - *aMemAllocTotal = 0; - *aMemFreeTotal = 0; - *aMemBalloonTotal = 0; - *aMemSharedTotal = 0; - *aMemShared = 0; + Console::SafeVMPtr ptrVM(mParent); + if (!ptrVM.isOk()) return E_FAIL; - } + + uint64_t cbFreeTotal, cbAllocTotal, cbBalloonedTotal, cbSharedTotal; + int rc = PGMR3QueryGlobalMemoryStats(ptrVM.rawUVM(), &cbAllocTotal, &cbFreeTotal, &cbBalloonedTotal, &cbSharedTotal); + AssertRCReturn(rc, E_FAIL); + + *aMemAllocTotal = (ULONG)(cbAllocTotal / _1K); /* bytes -> KB */ + *aMemFreeTotal = (ULONG)(cbFreeTotal / _1K); + *aMemBalloonTotal = (ULONG)(cbBalloonedTotal / _1K); + *aMemSharedTotal = (ULONG)(cbSharedTotal / _1K); + + /* Query the missing per-VM memory statistics. */ + uint64_t cbTotalMemIgn, cbPrivateMemIgn, cbSharedMem, cbZeroMemIgn; + rc = PGMR3QueryMemoryStats(ptrVM.rawUVM(), &cbTotalMemIgn, &cbPrivateMemIgn, &cbSharedMem, &cbZeroMemIgn); + AssertRCReturn(rc, E_FAIL); + *aMemShared = (ULONG)(cbSharedMem / _1K); return S_OK; } @@ -619,15 +714,15 @@ HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal) { static ULONG indexToPerfMask[] = { - pm::GUESTSTATMASK_CPUUSER, - pm::GUESTSTATMASK_CPUKERNEL, - pm::GUESTSTATMASK_CPUIDLE, - pm::GUESTSTATMASK_MEMTOTAL, - pm::GUESTSTATMASK_MEMFREE, - pm::GUESTSTATMASK_MEMBALLOON, - pm::GUESTSTATMASK_MEMCACHE, - pm::GUESTSTATMASK_PAGETOTAL, - pm::GUESTSTATMASK_NONE + pm::VMSTATMASK_GUEST_CPUUSER, + pm::VMSTATMASK_GUEST_CPUKERNEL, + pm::VMSTATMASK_GUEST_CPUIDLE, + pm::VMSTATMASK_GUEST_MEMTOTAL, + pm::VMSTATMASK_GUEST_MEMFREE, + pm::VMSTATMASK_GUEST_MEMBALLOON, + pm::VMSTATMASK_GUEST_MEMCACHE, + pm::VMSTATMASK_GUEST_PAGETOTAL, + pm::VMSTATMASK_NONE }; AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -638,7 +733,7 @@ HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal) return E_INVALIDARG; mCurrentGuestStat[enmType] = aVal; - mGuestValidStats |= indexToPerfMask[enmType]; + mVmValidStats |= indexToPerfMask[enmType]; return S_OK; } @@ -1052,6 +1147,31 @@ void Guest::facilityUpdate(VBoxGuestFacilityType a_enmFacility, VBoxGuestFacilit } /** + * Issued by the guest when a guest user changed its state. + * + * @return IPRT status code. + * @param aUser Guest user name. + * @param aDomain Domain of guest user account. Optional. + * @param enmState New state to indicate. + * @param puDetails Pointer to state details. Optional. + * @param cbDetails Size (in bytes) of state details. Pass 0 if not used. + */ +void Guest::onUserStateChange(Bstr aUser, Bstr aDomain, VBoxGuestUserState enmState, + const uint8_t *puDetails, uint32_t cbDetails) +{ + LogFlowThisFunc(("\n")); + + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + Bstr strDetails; /** @todo Implement state details here. */ + + fireGuestUserStateChangedEvent(mEventSource, aUser.raw(), aDomain.raw(), + (GuestUserState_T)enmState, strDetails.raw()); + LogFlowFuncLeave(); +} + +/** * Sets the status of a certain Guest Additions facility. * * Gets called by vmmdevUpdateGuestStatus, which just passes the report along. |