VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxStatistics.cpp@ 10759

Last change on this file since 10759 was 10552, checked in by vboxsync, 16 years ago

More IOCTLs.

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