VirtualBox

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

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

Don't assert

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