VirtualBox

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

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

Adjusted timeout. VBoxGuest used to wait 10 times longer than it should.

File size: 13.4 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(("VBoxStatsInit: new statistics interval %d seconds\n", req.u32StatInterval));
70 gCtx.uStatInterval = req.u32StatInterval * 1000;
71 }
72 else
73 dprintf(("VBoxStatsInit: 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, "GlobalMemoryStatusEx");
94 if (gCtx.pfnGlobalMemoryStatusEx)
95 dprintf(("gCtx.GlobalMemoryStatusEx = %x\n", gCtx.pfnGlobalMemoryStatusEx));
96 else
97 {
98 /** @todo now fails in NT4; do we care? */
99 dprintf(("KERNEL32.GlobalMemoryStatusEx 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 memStatus.dwLength = sizeof(memStatus);
146 gCtx.pfnGlobalMemoryStatusEx(&memStatus);
147
148 req.guestStats.u32PageSize = systemInfo.dwPageSize;
149 req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / systemInfo.dwPageSize);
150 req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / systemInfo.dwPageSize);
151 /* The current size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead. */
152 req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / systemInfo.dwPageSize) - req.guestStats.u32PhysMemTotal;
153 req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad;
154 req.guestStats.u32PhysMemBalloon = VBoxMemBalloonQuerySize() * (_1M/systemInfo.dwPageSize); /* was in megabytes */
155 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;
156
157 if (gCtx.pfnGetPerformanceInfo)
158 {
159 PERFORMANCE_INFORMATION perfInfo;
160
161 if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
162 {
163 req.guestStats.u32Processes = perfInfo.ProcessCount;
164 req.guestStats.u32Threads = perfInfo.ThreadCount;
165 req.guestStats.u32Handles = perfInfo.HandleCount;
166 req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
167 req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
168 req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
169 req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
170 req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
171 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES | 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;
172 }
173 else
174 dprintf(("GetPerformanceInfo failed with %d\n", GetLastError()));
175 }
176
177 /* Query CPU load information */
178 cbStruct = systemInfo.dwNumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
179 pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)malloc(cbStruct);
180 Assert(pProcInfo);
181 if (!pProcInfo)
182 return;
183
184 /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
185 NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
186 if ( !rc
187 && cbReturned == cbStruct)
188 {
189 if (gCtx.ullLastCpuLoad_Kernel == 0)
190 {
191 /* first time */
192 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
193 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
194 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
195
196 Sleep(250);
197
198 rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
199 Assert(!rc);
200 }
201
202 uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.ullLastCpuLoad_Idle);
203 uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.ullLastCpuLoad_Kernel);
204 uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.ullLastCpuLoad_User);
205 deltaKernel -= deltaIdle; /* idle time is added to kernel time */
206 uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
207
208 req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
209 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
210 req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
211
212 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER;
213
214 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
215 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
216 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
217 }
218
219 for (uint32_t i=0;i<systemInfo.dwNumberOfProcessors;i++)
220 {
221 req.guestStats.u32CpuId = i;
222
223 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
224 {
225 dprintf(("VBoxStatsReportStatistics: new statistics reported successfully!\n"));
226 }
227 else
228 dprintf(("VBoxStatsReportStatistics: DeviceIoControl (stats report) failed with %d\n", GetLastError()));
229 }
230
231 free(pProcInfo);
232}
233
234/**
235 * Thread function to wait for and process seamless mode change
236 * requests
237 */
238unsigned __stdcall VBoxStatsThread(void *pInstance)
239{
240 VBOXSTATSCONTEXT *pCtx = (VBOXSTATSCONTEXT *)pInstance;
241 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
242 bool fTerminate = false;
243 VBoxGuestFilterMaskInfo maskInfo;
244 DWORD cbReturned;
245
246 maskInfo.u32OrMask = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
247 maskInfo.u32NotMask = 0;
248 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
249 {
250 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask - or) succeeded\n"));
251 }
252 else
253 {
254 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask) failed, SeamlessChangeThread exited\n"));
255 return 0;
256 }
257
258 do
259 {
260 /* wait for a seamless change event */
261 VBoxGuestWaitEventInfo waitEvent;
262 waitEvent.u32TimeoutIn = (pCtx->uStatInterval) ? pCtx->uStatInterval : 5000;
263 waitEvent.u32EventMaskIn = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
264 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
265 {
266 dprintf(("VBoxStatsThread: DeviceIOControl succeded\n"));
267
268 /* are we supposed to stop? */
269 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
270 break;
271
272 dprintf(("VBoxStatsThread: checking event\n"));
273
274 /* did we get the right event? */
275 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST)
276 {
277 VMMDevGetStatisticsChangeRequest req;
278 vmmdevInitRequest(&req.header, VMMDevReq_GetStatisticsChangeRequest);
279 req.eventAck = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
280
281 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
282 {
283 dprintf(("VBoxStatsThread: new statistics interval %d seconds\n", req.u32StatInterval));
284 pCtx->uStatInterval = req.u32StatInterval * 1000;
285 }
286 else
287 dprintf(("VBoxStatsThread: DeviceIoControl (stat) failed with %d\n", GetLastError()));
288 }
289 }
290 else
291 {
292 dprintf(("VBoxStatsThread: error 0 from DeviceIoControl IOCTL_VBOXGUEST_WAITEVENT\n"));
293
294 /* sleep a bit to not eat too much CPU in case the above call always fails */
295 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0)
296 {
297 fTerminate = true;
298 break;
299 }
300 }
301 /* Report statistics to the host */
302 if ( gCtx.uStatInterval
303 && gCtx.pfnNtQuerySystemInformation)
304 {
305 VBoxStatsReportStatistics(pCtx);
306 }
307 }
308 while (!fTerminate);
309
310 maskInfo.u32OrMask = 0;
311 maskInfo.u32NotMask = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
312 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
313 {
314 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask - not) succeeded\n"));
315 }
316 else
317 {
318 dprintf(("VBoxStatsThread: DeviceIOControl(CtlMask) failed\n"));
319 }
320
321 dprintf(("VBoxStatsThread: finished statistics change request thread\n"));
322 return 0;
323}
324
325
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