VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxStatistics.cpp@ 4594

Last change on this file since 4594 was 4594, checked in by vboxsync, 17 years ago

Split up stats & memory balloon management

File size: 13.0 KB
Line 
1/** @file
2 *
3 * VBoxStats - Guest statistics 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 "VBoxStatistics.h"
23#include "VBoxMemBalloon.h"
24#include <VBoxDisplay.h>
25#include <VBox/VBoxDev.h>
26#include <VBox/VBoxGuest.h>
27#include <VBoxGuestInternal.h>
28#include <iprt/assert.h>
29#include "helpers.h"
30#include <winternl.h>
31
32typedef struct _VBOXSTATSCONTEXT
33{
34 const VBOXSERVICEENV *pEnv;
35 uint32_t uStatInterval;
36
37 uint64_t ullLastCpuLoad_Idle;
38 uint64_t ullLastCpuLoad_Kernel;
39 uint64_t ullLastCpuLoad_User;
40
41 NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
42 void (WINAPI *pfnGlobalMemoryStatusEx)(LPMEMORYSTATUSEX lpBuffer);
43 BOOL (WINAPI *pfnGetPerformanceInfo)(PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb);
44} VBOXSTATSCONTEXT;
45
46
47static VBOXSTATSCONTEXT gCtx = {0};
48
49
50int VBoxStatsInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
51{
52 HANDLE gVBoxDriver = pEnv->hDriver;
53 DWORD cbReturned;
54
55 dprintf(("VBoxStatsInit\n"));
56
57 gCtx.pEnv = pEnv;
58 gCtx.uStatInterval = 0; /* default */
59 gCtx.ullLastCpuLoad_Idle = 0;
60 gCtx.ullLastCpuLoad_Kernel = 0;
61 gCtx.ullLastCpuLoad_User = 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(("VBoxStatsThread: new statistics interval %d seconds\n", req.u32StatInterval));
70 gCtx.uStatInterval = req.u32StatInterval * 1000;
71 }
72 else
73 dprintf(("VBoxStatsThread: 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 *pfStartThread = true;
114 *ppInstance = &gCtx;
115 return VINF_SUCCESS;
116}
117
118
119void VBoxStatsDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
120{
121 dprintf(("VBoxStatsDestroy\n"));
122 return;
123}
124
125void VBoxStatsReportStatistics(VBOXSTATSCONTEXT *pCtx)
126{
127 SYSTEM_INFO systemInfo;
128 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo;
129 MEMORYSTATUSEX memStatus;
130 VMMDevReportGuestStats req;
131 uint32_t cbStruct;
132 DWORD cbReturned;
133 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
134
135 Assert(gCtx.pfnGlobalMemoryStatusEx && gCtx.pfnNtQuerySystemInformation);
136 if ( !gCtx.pfnGlobalMemoryStatusEx
137 || !gCtx.pfnNtQuerySystemInformation)
138 return;
139
140 vmmdevInitRequest(&req.header, VMMDevReq_ReportGuestStats);
141
142 /* Query and report guest statistics */
143 GetSystemInfo(&systemInfo);
144
145 gCtx.pfnGlobalMemoryStatusEx(&memStatus);
146
147 req.guestStats.u32PageSize = systemInfo.dwPageSize;
148 req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / systemInfo.dwPageSize);
149 req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / systemInfo.dwPageSize);
150 req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / systemInfo.dwPageSize);
151 req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad;
152 req.guestStats.u32PhysMemBalloon = VBoxMemBalloonQuerySize() * (_1M/systemInfo.dwPageSize); /* was in megabytes */
153 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;
154
155 if (gCtx.pfnGetPerformanceInfo)
156 {
157 PERFORMANCE_INFORMATION perfInfo;
158
159 if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
160 {
161 req.guestStats.u32Processes = perfInfo.ProcessCount;
162 req.guestStats.u32Threads = perfInfo.ThreadCount;
163 req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
164 req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
165 req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
166 req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
167 req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
168 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;
169 }
170 else
171 dprintf(("GetPerformanceInfo failed with %d\n", GetLastError()));
172 }
173
174 /* Query CPU load information */
175 cbStruct = systemInfo.dwNumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
176 pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)malloc(cbStruct);
177 Assert(pProcInfo);
178 if (!pProcInfo)
179 return;
180
181 /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
182 NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
183 if ( !rc
184 && cbReturned == cbStruct)
185 {
186 if (gCtx.ullLastCpuLoad_Kernel == 0)
187 {
188 /* first time */
189 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
190 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
191 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
192
193 Sleep(250);
194
195 rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
196 Assert(!rc);
197 }
198
199 uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.ullLastCpuLoad_Idle);
200 uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.ullLastCpuLoad_Kernel);
201 uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.ullLastCpuLoad_User);
202 uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
203
204 req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
205 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
206 req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
207
208 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER;
209
210 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
211 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
212 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
213 }
214
215 for (uint32_t i=0;i<systemInfo.dwNumberOfProcessors;i++)
216 {
217 req.guestStats.u32CpuId = i;
218
219 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
220 {
221 dprintf(("VBoxStatsThread: new statistics reported successfully!\n"));
222 }
223 else
224 dprintf(("VBoxStatsThread: DeviceIoControl (stats report) failed with %d\n", GetLastError()));
225 }
226
227 free(pProcInfo);
228}
229
230/**
231 * Thread function to wait for and process seamless mode change
232 * requests
233 */
234unsigned __stdcall VBoxStatsThread(void *pInstance)
235{
236 VBOXSTATSCONTEXT *pCtx = (VBOXSTATSCONTEXT *)pInstance;
237 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
238 bool fTerminate = false;
239 VBoxGuestFilterMaskInfo maskInfo;
240 DWORD cbReturned;
241
242 maskInfo.u32OrMask = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
243 maskInfo.u32NotMask = 0;
244 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
245 {
246 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask - or) succeeded\n"));
247 }
248 else
249 {
250 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask) failed, SeamlessChangeThread exited\n"));
251 return 0;
252 }
253
254 do
255 {
256 /* wait for a seamless change event */
257 VBoxGuestWaitEventInfo waitEvent;
258 waitEvent.u32TimeoutIn = (pCtx->uStatInterval) ? pCtx->uStatInterval : 1000;
259 waitEvent.u32EventMaskIn = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
260 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
261 {
262 dprintf(("VBoxStatsThread: DeviceIOControl succeded\n"));
263
264 /* are we supposed to stop? */
265 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
266 break;
267
268 dprintf(("VBoxStatsThread: checking event\n"));
269
270 /* did we get the right event? */
271 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST)
272 {
273 VMMDevGetStatisticsChangeRequest req;
274 vmmdevInitRequest(&req.header, VMMDevReq_GetStatisticsChangeRequest);
275 req.eventAck = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
276
277 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
278 {
279 dprintf(("VBoxStatsThread: new statistics interval %d seconds\n", req.u32StatInterval));
280 pCtx->uStatInterval = req.u32StatInterval * 1000;
281 }
282 else
283 dprintf(("VBoxStatsThread: DeviceIoControl (stat) failed with %d\n", GetLastError()));
284 }
285 }
286 else
287 {
288 dprintf(("VBoxStatsThread: error 0 from DeviceIoControl IOCTL_VBOXGUEST_WAITEVENT\n"));
289
290 /* sleep a bit to not eat too much CPU in case the above call always fails */
291 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0)
292 {
293 fTerminate = true;
294 break;
295 }
296 }
297 /* Report statistics to the host */
298 if ( gCtx.uStatInterval
299 && gCtx.pfnNtQuerySystemInformation)
300 {
301 VBoxStatsReportStatistics(pCtx);
302 }
303 }
304 while (!fTerminate);
305
306 maskInfo.u32OrMask = 0;
307 maskInfo.u32NotMask = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
308 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
309 {
310 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask - not) succeeded\n"));
311 }
312 else
313 {
314 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask) failed\n"));
315 }
316
317 dprintf(("VBoxStatsThread: finished seamless change request thread\n"));
318 return 0;
319}
320
321
Note: See TracBrowser for help on using the repository browser.

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