VirtualBox

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

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

Corrections

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