VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp@ 26292

Last change on this file since 26292 was 26292, checked in by vboxsync, 15 years ago

Moved statistics and balloon handling to the guest service process

  • Property svn:eol-style set to native
File size: 14.0 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 <VBoxDisplay.h>
26#include <VBox/VMMDev.h>
27#include <VBox/VBoxGuest.h>
28#include <VBoxGuestInternal.h>
29#include <iprt/assert.h>
30#include "helpers.h"
31#include <winternl.h>
32
33typedef struct _VBOXSTATSCONTEXT
34{
35 uint32_t uStatInterval;
36
37 uint64_t ullLastCpuLoad_Idle;
38 uint64_t ullLastCpuLoad_Kernel;
39 uint64_t ullLastCpuLoad_User;
40
41#ifdef RT_OS_WINDOWS
42 NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
43 void (WINAPI *pfnGlobalMemoryStatusEx)(LPMEMORYSTATUSEX lpBuffer);
44 BOOL (WINAPI *pfnGetPerformanceInfo)(PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb);
45#endif
46} VBOXSTATSCONTEXT;
47
48/*******************************************************************************
49* Global Variables *
50*******************************************************************************/
51static VBOXSTATSCONTEXT gCtx = {0};
52
53/** The semaphore we're blocking on. */
54static RTSEMEVENTMULTI g_VMStatEvent = NIL_RTSEMEVENTMULTI;
55/** The vmstats interval (millseconds). */
56uint32_t g_VMStatsInterval = 0;
57
58
59/** @copydoc VBOXSERVICE::pfnPreInit */
60static DECLCALLBACK(int) VBoxServiceVMStatsPreInit(void)
61{
62 return VINF_SUCCESS;
63}
64
65
66/** @copydoc VBOXSERVICE::pfnOption */
67static DECLCALLBACK(int) VBoxServiceVMStatsOption(const char **ppszShort, int argc, char **argv, int *pi)
68{
69 int rc = -1;
70 if (ppszShort)
71 /* no short options */;
72 else if (!strcmp(argv[*pi], "--vmstats-interval"))
73 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
74 &g_VMStatsInterval, 1000, UINT32_MAX - 1);
75 return rc;
76}
77
78
79/** @copydoc VBOXSERVICE::pfnInit */
80static DECLCALLBACK(int) VBoxServiceVMStatsInit(void)
81{
82 VBoxServiceVerbose(3, "VBoxServiceVMStatsInit\n");
83
84 int rc = RTSemEventMultiCreate(&g_VMStatEvent);
85 AssertRCReturn(rc, rc);
86
87 gCtx.pEnv = pEnv;
88 gCtx.uStatInterval = 0; /* default */
89 gCtx.ullLastCpuLoad_Idle = 0;
90 gCtx.ullLastCpuLoad_Kernel = 0;
91 gCtx.ullLastCpuLoad_User = 0;
92
93 VMMDevGetStatisticsChangeRequest req;
94 vmmdevInitRequest(&req.header, VMMDevReq_GetStatisticsChangeRequest);
95 req.eventAck = 0;
96
97 if (DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(req.header.size), &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
98 {
99 Log(("VBoxStatsInit: new statistics interval %d seconds\n", req.u32StatInterval));
100 gCtx.uStatInterval = req.u32StatInterval * 1000;
101 }
102 else
103 Log(("VBoxStatsInit: DeviceIoControl failed with %d\n", GetLastError()));
104
105#ifdef RT_OS_WINDOWS
106 /* NtQuerySystemInformation might be dropped in future releases, so load it dynamically as per Microsoft's recommendation */
107 HMODULE hMod = LoadLibrary("NTDLL.DLL");
108 if (hMod)
109 {
110 *(uintptr_t *)&gCtx.pfnNtQuerySystemInformation = (uintptr_t)GetProcAddress(hMod, "NtQuerySystemInformation");
111 if (gCtx.pfnNtQuerySystemInformation)
112 VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnNtQuerySystemInformation = %x\n", gCtx.pfnNtQuerySystemInformation);
113 else
114 {
115 VBoxServiceError("VBoxStatsInit: NTDLL.NtQuerySystemInformation not found!!\n");
116 return VERR_NOT_IMPLEMENTED;
117 }
118 }
119
120 /* GlobalMemoryStatus is win2k and up, so load it dynamically */
121 hMod = LoadLibrary("KERNEL32.DLL");
122 if (hMod)
123 {
124 *(uintptr_t *)&gCtx.pfnGlobalMemoryStatusEx = (uintptr_t)GetProcAddress(hMod, "GlobalMemoryStatusEx");
125 if (gCtx.pfnGlobalMemoryStatusEx)
126 VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.GlobalMemoryStatusEx = %x\n", gCtx.pfnGlobalMemoryStatusEx);
127 else
128 {
129 /** @todo now fails in NT4; do we care? */
130 VBoxServiceError("VBoxStatsInit: KERNEL32.GlobalMemoryStatusEx not found!!\n");
131 return VERR_NOT_IMPLEMENTED;
132 }
133 }
134 /* GetPerformanceInfo is xp and up, so load it dynamically */
135 hMod = LoadLibrary("PSAPI.DLL");
136 if (hMod)
137 {
138 *(uintptr_t *)&gCtx.pfnGetPerformanceInfo = (uintptr_t)GetProcAddress(hMod, "GetPerformanceInfo");
139 if (gCtx.pfnGetPerformanceInfo)
140 VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnGetPerformanceInfo= %x\n", gCtx.pfnGetPerformanceInfo);
141 /* failure is not fatal */
142 }
143#endif /* RT_OS_WINDOWS */
144
145 return VINF_SUCCESS;
146}
147
148
149static void VBoxServiceVMStatsReport(VBOXSTATSCONTEXT *pCtx)
150{
151 SYSTEM_INFO systemInfo;
152 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo;
153 MEMORYSTATUSEX memStatus;
154 VMMDevReportGuestStats req;
155 uint32_t cbStruct;
156 DWORD cbReturned;
157 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
158
159 Assert(gCtx.pfnGlobalMemoryStatusEx && gCtx.pfnNtQuerySystemInformation);
160 if ( !gCtx.pfnGlobalMemoryStatusEx
161 || !gCtx.pfnNtQuerySystemInformation)
162 return;
163
164 vmmdevInitRequest(&req.header, VMMDevReq_ReportGuestStats);
165
166 /* Query and report guest statistics */
167 GetSystemInfo(&systemInfo);
168
169 memStatus.dwLength = sizeof(memStatus);
170 gCtx.pfnGlobalMemoryStatusEx(&memStatus);
171
172 req.guestStats.u32PageSize = systemInfo.dwPageSize;
173 req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / systemInfo.dwPageSize);
174 req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / systemInfo.dwPageSize);
175 /* 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. */
176 req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / systemInfo.dwPageSize) - req.guestStats.u32PhysMemTotal;
177 req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad;
178 req.guestStats.u32PhysMemBalloon = VBoxMemBalloonQuerySize() * (_1M/systemInfo.dwPageSize); /* was in megabytes */
179 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;
180
181 if (gCtx.pfnGetPerformanceInfo)
182 {
183 PERFORMANCE_INFORMATION perfInfo;
184
185 if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
186 {
187 req.guestStats.u32Processes = perfInfo.ProcessCount;
188 req.guestStats.u32Threads = perfInfo.ThreadCount;
189 req.guestStats.u32Handles = perfInfo.HandleCount;
190 req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
191 req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
192 req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
193 req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
194 req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
195 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;
196 }
197 else
198 Log(("GetPerformanceInfo failed with %d\n", GetLastError()));
199 }
200
201 /* Query CPU load information */
202 cbStruct = systemInfo.dwNumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
203 pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)malloc(cbStruct);
204 Assert(pProcInfo);
205 if (!pProcInfo)
206 return;
207
208 /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
209 NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
210 if ( !rc
211 && cbReturned == cbStruct)
212 {
213 if (gCtx.ullLastCpuLoad_Kernel == 0)
214 {
215 /* first time */
216 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
217 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
218 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
219
220 Sleep(250);
221
222 rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
223 Assert(!rc);
224 }
225
226 uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.ullLastCpuLoad_Idle);
227 uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.ullLastCpuLoad_Kernel);
228 uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.ullLastCpuLoad_User);
229 deltaKernel -= deltaIdle; /* idle time is added to kernel time */
230 uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
231
232 req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
233 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
234 req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
235
236 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER;
237
238 gCtx.ullLastCpuLoad_Idle = pProcInfo->IdleTime.QuadPart;
239 gCtx.ullLastCpuLoad_Kernel = pProcInfo->KernelTime.QuadPart;
240 gCtx.ullLastCpuLoad_User = pProcInfo->UserTime.QuadPart;
241 }
242
243 for (uint32_t i=0;i<systemInfo.dwNumberOfProcessors;i++)
244 {
245 req.guestStats.u32CpuId = i;
246
247 if (DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(req.header.size), &req, req.header.size, &req, req.header.size, &cbReturned, NULL))
248 {
249 Log(("VBoxStatsReportStatistics: new statistics reported successfully!\n"));
250 }
251 else
252 Log(("VBoxStatsReportStatistics: DeviceIoControl (stats report) failed with %d\n", GetLastError()));
253 }
254
255 free(pProcInfo);
256}
257
258/** @copydoc VBOXSERVICE::pfnWorker */
259DECLCALLBACK(int) VBoxServiceVMStatsWorker(bool volatile *pfShutdown)
260{
261 VBOXSTATSCONTEXT *pCtx = (VBOXSTATSCONTEXT *)pInstance;
262 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
263 bool fTerminate = false;
264 VBoxGuestFilterMaskInfo maskInfo;
265 DWORD cbReturned;
266
267 int rc = VINF_SUCCESS;
268
269 /*
270 * Tell the control thread that it can continue
271 * spawning services.
272 */
273 RTThreadUserSignal(RTThreadSelf());
274
275 maskInfo.u32OrMask = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
276 maskInfo.u32NotMask = 0;
277 if (DeviceIoControl (gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
278 {
279 Log(("VBoxStatsThread: DeviceIOControl(CtlMask - or) succeeded\n"));
280 }
281 else
282 {
283 Log(("VBoxStatsThread: DeviceIOControl(CtlMask) failed, SeamlessChangeThread exited\n"));
284 return 0;
285 }
286
287 /*
288 * Now enter the loop retrieving runtime data continuously.
289 */
290 for (;;)
291 {
292 /* Report statistics to the host */
293 if (gCtx.pfnNtQuerySystemInformation)
294 {
295 VBoxServiceVMStatsReport();
296 }
297
298 /*
299 * Block for a while.
300 *
301 * The event semaphore takes care of ignoring interruptions and it
302 * allows us to implement service wakeup later.
303 */
304 if (*pfShutdown)
305 break;
306 int rc2 = RTSemEventMultiWait(g_VMStatEvent, g_VMStatsInterval);
307 if (*pfShutdown)
308 break;
309 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
310 {
311 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
312 rc = rc2;
313 break;
314 }
315 }
316
317 maskInfo.u32OrMask = 0;
318 maskInfo.u32NotMask = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
319 if (DeviceIoControl (gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
320 {
321 Log(("VBoxStatsThread: DeviceIOControl(CtlMask - not) succeeded\n"));
322 }
323 else
324 {
325 Log(("VBoxStatsThread: DeviceIOControl(CtlMask) failed\n"));
326 }
327
328 RTSemEventMultiDestroy(g_VMStatsEvent);
329 g_VMStatsEvent = NIL_RTSEMEVENTMULTI;
330
331 Log(("VBoxStatsThread: finished statistics change request thread\n"));
332 return 0;
333}
334
335
336/** @copydoc VBOXSERVICE::pfnTerm */
337static DECLCALLBACK(void) VBoxServiceVMStatsTerm(void)
338{
339 VBoxServiceVerbose(3, "VBoxServiceVMStatsTerm\n");
340 return;
341}
342
343
344/** @copydoc VBOXSERVICE::pfnStop */
345static DECLCALLBACK(void) VBoxServiceVMStatsStop(void)
346{
347 RTSemEventMultiSignal(g_VMStatsEvent);
348}
349
350
351/**
352 * The 'vminfo' service description.
353 */
354VBOXSERVICE g_VMStatistics =
355{
356 /* pszName. */
357 "vmstats",
358 /* pszDescription. */
359 "Virtual Machine Statistics",
360 /* pszUsage. */
361 "[--vmstats-interval <ms>]"
362 ,
363 /* pszOptions. */
364 " --vmstats-interval Specifies the interval at which to retrieve the\n"
365 " VM statistcs. The default is 10000 ms.\n"
366 ,
367 /* methods */
368 VBoxServiceVMStatsPreInit,
369 VBoxServiceVMStatsOption,
370 VBoxServiceVMStatsInit,
371 VBoxServiceVMStatsWorker,
372 VBoxServiceVMStatsStop,
373 VBoxServiceVMStatsTerm
374};
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