VirtualBox

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

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

Activated memory balloon and statistics again

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