VirtualBox

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

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

Added note

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette