VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp@ 29938

Last change on this file since 29938 was 29938, checked in by vboxsync, 15 years ago

Stupid tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.1 KB
Line 
1/* $Id: VBoxServicePageSharing.cpp 29938 2010-06-01 08:42:07Z vboxsync $ */
2/** @file
3 * VBoxService - Guest page sharing.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/assert.h>
23#include <iprt/avl.h>
24#include <iprt/asm.h>
25#include <iprt/mem.h>
26#include <iprt/stream.h>
27#include <iprt/string.h>
28#include <iprt/semaphore.h>
29#include <iprt/system.h>
30#include <iprt/thread.h>
31#include <iprt/time.h>
32#include <VBox/VBoxGuestLib.h>
33#include "VBoxServiceInternal.h"
34#include "VBoxServiceUtils.h"
35
36
37/*******************************************************************************
38* Global Variables *
39*******************************************************************************/
40
41/** The semaphore we're blocking on. */
42static RTSEMEVENTMULTI g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
43
44#if defined(RT_OS_WINDOWS) && !defined(TARGET_NT4)
45#include <tlhelp32.h>
46#include <psapi.h>
47
48typedef struct
49{
50 AVLPVNODECORE Core;
51 HMODULE hModule;
52 char szFileVersion[16];
53 MODULEENTRY32 Info;
54} KNOWN_MODULE, *PKNOWN_MODULE;
55
56static DECLCALLBACK(int) VBoxServicePageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *);
57
58static PAVLPVNODECORE pKnownModuleTree = NULL;
59
60/**
61 * Registers a new module with the VMM
62 * @param pModule Module ptr
63 */
64void VBoxServicePageSharingRegisterModule(PKNOWN_MODULE pModule)
65{
66 VMMDEVSHAREDREGIONDESC aRegions[VMMDEVSHAREDREGIONDESC_MAX];
67 DWORD dwModuleSize = pModule->Info.modBaseSize;
68 BYTE *pBaseAddress = pModule->Info.modBaseAddr;
69 DWORD cbVersionSize, dummy;
70 BYTE *pVersionInfo;
71
72 VBoxServiceVerbose(3, "VBoxServicePageSharingRegisterModule\n");
73
74 cbVersionSize = GetFileVersionInfoSize(pModule->Info.szExePath, &dummy);
75 if (!cbVersionSize)
76 {
77 VBoxServiceVerbose(3, "VBoxServicePageSharingRegisterModule: GetFileVersionInfoSize failed with %d\n", GetLastError());
78 return;
79 }
80 pVersionInfo = (BYTE *)RTMemAlloc(cbVersionSize);
81 if (!pVersionInfo)
82 return;
83
84 if (!GetFileVersionInfo(pModule->Info.szExePath, 0, cbVersionSize, pVersionInfo))
85 {
86 VBoxServiceVerbose(3, "VBoxServicePageSharingRegisterModule: GetFileVersionInfo failed with %d\n", GetLastError());
87 goto end;
88 }
89
90 /* Fetch default code page. */
91 struct LANGANDCODEPAGE {
92 WORD wLanguage;
93 WORD wCodePage;
94 } *lpTranslate;
95
96 UINT cbTranslate;
97 BOOL ret = VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"), (LPVOID *)&lpTranslate, &cbTranslate);
98 if ( !ret
99 || cbTranslate < 4)
100 {
101 VBoxServiceVerbose(3, "VBoxServicePageSharingRegisterModule: VerQueryValue failed with %d (cb=%d)\n", GetLastError(), cbTranslate);
102 goto end;
103 }
104
105 unsigned i;
106 UINT cbFileVersion;
107 char *lpszFileVersion;
108 unsigned cTranslationBlocks = cbTranslate/sizeof(struct LANGANDCODEPAGE);
109
110 for(i = 0; i < cTranslationBlocks; i++)
111 {
112 /* Fetch file version string. */
113 char szFileVersionLocation[256];
114
115 sprintf(szFileVersionLocation, TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage);
116 ret = VerQueryValue(pVersionInfo, szFileVersionLocation, (LPVOID *)&lpszFileVersion, &cbFileVersion);
117 if (ret)
118 break;
119 }
120 if (i == cTranslationBlocks)
121 {
122 VBoxServiceVerbose(3, "VBoxServicePageSharingRegisterModule: no file version found!\n");
123 goto end;
124 }
125
126 _snprintf(pModule->szFileVersion, sizeof(pModule->szFileVersion), "%s", lpszFileVersion);
127 pModule->szFileVersion[RT_ELEMENTS(pModule->szFileVersion) - 1] = 0;
128
129 unsigned idxRegion = 0;
130 do
131 {
132 MEMORY_BASIC_INFORMATION MemInfo;
133
134 SIZE_T ret = VirtualQuery(pBaseAddress, &MemInfo, sizeof(MemInfo));
135 Assert(ret);
136 if (!ret)
137 {
138 VBoxServiceVerbose(3, "VBoxServicePageSharingRegisterModule: VirtualQueryEx failed with %d\n", GetLastError());
139 break;
140 }
141
142 if ( MemInfo.State == MEM_COMMIT
143 && MemInfo.Type == MEM_IMAGE)
144 {
145 switch (MemInfo.Protect)
146 {
147 case PAGE_EXECUTE:
148 case PAGE_EXECUTE_READ:
149 case PAGE_READONLY:
150 {
151 char *pRegion = (char *)MemInfo.BaseAddress;
152
153 /* Skip the first region as it only contains the image file header. */
154 if (pRegion != (char *)pModule->Info.modBaseAddr)
155 {
156 /* Touch all pages. */
157 while (pRegion < (char *)MemInfo.BaseAddress + MemInfo.RegionSize)
158 {
159 /* Try to trick the optimizer to leave the page touching code in place. */
160 ASMProbeReadByte(pRegion);
161 pRegion += PAGE_SIZE;
162 }
163 }
164 aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)MemInfo.BaseAddress;
165 aRegions[idxRegion].cbRegion = MemInfo.RegionSize;
166 idxRegion++;
167
168 break;
169 }
170
171 default:
172 break; /* ignore */
173 }
174 }
175
176 pBaseAddress = (BYTE *)MemInfo.BaseAddress + MemInfo.RegionSize;
177 if (dwModuleSize > MemInfo.RegionSize)
178 {
179 dwModuleSize -= MemInfo.RegionSize;
180 }
181 else
182 {
183 dwModuleSize = 0;
184 break;
185 }
186
187 if (idxRegion >= RT_ELEMENTS(aRegions))
188 break; /* out of room */
189 }
190 while (dwModuleSize);
191
192 VBoxServiceVerbose(3, "VbglR3RegisterSharedModule %s %s base=%p size=%x cregions=%d\n", pModule->Info.szModule, pModule->szFileVersion, pModule->Info.modBaseAddr, pModule->Info.modBaseSize, idxRegion);
193 int rc = VbglR3RegisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (RTGCPTR64)pModule->Info.modBaseAddr,
194 pModule->Info.modBaseSize, idxRegion, aRegions);
195// AssertRC(rc);
196 if (RT_FAILURE(rc))
197 VBoxServiceVerbose(3, "VbglR3RegisterSharedModule failed with %d\n", rc);
198
199end:
200 RTMemFree(pVersionInfo);
201 return;
202}
203
204/**
205 * Inspect all loaded modules for the specified process
206 * @param dwProcessId Process id
207 */
208void VBoxServicePageSharingInspectModules(DWORD dwProcessId, PAVLPVNODECORE *ppNewTree)
209{
210 HANDLE hProcess, hSnapshot;
211
212 /* Get a list of all the modules in this process. */
213 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
214 FALSE /* no child process handle inheritance */, dwProcessId);
215 if (hProcess == NULL)
216 {
217 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules: OpenProcess %x failed with %d\n", dwProcessId, GetLastError());
218 return;
219 }
220
221 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
222 if (hSnapshot == INVALID_HANDLE_VALUE)
223 {
224 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
225 CloseHandle(hProcess);
226 return;
227 }
228
229 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules\n");
230
231 MODULEENTRY32 ModuleInfo;
232 BOOL bRet;
233
234 ModuleInfo.dwSize = sizeof(ModuleInfo);
235 bRet = Module32First(hSnapshot, &ModuleInfo);
236 do
237 {
238 /** todo when changing this make sure VBoxService.exe is excluded! */
239 char *pszDot = strrchr(ModuleInfo.szModule, '.');
240 if ( pszDot
241 && (pszDot[1] == 'e' || pszDot[1] == 'E'))
242 continue; /* ignore executables for now. */
243
244 /* Found it before? */
245 PAVLPVNODECORE pRec = RTAvlPVGet(ppNewTree, ModuleInfo.modBaseAddr);
246 if (!pRec)
247 {
248 pRec = RTAvlPVRemove(&pKnownModuleTree, ModuleInfo.modBaseAddr);
249 if (!pRec)
250 {
251 /* New module; register it. */
252 PKNOWN_MODULE pModule = (PKNOWN_MODULE)RTMemAllocZ(sizeof(*pModule));
253 Assert(pModule);
254 if (!pModule)
255 break;
256
257 pModule->Info = ModuleInfo;
258 pModule->Core.Key = ModuleInfo.modBaseAddr;
259 pModule->hModule = LoadLibraryEx(ModuleInfo.szExePath, 0, DONT_RESOLVE_DLL_REFERENCES);
260 if (pModule->hModule)
261 VBoxServicePageSharingRegisterModule(pModule);
262
263 pRec = &pModule->Core;
264 }
265 bool ret = RTAvlPVInsert(ppNewTree, pRec);
266 Assert(ret); NOREF(ret);
267
268 VBoxServiceVerbose(3, "\n\n MODULE NAME: %s", ModuleInfo.szModule );
269 VBoxServiceVerbose(3, "\n executable = %s", ModuleInfo.szExePath );
270 VBoxServiceVerbose(3, "\n process ID = 0x%08X", ModuleInfo.th32ProcessID );
271 VBoxServiceVerbose(3, "\n base address = 0x%08X", (DWORD) ModuleInfo.modBaseAddr );
272 VBoxServiceVerbose(3, "\n base size = %d", ModuleInfo.modBaseSize );
273 }
274 }
275 while (Module32Next(hSnapshot, &ModuleInfo));
276
277 CloseHandle(hSnapshot);
278 CloseHandle(hProcess);
279}
280
281/**
282 * Inspect all running processes for executables and dlls that might be worth sharing
283 * with other VMs.
284 *
285 */
286void VBoxServicePageSharingInspectGuest()
287{
288 HANDLE hSnapshot;
289 PAVLPVNODECORE pNewTree = NULL;
290 DWORD dwProcessId = GetCurrentProcessId();
291
292 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectGuest\n");
293
294 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
295 if (hSnapshot == INVALID_HANDLE_VALUE)
296 {
297 VBoxServiceVerbose(3, "CreateToolhelp32Snapshot failed with %d\n", GetLastError());
298 return;
299 }
300
301 /* Check loaded modules for all running processes. */
302 PROCESSENTRY32 ProcessInfo;
303
304 ProcessInfo.dwSize = sizeof(ProcessInfo);
305 Process32First(hSnapshot, &ProcessInfo);
306
307 do
308 {
309 /* Skip our own process. */
310 if (ProcessInfo.th32ProcessID != dwProcessId)
311 VBoxServicePageSharingInspectModules(ProcessInfo.th32ProcessID, &pNewTree);
312 }
313 while (Process32Next(hSnapshot, &ProcessInfo));
314
315 CloseHandle(hSnapshot);
316
317 /* Delete leftover modules in the old tree. */
318 RTAvlPVDestroy(&pKnownModuleTree, VBoxServicePageSharingEmptyTreeCallback, NULL);
319
320 /* Check all registered modules. */
321 VbglR3CheckSharedModules();
322
323 /* Activate new module tree. */
324 pKnownModuleTree = pNewTree;
325}
326
327/**
328 * RTAvlPVDestroy callback.
329 */
330static DECLCALLBACK(int) VBoxServicePageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *)
331{
332 PKNOWN_MODULE pModule = (PKNOWN_MODULE)pNode;
333
334 VBoxServiceVerbose(3, "VBoxServicePageSharingEmptyTreeCallback %s %s\n", pModule->Info.szModule, pModule->szFileVersion);
335
336 /* Defererence module in the hypervisor. */
337 int rc = VbglR3UnregisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (RTGCPTR64)pModule->Info.modBaseAddr, pModule->Info.modBaseSize);
338 AssertRC(rc);
339
340 if (pModule->hModule)
341 FreeLibrary(pModule->hModule);
342 RTMemFree(pNode);
343 return 0;
344}
345
346
347#elif TARGET_NT4
348void VBoxServicePageSharingInspectGuest()
349{
350 /* not implemented */
351}
352#else
353void VBoxServicePageSharingInspectGuest()
354{
355 /* @todo other platforms */
356}
357#endif
358
359/** @copydoc VBOXSERVICE::pfnPreInit */
360static DECLCALLBACK(int) VBoxServicePageSharingPreInit(void)
361{
362 return VINF_SUCCESS;
363}
364
365
366/** @copydoc VBOXSERVICE::pfnOption */
367static DECLCALLBACK(int) VBoxServicePageSharingOption(const char **ppszShort, int argc, char **argv, int *pi)
368{
369 NOREF(ppszShort);
370 NOREF(argc);
371 NOREF(argv);
372 NOREF(pi);
373 return VINF_SUCCESS;
374}
375
376
377/** @copydoc VBOXSERVICE::pfnInit */
378static DECLCALLBACK(int) VBoxServicePageSharingInit(void)
379{
380 VBoxServiceVerbose(3, "VBoxServicePageSharingInit\n");
381
382 int rc = RTSemEventMultiCreate(&g_PageSharingEvent);
383 AssertRCReturn(rc, rc);
384
385 /* @todo report system name and version */
386 /* Never fail here. */
387 return VINF_SUCCESS;
388}
389
390/** @copydoc VBOXSERVICE::pfnWorker */
391DECLCALLBACK(int) VBoxServicePageSharingWorker(bool volatile *pfShutdown)
392{
393 /*
394 * Tell the control thread that it can continue
395 * spawning services.
396 */
397 RTThreadUserSignal(RTThreadSelf());
398
399 /*
400 * Block here first for a minute as using DONT_RESOLVE_DLL_REFERENCES is kind of risky; other code that uses LoadLibrary on a dll loaded like this
401 * before will end up crashing the process as the dll's init routine was never called.
402 *
403 * We have to use this feature as we can't simply execute all init code in our service process.
404 *
405 */
406 int rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
407 if (*pfShutdown)
408 goto end;
409
410 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
411 {
412 VBoxServiceError("RTSemEventMultiWait failed; rc=%Rrc\n", rc);
413 goto end;
414 }
415
416 /*
417 * Now enter the loop retrieving runtime data continuously.
418 */
419 for (;;)
420 {
421 VBoxServiceVerbose(3, "VBoxServicePageSharingWorker: enabled=%d\n", VbglR3PageSharingIsEnabled());
422
423 if (VbglR3PageSharingIsEnabled())
424 VBoxServicePageSharingInspectGuest();
425
426 /*
427 * Block for a minute.
428 *
429 * The event semaphore takes care of ignoring interruptions and it
430 * allows us to implement service wakeup later.
431 */
432 if (*pfShutdown)
433 break;
434 rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
435 if (*pfShutdown)
436 break;
437 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
438 {
439 VBoxServiceError("RTSemEventMultiWait failed; rc=%Rrc\n", rc);
440 break;
441 }
442 }
443
444end:
445 RTSemEventMultiDestroy(g_PageSharingEvent);
446 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
447
448 VBoxServiceVerbose(3, "VBoxServicePageSharingWorker: finished thread\n");
449 return 0;
450}
451
452/** @copydoc VBOXSERVICE::pfnTerm */
453static DECLCALLBACK(void) VBoxServicePageSharingTerm(void)
454{
455 VBoxServiceVerbose(3, "VBoxServicePageSharingTerm\n");
456 return;
457}
458
459
460/** @copydoc VBOXSERVICE::pfnStop */
461static DECLCALLBACK(void) VBoxServicePageSharingStop(void)
462{
463 RTSemEventMultiSignal(g_PageSharingEvent);
464}
465
466
467/**
468 * The 'pagesharing' service description.
469 */
470VBOXSERVICE g_PageSharing =
471{
472 /* pszName. */
473 "pagesharing",
474 /* pszDescription. */
475 "Page Sharing",
476 /* pszUsage. */
477 NULL,
478 /* pszOptions. */
479 NULL,
480 /* methods */
481 VBoxServicePageSharingPreInit,
482 VBoxServicePageSharingOption,
483 VBoxServicePageSharingInit,
484 VBoxServicePageSharingWorker,
485 VBoxServicePageSharingStop,
486 VBoxServicePageSharingTerm
487};
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