VirtualBox

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

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

Cleaned up

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 KB
Line 
1/* $Id: VBoxServicePageSharing.cpp 29472 2010-05-14 13:48:15Z 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 dwProcessId Process id
62 */
63void VBoxServicePageSharingRegisterModule(HANDLE hProcess, 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 = VirtualQueryEx(hProcess, 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 aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)MemInfo.BaseAddress;
150 aRegions[idxRegion].cbRegion = MemInfo.RegionSize;
151 idxRegion++;
152 break;
153
154 default:
155 break; /* ignore */
156 }
157 }
158
159 pBaseAddress = (BYTE *)MemInfo.BaseAddress + MemInfo.RegionSize;
160 if (dwModuleSize > MemInfo.RegionSize)
161 {
162 dwModuleSize -= MemInfo.RegionSize;
163 }
164 else
165 {
166 dwModuleSize = 0;
167 break;
168 }
169
170 if (idxRegion >= RT_ELEMENTS(aRegions))
171 break; /* out of room */
172 }
173 while (dwModuleSize);
174
175 VBoxServiceVerbose(3, "VbglR3RegisterSharedModule %s %s base=%p size=%x cregions=%d\n", pModule->Info.szModule, pModule->szFileVersion, pModule->Info.modBaseAddr, pModule->Info.modBaseSize, idxRegion);
176 int rc = VbglR3RegisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (RTGCPTR64)pModule->Info.modBaseAddr,
177 pModule->Info.modBaseSize, idxRegion, aRegions);
178// AssertRC(rc);
179 if (RT_FAILURE(rc))
180 VBoxServiceVerbose(3, "VbglR3RegisterSharedModule failed with %d\n", rc);
181
182
183end:
184 RTMemFree(pVersionInfo);
185 return;
186}
187
188/**
189 * Inspect all loaded modules for the specified process
190 * @param dwProcessId Process id
191 */
192void VBoxServicePageSharingInspectModules(DWORD dwProcessId, PAVLPVNODECORE *ppNewTree)
193{
194 HANDLE hProcess, hSnapshot;
195
196 /* Get a list of all the modules in this process. */
197 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
198 FALSE /* no child process handle inheritance */, dwProcessId);
199 if (hProcess == NULL)
200 {
201 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules: OpenProcess %x failed with %d\n", dwProcessId, GetLastError());
202 return;
203 }
204
205 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
206 if (hSnapshot == INVALID_HANDLE_VALUE)
207 {
208 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
209 CloseHandle(hProcess);
210 return;
211 }
212
213 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectModules\n");
214
215 MODULEENTRY32 ModuleInfo;
216 BOOL bRet;
217
218 ModuleInfo.dwSize = sizeof(ModuleInfo);
219 bRet = Module32First(hSnapshot, &ModuleInfo);
220 do
221 {
222 /* Found it before? */
223 PAVLPVNODECORE pRec = RTAvlPVGet(ppNewTree, ModuleInfo.modBaseAddr);
224 if (!pRec)
225 {
226 pRec = RTAvlPVRemove(&pKnownModuleTree, ModuleInfo.modBaseAddr);
227 if (!pRec)
228 {
229 /* New module; register it. */
230 PKNOWN_MODULE pModule = (PKNOWN_MODULE)RTMemAllocZ(sizeof(*pModule));
231 Assert(pModule);
232 if (!pModule)
233 break;
234
235 pModule->Info = ModuleInfo;
236 pModule->Core.Key = ModuleInfo.modBaseAddr;
237 pModule->hModule = LoadLibraryEx(ModuleInfo.szExePath, 0, DONT_RESOLVE_DLL_REFERENCES);
238 if (pModule->hModule)
239 VBoxServicePageSharingRegisterModule(hProcess, pModule);
240
241 pRec = &pModule->Core;
242 }
243 bool ret = RTAvlPVInsert(ppNewTree, pRec);
244 Assert(ret); NOREF(ret);
245
246 VBoxServiceVerbose(3, "\n\n MODULE NAME: %s", ModuleInfo.szModule );
247 VBoxServiceVerbose(3, "\n executable = %s", ModuleInfo.szExePath );
248 VBoxServiceVerbose(3, "\n process ID = 0x%08X", ModuleInfo.th32ProcessID );
249 VBoxServiceVerbose(3, "\n base address = 0x%08X", (DWORD) ModuleInfo.modBaseAddr );
250 VBoxServiceVerbose(3, "\n base size = %d", ModuleInfo.modBaseSize );
251 }
252 }
253 while (Module32Next(hSnapshot, &ModuleInfo));
254
255 CloseHandle(hSnapshot);
256 CloseHandle(hProcess);
257}
258
259/**
260 * Inspect all running processes for executables and dlls that might be worth sharing
261 * with other VMs.
262 *
263 */
264void VBoxServicePageSharingInspectGuest()
265{
266 HANDLE hSnapshot;
267 PAVLPVNODECORE pNewTree = NULL;
268
269 VBoxServiceVerbose(3, "VBoxServicePageSharingInspectGuest\n");
270
271 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
272 if (hSnapshot == INVALID_HANDLE_VALUE)
273 {
274 VBoxServiceVerbose(3, "CreateToolhelp32Snapshot failed with %d\n", GetLastError());
275 return;
276 }
277
278 /* Check loaded modules for all running processes. */
279 PROCESSENTRY32 ProcessInfo;
280
281 ProcessInfo.dwSize = sizeof(ProcessInfo);
282 Process32First(hSnapshot, &ProcessInfo);
283
284 do
285 {
286 VBoxServicePageSharingInspectModules(ProcessInfo.th32ProcessID, &pNewTree);
287 }
288 while (Process32Next(hSnapshot, &ProcessInfo));
289
290 CloseHandle(hSnapshot);
291
292 /* Delete leftover modules in the old tree. */
293 RTAvlPVDestroy(&pKnownModuleTree, VBoxServicePageSharingEmptyTreeCallback, NULL);
294
295 /* Check all registered modules. */
296 VbglR3CheckSharedModules();
297
298 /* Activate new module tree. */
299 pKnownModuleTree = pNewTree;
300}
301
302/**
303 * RTAvlPVDestroy callback.
304 */
305static DECLCALLBACK(int) VBoxServicePageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *)
306{
307 PKNOWN_MODULE pModule = (PKNOWN_MODULE)pNode;
308
309 VBoxServiceVerbose(3, "VBoxServicePageSharingEmptyTreeCallback %s %s\n", pModule->Info.szModule, pModule->szFileVersion);
310
311 /* Defererence module in the hypervisor. */
312 int rc = VbglR3UnregisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (RTGCPTR64)pModule->Info.modBaseAddr, pModule->Info.modBaseSize);
313 AssertRC(rc);
314
315 if (pModule->hModule)
316 FreeLibrary(pModule->hModule);
317 RTMemFree(pNode);
318 return 0;
319}
320
321
322#elif TARGET_NT4
323void VBoxServicePageSharingInspectGuest()
324{
325 /* not implemented */
326}
327#else
328void VBoxServicePageSharingInspectGuest()
329{
330 /* @todo other platforms */
331}
332#endif
333
334/** @copydoc VBOXSERVICE::pfnPreInit */
335static DECLCALLBACK(int) VBoxServicePageSharingPreInit(void)
336{
337 return VINF_SUCCESS;
338}
339
340
341/** @copydoc VBOXSERVICE::pfnOption */
342static DECLCALLBACK(int) VBoxServicePageSharingOption(const char **ppszShort, int argc, char **argv, int *pi)
343{
344 NOREF(ppszShort);
345 NOREF(argc);
346 NOREF(argv);
347 NOREF(pi);
348 return VINF_SUCCESS;
349}
350
351
352/** @copydoc VBOXSERVICE::pfnInit */
353static DECLCALLBACK(int) VBoxServicePageSharingInit(void)
354{
355 VBoxServiceVerbose(3, "VBoxServicePageSharingInit\n");
356
357 int rc = RTSemEventMultiCreate(&g_PageSharingEvent);
358 AssertRCReturn(rc, rc);
359
360 /* @todo report system name and version */
361 /* Never fail here. */
362 return VINF_SUCCESS;
363}
364
365/** @copydoc VBOXSERVICE::pfnWorker */
366DECLCALLBACK(int) VBoxServicePageSharingWorker(bool volatile *pfShutdown)
367{
368 /*
369 * Tell the control thread that it can continue
370 * spawning services.
371 */
372 RTThreadUserSignal(RTThreadSelf());
373
374 /*
375 * Now enter the loop retrieving runtime data continuously.
376 */
377 for (;;)
378 {
379 VBoxServiceVerbose(3, "VBoxServicePageSharingWorker: enabled=%d\n", VbglR3PageSharingIsEnabled());
380
381 if (VbglR3PageSharingIsEnabled())
382 VBoxServicePageSharingInspectGuest();
383
384 /*
385 * Block for a minute.
386 *
387 * The event semaphore takes care of ignoring interruptions and it
388 * allows us to implement service wakeup later.
389 */
390 if (*pfShutdown)
391 break;
392 int rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
393 if (*pfShutdown)
394 break;
395 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
396 {
397 VBoxServiceError("RTSemEventMultiWait failed; rc=%Rrc\n", rc);
398 break;
399 }
400 }
401
402 RTSemEventMultiDestroy(g_PageSharingEvent);
403 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
404
405 VBoxServiceVerbose(3, "VBoxServicePageSharingWorker: finished thread\n");
406 return 0;
407}
408
409/** @copydoc VBOXSERVICE::pfnTerm */
410static DECLCALLBACK(void) VBoxServicePageSharingTerm(void)
411{
412 VBoxServiceVerbose(3, "VBoxServicePageSharingTerm\n");
413 return;
414}
415
416
417/** @copydoc VBOXSERVICE::pfnStop */
418static DECLCALLBACK(void) VBoxServicePageSharingStop(void)
419{
420 RTSemEventMultiSignal(g_PageSharingEvent);
421}
422
423
424/**
425 * The 'pagesharing' service description.
426 */
427VBOXSERVICE g_PageSharing =
428{
429 /* pszName. */
430 "pagesharing",
431 /* pszDescription. */
432 "Page Sharing",
433 /* pszUsage. */
434 NULL,
435 /* pszOptions. */
436 NULL,
437 /* methods */
438 VBoxServicePageSharingPreInit,
439 VBoxServicePageSharingOption,
440 VBoxServicePageSharingInit,
441 VBoxServicePageSharingWorker,
442 VBoxServicePageSharingStop,
443 VBoxServicePageSharingTerm
444};
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