VirtualBox

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

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

Skip the first region as it only contains the image file header.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: VBoxServicePageSharing.cpp 29507 2010-05-17 08:16:18Z 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 /* Skip the first region as it only contains the image file header. */
153 if (pRegion != (char *)pModule->Info.modBaseAddr)
154 {
155 /* Touch all pages. */
156 while (pRegion < (char *)MemInfo.BaseAddress + MemInfo.RegionSize)
157 {
158 char dummy;
159
160 memcpy(&dummy, pRegion, 1);
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
199
200end:
201 RTMemFree(pVersionInfo);
202 return;
203}
204
205/**
206 * Inspect all loaded modules for the specified process
207 * @param dwProcessId Process id
208 */
209void VBoxServicePageSharingInspectModules(DWORD dwProcessId, PAVLPVNODECORE *ppNewTree)
210{
211 HANDLE hProcess, hSnapshot;
212
213 /* Get a list of all the modules in this process. */
214 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
215 FALSE /* no child process handle inheritance */, dwProcessId);
216 if (hProcess == NULL)
217 {
218 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules: OpenProcess %x failed with %d\n", dwProcessId, GetLastError());
219 return;
220 }
221
222 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
223 if (hSnapshot == INVALID_HANDLE_VALUE)
224 {
225 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
226 CloseHandle(hProcess);
227 return;
228 }
229
230 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules\n");
231
232 MODULEENTRY32 ModuleInfo;
233 BOOL bRet;
234
235 ModuleInfo.dwSize = sizeof(ModuleInfo);
236 bRet = Module32First(hSnapshot, &ModuleInfo);
237 do
238 {
239 /** todo when changing this make sure VBoxService.exe is excluded! */
240 char *pszDot = strrchr(ModuleInfo.szModule, '.');
241 if ( pszDot
242 && (pszDot[1] == 'e' || pszDot[1] == 'E'))
243 continue; /* ignore executables for now. */
244
245 /* Found it before? */
246 PAVLPVNODECORE pRec = RTAvlPVGet(ppNewTree, ModuleInfo.modBaseAddr);
247 if (!pRec)
248 {
249 pRec = RTAvlPVRemove(&pKnownModuleTree, ModuleInfo.modBaseAddr);
250 if (!pRec)
251 {
252 /* New module; register it. */
253 PKNOWN_MODULE pModule = (PKNOWN_MODULE)RTMemAllocZ(sizeof(*pModule));
254 Assert(pModule);
255 if (!pModule)
256 break;
257
258 pModule->Info = ModuleInfo;
259 pModule->Core.Key = ModuleInfo.modBaseAddr;
260 pModule->hModule = LoadLibraryEx(ModuleInfo.szExePath, 0, DONT_RESOLVE_DLL_REFERENCES);
261 if (pModule->hModule)
262 VBoxServicePageSharingRegisterModule(pModule);
263
264 pRec = &pModule->Core;
265 }
266 bool ret = RTAvlPVInsert(ppNewTree, pRec);
267 Assert(ret); NOREF(ret);
268
269 VBoxServiceVerbose(3, "\n\n MODULE NAME: %s", ModuleInfo.szModule );
270 VBoxServiceVerbose(3, "\n executable = %s", ModuleInfo.szExePath );
271 VBoxServiceVerbose(3, "\n process ID = 0x%08X", ModuleInfo.th32ProcessID );
272 VBoxServiceVerbose(3, "\n base address = 0x%08X", (DWORD) ModuleInfo.modBaseAddr );
273 VBoxServiceVerbose(3, "\n base size = %d", ModuleInfo.modBaseSize );
274 }
275 }
276 while (Module32Next(hSnapshot, &ModuleInfo));
277
278 CloseHandle(hSnapshot);
279 CloseHandle(hProcess);
280}
281
282/**
283 * Inspect all running processes for executables and dlls that might be worth sharing
284 * with other VMs.
285 *
286 */
287void VBoxServicePageSharingInspectGuest()
288{
289 HANDLE hSnapshot;
290 PAVLPVNODECORE pNewTree = NULL;
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 VBoxServicePageSharingInspectModules(ProcessInfo.th32ProcessID, &pNewTree);
310 }
311 while (Process32Next(hSnapshot, &ProcessInfo));
312
313 CloseHandle(hSnapshot);
314
315 /* Delete leftover modules in the old tree. */
316 RTAvlPVDestroy(&pKnownModuleTree, VBoxServicePageSharingEmptyTreeCallback, NULL);
317
318 /* Check all registered modules. */
319 VbglR3CheckSharedModules();
320
321 /* Activate new module tree. */
322 pKnownModuleTree = pNewTree;
323}
324
325/**
326 * RTAvlPVDestroy callback.
327 */
328static DECLCALLBACK(int) VBoxServicePageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *)
329{
330 PKNOWN_MODULE pModule = (PKNOWN_MODULE)pNode;
331
332 VBoxServiceVerbose(3, "VBoxServicePageSharingEmptyTreeCallback %s %s\n", pModule->Info.szModule, pModule->szFileVersion);
333
334 /* Defererence module in the hypervisor. */
335 int rc = VbglR3UnregisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (RTGCPTR64)pModule->Info.modBaseAddr, pModule->Info.modBaseSize);
336 AssertRC(rc);
337
338 if (pModule->hModule)
339 FreeLibrary(pModule->hModule);
340 RTMemFree(pNode);
341 return 0;
342}
343
344
345#elif TARGET_NT4
346void VBoxServicePageSharingInspectGuest()
347{
348 /* not implemented */
349}
350#else
351void VBoxServicePageSharingInspectGuest()
352{
353 /* @todo other platforms */
354}
355#endif
356
357/** @copydoc VBOXSERVICE::pfnPreInit */
358static DECLCALLBACK(int) VBoxServicePageSharingPreInit(void)
359{
360 return VINF_SUCCESS;
361}
362
363
364/** @copydoc VBOXSERVICE::pfnOption */
365static DECLCALLBACK(int) VBoxServicePageSharingOption(const char **ppszShort, int argc, char **argv, int *pi)
366{
367 NOREF(ppszShort);
368 NOREF(argc);
369 NOREF(argv);
370 NOREF(pi);
371 return VINF_SUCCESS;
372}
373
374
375/** @copydoc VBOXSERVICE::pfnInit */
376static DECLCALLBACK(int) VBoxServicePageSharingInit(void)
377{
378 VBoxServiceVerbose(3, "VBoxServicePageSharingInit\n");
379
380 int rc = RTSemEventMultiCreate(&g_PageSharingEvent);
381 AssertRCReturn(rc, rc);
382
383 /* @todo report system name and version */
384 /* Never fail here. */
385 return VINF_SUCCESS;
386}
387
388/** @copydoc VBOXSERVICE::pfnWorker */
389DECLCALLBACK(int) VBoxServicePageSharingWorker(bool volatile *pfShutdown)
390{
391 /*
392 * Tell the control thread that it can continue
393 * spawning services.
394 */
395 RTThreadUserSignal(RTThreadSelf());
396
397 /*
398 * Now enter the loop retrieving runtime data continuously.
399 */
400 for (;;)
401 {
402 VBoxServiceVerbose(3, "VBoxServicePageSharingWorker: enabled=%d\n", VbglR3PageSharingIsEnabled());
403
404 if (VbglR3PageSharingIsEnabled())
405 VBoxServicePageSharingInspectGuest();
406
407 /*
408 * Block for a minute.
409 *
410 * The event semaphore takes care of ignoring interruptions and it
411 * allows us to implement service wakeup later.
412 */
413 if (*pfShutdown)
414 break;
415 int rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
416 if (*pfShutdown)
417 break;
418 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
419 {
420 VBoxServiceError("RTSemEventMultiWait failed; rc=%Rrc\n", rc);
421 break;
422 }
423 }
424
425 RTSemEventMultiDestroy(g_PageSharingEvent);
426 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
427
428 VBoxServiceVerbose(3, "VBoxServicePageSharingWorker: finished thread\n");
429 return 0;
430}
431
432/** @copydoc VBOXSERVICE::pfnTerm */
433static DECLCALLBACK(void) VBoxServicePageSharingTerm(void)
434{
435 VBoxServiceVerbose(3, "VBoxServicePageSharingTerm\n");
436 return;
437}
438
439
440/** @copydoc VBOXSERVICE::pfnStop */
441static DECLCALLBACK(void) VBoxServicePageSharingStop(void)
442{
443 RTSemEventMultiSignal(g_PageSharingEvent);
444}
445
446
447/**
448 * The 'pagesharing' service description.
449 */
450VBOXSERVICE g_PageSharing =
451{
452 /* pszName. */
453 "pagesharing",
454 /* pszDescription. */
455 "Page Sharing",
456 /* pszUsage. */
457 NULL,
458 /* pszOptions. */
459 NULL,
460 /* methods */
461 VBoxServicePageSharingPreInit,
462 VBoxServicePageSharingOption,
463 VBoxServicePageSharingInit,
464 VBoxServicePageSharingWorker,
465 VBoxServicePageSharingStop,
466 VBoxServicePageSharingTerm
467};
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