VirtualBox

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

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

VBoxGuestR3LibMisc.cpp,VBoxGuestLib.h: minor adjustments.

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