VirtualBox

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

Last change on this file since 22683 was 21219, checked in by vboxsync, 16 years ago

VBox/VBoxDev.h -> VBox/VMMDev.h

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