tstPageFusion.cpp revision b35e3948f1287430503b6b432945b8cf4bfd3a23
/* $Id$ */
/** @file
* VBoxService - Guest page sharing testcase
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/assert.h>
#include <iprt/asm.h>
#include <iprt/mem.h>
#include <iprt/stream.h>
#include <iprt/string.h>
#include <iprt/initterm.h>
#include <VBox/VBoxGuestLib.h>
#include <VBox/x86.h>
#include <stdio.h>
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef RT_OS_WINDOWS
#include <Windows.h>
#include <process.h> /* Needed for file version information. */
#include <tlhelp32.h>
#include <psapi.h>
#include <winternl.h>
#define SystemModuleInformation 11
typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
ULONG Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
CHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
typedef struct _RTL_PROCESS_MODULES
{
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
typedef NTSTATUS (WINAPI *PFNZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
static PFNZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
static HMODULE hNtdll = 0;
#define PAGE_STATE_INVALID 0
#define PAGE_STATE_SHARED 1
#define PAGE_STATE_READ_WRITE 2
#define PAGE_STATE_READ_ONLY 3
#define PAGE_STATE_NOT_PRESENT 4
/* Page counters. */
static unsigned cNotPresentPages = 0;
static unsigned cWritablePages = 0;
static unsigned cSharedPages = 0;
static unsigned cPrivatePages = 0;
/**
* Registers a new module with the VMM
* @param pModule Module ptr
*/
void VBoxServicePageSharingCheckModule(MODULEENTRY32 *pModule)
{
DWORD dwModuleSize = pModule->modBaseSize;
BYTE *pBaseAddress = pModule->modBaseAddr;
bool fFirstLine = true;
unsigned uPageState, uLastPageState;
bool fLastWritable = false;
BYTE *pLastBaseAddress = pBaseAddress;
uPageState = uLastPageState = PAGE_STATE_INVALID;
printf("Check module %s base %p size %x\n", pModule->szModule, pBaseAddress, dwModuleSize);
do
{
bool fShared;
uint64_t uPageFlags;
#ifdef RT_ARCH_X86
int rc = VbglR3PageIsShared((uint32_t)pLastBaseAddress, &fShared, &uPageFlags);
#else
int rc = VbglR3PageIsShared((RTGCPTR)pLastBaseAddress, &fShared, &uPageFlags);
#endif
if (RT_FAILURE(rc))
printf("VbglR3PageIsShared %p failed with %d\n", pLastBaseAddress, rc);
if (RT_SUCCESS(rc))
{
if (uPageFlags & X86_PTE_P)
{
if (uPageFlags & X86_PTE_RW)
{
cWritablePages++;
uPageState = PAGE_STATE_READ_WRITE;
}
else
if (fShared)
{
cSharedPages++;
uPageState = PAGE_STATE_SHARED;
}
else
{
cPrivatePages++;
uPageState = PAGE_STATE_READ_ONLY;
}
}
else
{
cNotPresentPages++;
uPageState = PAGE_STATE_NOT_PRESENT;
}
if ( !fFirstLine
&& uPageState != uLastPageState)
{
printf("0x%p\n", pLastBaseAddress + 0xfff);
}
if (uPageState != uLastPageState)
{
switch (uPageState)
{
case PAGE_STATE_READ_WRITE:
printf("%s RW 0x%p - ", pModule->szModule, pBaseAddress);
break;
case PAGE_STATE_SHARED:
printf("%s SHARED 0x%p - ", pModule->szModule, pBaseAddress);
break;
case PAGE_STATE_READ_ONLY:
printf("%s PRIV 0x%p - ", pModule->szModule, pBaseAddress);
break;
case PAGE_STATE_NOT_PRESENT:
printf("%s NP 0x%p - ", pModule->szModule, pBaseAddress);
break;
}
fFirstLine = false;
}
uLastPageState = uPageState;
}
else
if (!fFirstLine)
{
printf("0x%p\n", pLastBaseAddress + 0xfff);
fFirstLine = true;
}
if (dwModuleSize > PAGE_SIZE)
dwModuleSize -= PAGE_SIZE;
else
dwModuleSize = 0;
pLastBaseAddress = pBaseAddress;
pBaseAddress += PAGE_SIZE;
}
while (dwModuleSize);
printf("0x%p\n", pLastBaseAddress + 0xfff);
return;
}
/**
* Inspect all loaded modules for the specified process
* @param dwProcessId Process id
*/
void VBoxServicePageSharingInspectModules(DWORD dwProcessId)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
if (hSnapshot == INVALID_HANDLE_VALUE)
{
printf("VBoxServicePageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
return;
}
printf("VBoxServicePageSharingInspectModules\n");
MODULEENTRY32 ModuleInfo;
BOOL bRet;
ModuleInfo.dwSize = sizeof(ModuleInfo);
bRet = Module32First(hSnapshot, &ModuleInfo);
do
{
/** todo when changing this make sure VBoxService.exe is excluded! */
char *pszDot = strrchr(ModuleInfo.szModule, '.');
if ( pszDot
&& (pszDot[1] == 'e' || pszDot[1] == 'E'))
continue; /* ignore executables for now. */
VBoxServicePageSharingCheckModule(&ModuleInfo);
}
while (Module32Next(hSnapshot, &ModuleInfo));
CloseHandle(hSnapshot);
}
/**
* Inspect all running processes for executables and dlls that might be worth sharing
* with other VMs.
*
*/
void VBoxServicePageSharingInspectGuest()
{
VBoxServicePageSharingInspectModules(GetCurrentProcessId());
printf("\n\nUSER RESULTS\n");
printf("cNotPresentPages = %d\n", cNotPresentPages);
printf("cWritablePages = %d\n", cWritablePages);
printf("cPrivatePages = %d\n", cPrivatePages);
printf("cSharedPages = %d\n", cSharedPages);
cNotPresentPages = 0;
cWritablePages = 0;
cPrivatePages = 0;
cSharedPages = 0;
/* Check all loaded kernel modules. */
if (ZwQuerySystemInformation)
{
ULONG cbBuffer = 0;
PVOID pBuffer = NULL;
PRTL_PROCESS_MODULES pSystemModules;
NTSTATUS ret = ZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer);
if (!cbBuffer)
{
printf("ZwQuerySystemInformation returned length 0\n");
goto skipkernelmodules;
}
pBuffer = RTMemAllocZ(cbBuffer);
if (!pBuffer)
goto skipkernelmodules;
ret = ZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer);
if (ret != 0)
{
printf("ZwQuerySystemInformation returned %x (1)\n", ret);
goto skipkernelmodules;
}
pSystemModules = (PRTL_PROCESS_MODULES)pBuffer;
for (unsigned i = 0; i < pSystemModules->NumberOfModules; i++)
{
/* User-mode modules seem to have no flags set; skip them as we detected them above. */
if (pSystemModules->Modules[i].Flags == 0)
continue;
/* New module; register it. */
char szFullFilePath[512];
MODULEENTRY32 ModuleInfo;
strcpy(ModuleInfo.szModule, &pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName]);
GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
/* skip \Systemroot\system32 */
char *lpPath = strchr(&pSystemModules->Modules[i].FullPathName[1], '\\');
if (!lpPath)
{
printf("Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName);
break;
}
lpPath = strchr(lpPath+1, '\\');
if (!lpPath)
{
printf("Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName);
break;
}
strcat(szFullFilePath, lpPath);
strcpy(ModuleInfo.szExePath, szFullFilePath);
ModuleInfo.modBaseAddr = (BYTE *)pSystemModules->Modules[i].ImageBase;
ModuleInfo.modBaseSize = pSystemModules->Modules[i].ImageSize;
VBoxServicePageSharingCheckModule(&ModuleInfo);
}
skipkernelmodules:
if (pBuffer)
RTMemFree(pBuffer);
}
printf("\n\nKERNEL RESULTS\n");
printf("cNotPresentPages = %d\n", cNotPresentPages);
printf("cWritablePages = %d\n", cWritablePages);
printf("cPrivatePages = %d\n", cPrivatePages);
printf("cSharedPages = %d\n", cSharedPages);
}
#else
void VBoxServicePageSharingInspectGuest()
{
/* @todo other platforms */
}
#endif
/** @copydoc VBOXSERVICE::pfnInit */
static DECLCALLBACK(int) VBoxServicePageSharingInit(void)
{
printf("VBoxServicePageSharingInit\n");
#ifdef RT_OS_WINDOWS
hNtdll = LoadLibrary("ntdll.dll");
if (hNtdll)
ZwQuerySystemInformation = (PFNZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwQuerySystemInformation");
#endif
/* @todo report system name and version */
/* Never fail here. */
return VINF_SUCCESS;
}
static DECLCALLBACK(void) VBoxServicePageSharingTerm(void)
{
printf("VBoxServicePageSharingTerm\n");
#ifdef RT_OS_WINDOWS
if (hNtdll)
FreeLibrary(hNtdll);
#endif
return;
}
int main(int argc, char **argv)
{
int rc = VINF_SUCCESS;
/*
* Init globals and such.
*/
RTR3Init();
/*
* Connect to the kernel part before daemonizing so we can fail
* and complain if there is some kind of problem. We need to initialize
* the guest lib *before* we do the pre-init just in case one of services
* needs do to some initial stuff with it.
*/
printf("Calling VbgR3Init()\n");
rc = VbglR3Init();
if (RT_FAILURE(rc))
{
printf("VbglR3Init failed with rc=%Rrc.\n", rc);
return -1;
}
VBoxServicePageSharingInit();
VBoxServicePageSharingInspectGuest();
VBoxServicePageSharingTerm();
return 0;
}