VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxGuest.cpp@ 4573

Last change on this file since 4573 was 4573, checked in by vboxsync, 18 years ago

More statistics. Started implementing the guest side.

File size: 14.1 KB
Line 
1/** @file
2 *
3 * VBoxGuest - Guest management notification
4 *
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define _WIN32_WINNT 0x0500
19#include <windows.h>
20#include <psapi.h>
21#include "VBoxService.h"
22#include "VBoxGuest.h"
23#include <VBoxDisplay.h>
24#include <VBox/VBoxDev.h>
25#include <VBoxGuestInternal.h>
26#include <iprt/assert.h>
27#include "helpers.h"
28#include <winternl.h>
29
30typedef struct _VBOXGUESTCONTEXT
31{
32 const VBOXSERVICEENV *pEnv;
33 uint32_t uStatInterval;
34 uint32_t uMemBalloonSize;
35
36 uint64_t ullLastCpuLoad_Idle;
37 uint64_t ullLastCpuLoad_Kernel;
38 uint64_t ullLastCpuLoad_User;
39
40 NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
41 void (WINAPI *pfnGlobalMemoryStatusEx)(LPMEMORYSTATUSEX lpBuffer);
42 BOOL (WINAPI *pfnGetPerformanceInfo)(PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb);
43} VBOXGUESTCONTEXT;
44
45
46static VBOXGUESTCONTEXT gCtx = {0};
47
48
49int VBoxGuestInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
50{
51 HANDLE gVBoxDriver = pEnv->hDriver;
52 DWORD cbReturned;
53
54 dprintf(("VBoxGuestInit\n"));
55
56 gCtx.pEnv = pEnv;
57 gCtx.uStatInterval = 0; /* default */
58 gCtx.ullLastCpuLoad_Idle = 0;
59 gCtx.ullLastCpuLoad_Kernel = 0;
60 gCtx.ullLastCpuLoad_User = 0;
61 gCtx.uMemBalloonSize = 0;
62
63 VMMDevGetStatisticsChangeRequest req;
64 vmmdevInitRequest(&req.header, VMMDevReq_GetStatisticsChangeRequest);
65 req.eventAck = 0;
66
67 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
68 {
69 dprintf(("VBoxGuestThread: new statistics interval %d seconds\n", req.u32StatInterval));
70 gCtx.uStatInterval = req.u32StatInterval * 1000;
71 }
72 else
73 dprintf(("VBoxGuestThread: DeviceIoControl failed with %d\n", GetLastError()));
74
75 /* NtQuerySystemInformation might be dropped in future releases, so load it dynamically as per Microsoft's recommendation */
76 HMODULE hMod = LoadLibrary("NTDLL.DLL");
77 if (hMod)
78 {
79 *(uintptr_t *)&gCtx.pfnNtQuerySystemInformation = (uintptr_t)GetProcAddress(hMod, "NtQuerySystemInformation");
80 if (gCtx.pfnNtQuerySystemInformation)
81 dprintf(("gCtx.pfnNtQuerySystemInformation = %x\n", gCtx.pfnNtQuerySystemInformation));
82 else
83 {
84 dprintf(("NTDLL.NtQuerySystemInformation not found!!\n"));
85 return VERR_NOT_IMPLEMENTED;
86 }
87 }
88
89 /* GlobalMemoryStatus is win2k and up, so load it dynamically */
90 hMod = LoadLibrary("KERNEL32.DLL");
91 if (hMod)
92 {
93 *(uintptr_t *)&gCtx.pfnGlobalMemoryStatusEx = (uintptr_t)GetProcAddress(hMod, "GlobalMemoryStatus");
94 if (gCtx.pfnGlobalMemoryStatusEx)
95 dprintf(("gCtx.GlobalMemoryStatus= %x\n", gCtx.pfnGlobalMemoryStatusEx));
96 else
97 {
98 /** @todo now fails in NT4; do we care? */
99 dprintf(("KERNEL32.GlobalMemoryStatus not found!!\n"));
100 return VERR_NOT_IMPLEMENTED;
101 }
102 }
103 /* GetPerformanceInfo is xp and up, so load it dynamically */
104 hMod = LoadLibrary("PSAPI.DLL");
105 if (hMod)
106 {
107 *(uintptr_t *)&gCtx.pfnGetPerformanceInfo = (uintptr_t)GetProcAddress(hMod, "GetPerformanceInfo");
108 if (gCtx.pfnGetPerformanceInfo)
109 dprintf(("gCtx.pfnGetPerformanceInfo= %x\n", gCtx.pfnGetPerformanceInfo));
110 /* failure is not fatal */
111 }
112
113 /* Check balloon size */
114 DWORD dwMemBalloonSize;
115 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_CTL_CHECK_BALLOON, NULL, 0, &dwMemBalloonSize, sizeof(dwMemBalloonSize), &cbReturned, NULL))
116 {
117 dprintf(("VBoxGuestThread: new balloon size % MB\n", dwMemBalloonSize));
118 gCtx.uMemBalloonSize = dwMemBalloonSize;
119 }
120 else
121 dprintf(("VBoxGuestThread: DeviceIoControl (balloon) failed with %d\n", GetLastError()));
122
123 *pfStartThread = true;
124 *ppInstance = &gCtx;
125 return VINF_SUCCESS;
126}
127
128
129void VBoxGuestDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
130{
131 dprintf(("VBoxGuestDestroy\n"));
132 return;
133}
134
135void VBoxGuestReportStatistics(VBOXGUESTCONTEXT *pCtx)
136{
137 SYSTEM_INFO systemInfo;
138 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo;
139 MEMORYSTATUSEX memStatus;
140 VMMDevReportGuestStats req;
141 uint32_t cbStruct;
142 DWORD cbReturned;
143 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
144
145 Assert(gCtx.pfnGlobalMemoryStatusEx && gCtx.pfnNtQuerySystemInformation);
146 if ( !gCtx.pfnGlobalMemoryStatusEx
147 || !gCtx.pfnNtQuerySystemInformation)
148 return;
149
150 vmmdevInitRequest(&req.header, VMMDevReq_ReportGuestStats);
151
152 /* Query and report guest statistics */
153 GetSystemInfo(&systemInfo);
154
155 gCtx.pfnGlobalMemoryStatusEx(&memStatus);
156
157 req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / systemInfo.dwPageSize);
158 req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / systemInfo.dwPageSize);
159 req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / systemInfo.dwPageSize);
160 req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad;
161 req.guestStats.u32PhysMemBalloon = pCtx->uMemBalloonSize; /* in megabytes already */
162 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_PAGE_FILE_SIZE | VBOX_GUEST_STAT_MEMORY_LOAD | VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
163
164 if (gCtx.pfnGetPerformanceInfo)
165 {
166 PERFORMANCE_INFORMATION perfInfo;
167
168 if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
169 {
170 req.guestStats.u32Processes = perfInfo.ProcessCount;
171 req.guestStats.u32Threads = perfInfo.ThreadCount;
172 req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
173 req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
174 req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
175 req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
176 req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
177 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_MEM_COMMIT_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_PAGED | VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE;
178 }
179 else
180 dprintf(("GetPerformanceInfo failed with %d\n", GetLastError()));
181 }
182
183 /* Query CPU load information */
184 cbStruct = systemInfo.dwNumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
185 pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)malloc(cbStruct);
186 Assert(pProcInfo);
187 if (!pProcInfo)
188 return;
189
190 /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
191 NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
192 if ( !rc
193 && cbReturned == cbStruct)
194 {
195 if (gCtx.ullLastCpuLoad_Kernel == 0)
196 {
197 /* first time */
198 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
199 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
200 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
201
202 Sleep(250);
203
204 rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
205 Assert(!rc);
206 }
207
208 uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.ullLastCpuLoad_Idle);
209 uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.ullLastCpuLoad_Kernel);
210 uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.ullLastCpuLoad_User);
211 uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
212
213 req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
214 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
215 req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
216
217 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER;
218
219 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
220 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
221 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
222 }
223
224 for (uint32_t i=0;i<systemInfo.dwNumberOfProcessors;i++)
225 {
226 req.guestStats.u32CpuId = i;
227
228 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
229 {
230 dprintf(("VBoxGuestThread: new statistics reported successfully!\n"));
231 }
232 else
233 dprintf(("VBoxGuestThread: DeviceIoControl (stats report) failed with %d\n", GetLastError()));
234 }
235
236 free(pProcInfo);
237}
238
239/**
240 * Thread function to wait for and process seamless mode change
241 * requests
242 */
243unsigned __stdcall VBoxGuestThread(void *pInstance)
244{
245 VBOXGUESTCONTEXT *pCtx = (VBOXGUESTCONTEXT *)pInstance;
246 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
247 bool fTerminate = false;
248 VBoxGuestFilterMaskInfo maskInfo;
249 DWORD cbReturned;
250
251 maskInfo.u32OrMask = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST;
252 maskInfo.u32NotMask = 0;
253 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
254 {
255 dprintf(("VBoxGuestThread: DeviceIOControl(CtlMask - or) succeeded\n"));
256 }
257 else
258 {
259 dprintf(("VBoxGuestThread: DeviceIOControl(CtlMask) failed, SeamlessChangeThread exited\n"));
260 return 0;
261 }
262
263 do
264 {
265 /* wait for a seamless change event */
266 VBoxGuestWaitEventInfo waitEvent;
267 waitEvent.u32TimeoutIn = (pCtx->uStatInterval) ? pCtx->uStatInterval : 1000;
268 waitEvent.u32EventMaskIn = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST | VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
269 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
270 {
271 dprintf(("VBoxGuestThread: DeviceIOControl succeded\n"));
272
273 /* are we supposed to stop? */
274 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
275 break;
276
277 dprintf(("VBoxGuestThread: checking event\n"));
278
279 /* did we get the right event? */
280 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_BALLOON_CHANGE_REQUEST)
281 {
282 DWORD dwMemBalloonSize;
283 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_CTL_CHECK_BALLOON, NULL, 0, &dwMemBalloonSize, sizeof(dwMemBalloonSize), &cbReturned, NULL))
284 {
285 dprintf(("VBoxGuestThread: new balloon size % MB\n", dwMemBalloonSize));
286 pCtx->uMemBalloonSize = dwMemBalloonSize;
287 }
288 else
289 dprintf(("VBoxGuestThread: DeviceIoControl (balloon) failed with %d\n", GetLastError()));
290 }
291 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST)
292 {
293 VMMDevGetStatisticsChangeRequest req;
294 vmmdevInitRequest(&req.header, VMMDevReq_GetStatisticsChangeRequest);
295 req.eventAck = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
296
297 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
298 {
299 dprintf(("VBoxGuestThread: new statistics interval %d seconds\n", req.u32StatInterval));
300 pCtx->uStatInterval = req.u32StatInterval * 1000;
301 }
302 else
303 dprintf(("VBoxGuestThread: DeviceIoControl (stat) failed with %d\n", GetLastError()));
304 }
305 }
306 else
307 {
308 dprintf(("VBoxService: error 0 from DeviceIoControl IOCTL_VBOXGUEST_WAITEVENT\n"));
309
310 /* sleep a bit to not eat too much CPU in case the above call always fails */
311 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0)
312 {
313 fTerminate = true;
314 break;
315 }
316 }
317 /* Report statistics to the host */
318 if ( gCtx.uStatInterval
319 && gCtx.pfnNtQuerySystemInformation)
320 {
321 VBoxGuestReportStatistics(pCtx);
322 }
323 }
324 while (!fTerminate);
325
326 maskInfo.u32OrMask = 0;
327 maskInfo.u32NotMask = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST | VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
328 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
329 {
330 dprintf(("VBoxGuestThread: DeviceIOControl(CtlMask - not) succeeded\n"));
331 }
332 else
333 {
334 dprintf(("VBoxGuestThread: DeviceIOControl(CtlMask) failed\n"));
335 }
336
337 dprintf(("VBoxGuestThread: finished seamless change request thread\n"));
338 return 0;
339}
340
341
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