VBoxServiceStats.cpp revision 92bfec9d6995e166f169f9da4bc1f8505d409748
/* $Id$ */
/** @file
* VBoxStats - Guest statistics notification
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#if defined(RT_OS_WINDOWS)
# ifdef TARGET_NT4
# define _WIN32_WINNT 0x501
# endif
# include <windows.h>
# include <psapi.h>
# include <winternl.h>
#elif defined(RT_OS_LINUX)
# include <unistd.h>
#elif defined(RT_OS_SOLARIS)
# include <kstat.h>
# include <unistd.h>
#else
/** @todo port me. */
#endif
#include <iprt/semaphore.h>
#include <VBox/VBoxGuestLib.h>
#include "VBoxServiceInternal.h"
#include "VBoxServiceUtils.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct _VBOXSTATSCONTEXT
{
#ifdef RT_OS_WINDOWS
NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
static VBOXSTATSCONTEXT gCtx = {0};
/** The semaphore we're blocking on. */
/** @copydoc VBOXSERVICE::pfnPreInit */
static DECLCALLBACK(int) VBoxServiceVMStatsPreInit(void)
{
return VINF_SUCCESS;
}
/** @copydoc VBOXSERVICE::pfnOption */
static DECLCALLBACK(int) VBoxServiceVMStatsOption(const char **ppszShort, int argc, char **argv, int *pi)
{
return VINF_SUCCESS;
}
/** @copydoc VBOXSERVICE::pfnInit */
static DECLCALLBACK(int) VBoxServiceVMStatsInit(void)
{
if (RT_SUCCESS(rc))
else
#ifdef RT_OS_WINDOWS
/* NtQuerySystemInformation might be dropped in future releases, so load it dynamically as per Microsoft's recommendation */
if (hMod)
{
*(uintptr_t *)&gCtx.pfnNtQuerySystemInformation = (uintptr_t)GetProcAddress(hMod, "NtQuerySystemInformation");
VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnNtQuerySystemInformation = %x\n", gCtx.pfnNtQuerySystemInformation);
else
{
VBoxServiceError("VBoxStatsInit: NTDLL.NtQuerySystemInformation not found!!\n");
return VERR_NOT_IMPLEMENTED;
}
}
/* GlobalMemoryStatus is win2k and up, so load it dynamically */
if (hMod)
{
*(uintptr_t *)&gCtx.pfnGlobalMemoryStatusEx = (uintptr_t)GetProcAddress(hMod, "GlobalMemoryStatusEx");
VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.GlobalMemoryStatusEx = %x\n", gCtx.pfnGlobalMemoryStatusEx);
else
{
/** @todo now fails in NT4; do we care? */
VBoxServiceError("VBoxStatsInit: KERNEL32.GlobalMemoryStatusEx not found!!\n");
return VERR_NOT_IMPLEMENTED;
}
}
/* GetPerformanceInfo is xp and up, so load it dynamically */
if (hMod)
{
VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnGetPerformanceInfo= %x\n", gCtx.pfnGetPerformanceInfo);
/* failure is not fatal */
}
#endif /* RT_OS_WINDOWS */
return VINF_SUCCESS;
}
/**
* Gathers VM statistics and reports them to the host.
*
* @returns
* @param oid .
*/
static void VBoxServiceVMStatsReport(void)
{
#if defined(RT_OS_WINDOWS)
if ( !gCtx.pfnGlobalMemoryStatusEx
return;
/* Clear the report so we don't report garbage should NtQuerySystemInformation
behave in an unexpected manner. */
/* Query and report guest statistics */
/* The current size of the committed memory limit, in bytes. This is physical
memory plus the size of the page file, minus a small overhead. */
req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / _4K) - req.guestStats.u32PhysMemTotal;
req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_PAGE_FILE_SIZE
#ifdef VBOX_WITH_MEMBALLOON
#else
#endif
{
{
req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES
}
else
}
/* Query CPU load information */
if (!pProcInfo)
return;
/* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
if ( !rc
&& cbReturned == cbStruct)
{
if (gCtx.au64LastCpuLoad_Kernel == 0)
{
/* first time */
Sleep(250);
rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
}
req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER;
/** @todo SMP: report details for each CPU? */
}
{
if (RT_SUCCESS(rc))
VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", i);
else
VBoxServiceVerbose(3, "VBoxStatsReportStatistics: DeviceIoControl (stats report) failed with %d\n", GetLastError());
}
#elif defined(RT_OS_LINUX)
char szLine[256];
char *psz;
if (RT_SUCCESS(rc))
{
for (;;)
{
if (RT_FAILURE(rc))
break;
{
if (RT_SUCCESS(rc))
}
{
if (RT_SUCCESS(rc))
}
{
if (RT_SUCCESS(rc))
}
{
if (RT_SUCCESS(rc))
}
{
if (RT_SUCCESS(rc))
}
}
}
else
#ifdef VBOX_WITH_MEMBALLOON
#else
#endif
/** @todo req.guestStats.u32Threads */
/** @todo req.guestStats.u32Processes */
/* req.guestStats.u32Handles doesn't make sense here. */
/** @todo req.guestStats.u32MemoryLoad */
/** @todo req.guestStats.u32MemCommitTotal */
/** @todo req.guestStats.u32MemKernelTotal */
/** @todo req.guestStats.u32MemKernelPaged, make any sense? = u32MemKernelTotal? */
/** @todo req.guestStats.u32MemKernelNonPaged, make any sense? = 0? */
bool fCpuInfoAvail = false;
if (RT_SUCCESS(rc))
{
for (;;)
{
if (RT_FAILURE(rc))
break;
{
if (u32CpuId < VMM_MAX_CPU_COUNT)
{
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
+ u64DeltaNice;
fCpuInfoAvail = true;
if (RT_SUCCESS(rc))
VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", u32CpuId);
else
}
else
}
}
}
if (!fCpuInfoAvail)
{
if (RT_SUCCESS(rc))
else
}
#elif defined(RT_OS_SOLARIS)
if (pStatKern)
{
/*
* Memory statistics.
*/
int rc = -1;
if (pStatPages)
{
if (rc != -1)
{
if (pStat)
if (pStat)
}
}
if (pStatZFS)
{
if (rc != -1)
{
if (pStat)
}
}
/*
* The vminfo are accumulative counters updated every "N" ticks. Let's get the
* number of stat updates so far and use that to divide the swap counter.
*/
if (pStatInfo)
{
if (rc != -1)
{
if (pStatVMInfo)
{
if (rc != -1)
{
}
}
}
}
/** @todo req.guestStats.u32Threads */
/** @todo req.guestStats.u32Processes */
/** @todo req.guestStats.u32Handles -- ??? */
/** @todo req.guestStats.u32MemoryLoad */
/** @todo req.guestStats.u32MemCommitTotal */
/** @todo req.guestStats.u32MemKernelTotal */
/** @todo req.guestStats.u32MemKernelPaged */
/** @todo req.guestStats.u32MemKernelNonPaged */
#ifdef VBOX_WITH_MEMBALLOON
#else
#endif
/*
* CPU statistics.
*/
bool fCpuInfoAvail = false;
{
{
if (rc == -1)
break;
fCpuInfoAvail = true;
if (RT_SUCCESS(rc))
VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", cCPUs);
else
cCPUs++;
}
}
/*
* Report whatever statistics were collected.
*/
if (!fCpuInfoAvail)
{
if (RT_SUCCESS(rc))
else
}
}
#else
/* todo: implement for other platforms. */
#endif
}
/** @copydoc VBOXSERVICE::pfnWorker */
{
int rc = VINF_SUCCESS;
/* Start monitoring of the stat event change event. */
if (RT_FAILURE(rc))
{
return rc;
}
/*
* Tell the control thread that it can continue
* spawning services.
*/
/*
* Now enter the loop retrieving runtime data continuously.
*/
for (;;)
{
/* Check if an update interval change is pending. */
if ( RT_SUCCESS(rc)
{
}
if (gCtx.cMsStatInterval)
{
}
else
cWaitMillies = 3000;
/*
* Block for a while.
*
* The event semaphore takes care of ignoring interruptions and it
* allows us to implement service wakeup later.
*/
if (*pfShutdown)
break;
if (*pfShutdown)
break;
{
break;
}
}
/* Cancel monitoring of the stat event change event. */
if (RT_FAILURE(rc))
return 0;
}
/** @copydoc VBOXSERVICE::pfnTerm */
static DECLCALLBACK(void) VBoxServiceVMStatsTerm(void)
{
return;
}
/** @copydoc VBOXSERVICE::pfnStop */
static DECLCALLBACK(void) VBoxServiceVMStatsStop(void)
{
}
/**
* The 'vminfo' service description.
*/
{
/* pszName. */
"vmstats",
/* pszDescription. */
"Virtual Machine Statistics",
/* pszUsage. */
NULL,
/* pszOptions. */
NULL,
/* methods */
};