VirtualBox

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

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

Additions: activated guest user level ballooning stuff for all guests

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