VirtualBox

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

Last change on this file since 58029 was 58029, checked in by vboxsync, 9 years ago

VBoxService: Using prefix 'VGSvc', code style/width cleanups. No real changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 28.9 KB
Line 
1/* $Id: VBoxServiceStats.cpp 58029 2015-10-05 20:50:18Z vboxsync $ */
2/** @file
3 * VBoxStats - Guest statistics notification
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#if defined(RT_OS_WINDOWS)
23# ifdef TARGET_NT4
24# undef _WIN32_WINNT
25# define _WIN32_WINNT 0x501
26# endif
27# include <windows.h>
28# include <psapi.h>
29# include <winternl.h>
30
31#elif defined(RT_OS_LINUX)
32# include <iprt/ctype.h>
33# include <iprt/stream.h>
34# include <unistd.h>
35
36#elif defined(RT_OS_SOLARIS)
37# include <kstat.h>
38# include <sys/sysinfo.h>
39# include <unistd.h>
40#else
41/** @todo port me. */
42
43#endif
44
45#include <iprt/assert.h>
46#include <iprt/mem.h>
47#include <iprt/ldr.h>
48#include <VBox/param.h>
49#include <iprt/semaphore.h>
50#include <iprt/string.h>
51#include <iprt/system.h>
52#include <iprt/time.h>
53#include <iprt/thread.h>
54#include <VBox/VBoxGuestLib.h>
55#include "VBoxServiceInternal.h"
56#include "VBoxServiceUtils.h"
57
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62typedef struct _VBOXSTATSCONTEXT
63{
64 RTMSINTERVAL cMsStatInterval;
65
66 uint64_t au64LastCpuLoad_Idle[VMM_MAX_CPU_COUNT];
67 uint64_t au64LastCpuLoad_Kernel[VMM_MAX_CPU_COUNT];
68 uint64_t au64LastCpuLoad_User[VMM_MAX_CPU_COUNT];
69 uint64_t au64LastCpuLoad_Nice[VMM_MAX_CPU_COUNT];
70
71#ifdef RT_OS_WINDOWS
72 NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,
73 ULONG SystemInformationLength, PULONG ReturnLength);
74 void (WINAPI *pfnGlobalMemoryStatusEx)(LPMEMORYSTATUSEX lpBuffer);
75 BOOL (WINAPI *pfnGetPerformanceInfo)(PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb);
76#endif
77} VBOXSTATSCONTEXT;
78
79
80/*********************************************************************************************************************************
81* Global Variables *
82*********************************************************************************************************************************/
83/** Global data. */
84static VBOXSTATSCONTEXT g_VMStat = {0};
85
86/** The semaphore we're blocking on. */
87static RTSEMEVENTMULTI g_VMStatEvent = NIL_RTSEMEVENTMULTI;
88
89
90/**
91 * @interface_method_impl{VBOXSERVICE,pfnInit}
92 */
93static DECLCALLBACK(int) vgsvcVMStatsInit(void)
94{
95 VGSvcVerbose(3, "vgsvcVMStatsInit\n");
96
97 int rc = RTSemEventMultiCreate(&g_VMStatEvent);
98 AssertRCReturn(rc, rc);
99
100 g_VMStat.cMsStatInterval = 0; /* default; update disabled */
101 RT_ZERO(g_VMStat.au64LastCpuLoad_Idle);
102 RT_ZERO(g_VMStat.au64LastCpuLoad_Kernel);
103 RT_ZERO(g_VMStat.au64LastCpuLoad_User);
104 RT_ZERO(g_VMStat.au64LastCpuLoad_Nice);
105
106 rc = VbglR3StatQueryInterval(&g_VMStat.cMsStatInterval);
107 if (RT_SUCCESS(rc))
108 VGSvcVerbose(3, "vgsvcVMStatsInit: New statistics interval %u seconds\n", g_VMStat.cMsStatInterval);
109 else
110 VGSvcVerbose(3, "vgsvcVMStatsInit: DeviceIoControl failed with %d\n", rc);
111
112#ifdef RT_OS_WINDOWS
113 /* NtQuerySystemInformation might be dropped in future releases, so load
114 it dynamically as per Microsoft's recommendation. */
115 *(void **)&g_VMStat.pfnNtQuerySystemInformation = RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation");
116 if (g_VMStat.pfnNtQuerySystemInformation)
117 VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.pfnNtQuerySystemInformation = %x\n", g_VMStat.pfnNtQuerySystemInformation);
118 else
119 {
120 VGSvcVerbose(3, "vgsvcVMStatsInit: ntdll.NtQuerySystemInformation not found!\n");
121 return VERR_SERVICE_DISABLED;
122 }
123
124 /* GlobalMemoryStatus is win2k and up, so load it dynamically */
125 *(void **)&g_VMStat.pfnGlobalMemoryStatusEx = RTLdrGetSystemSymbol("kernel32.dll", "GlobalMemoryStatusEx");
126 if (g_VMStat.pfnGlobalMemoryStatusEx)
127 VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.GlobalMemoryStatusEx = %x\n", g_VMStat.pfnGlobalMemoryStatusEx);
128 else
129 {
130 /** @todo Now fails in NT4; do we care? */
131 VGSvcVerbose(3, "vgsvcVMStatsInit: kernel32.GlobalMemoryStatusEx not found!\n");
132 return VERR_SERVICE_DISABLED;
133 }
134
135 /* GetPerformanceInfo is xp and up, so load it dynamically */
136 *(void **)&g_VMStat.pfnGetPerformanceInfo = RTLdrGetSystemSymbol("psapi.dll", "GetPerformanceInfo");
137 if (g_VMStat.pfnGetPerformanceInfo)
138 VGSvcVerbose(3, "vgsvcVMStatsInit: g_VMStat.pfnGetPerformanceInfo= %x\n", g_VMStat.pfnGetPerformanceInfo);
139#endif /* RT_OS_WINDOWS */
140
141 return VINF_SUCCESS;
142}
143
144
145/**
146 * Gathers VM statistics and reports them to the host.
147 */
148static void vgsvcVMStatsReport(void)
149{
150#if defined(RT_OS_WINDOWS)
151 Assert(g_VMStat.pfnGlobalMemoryStatusEx && g_VMStat.pfnNtQuerySystemInformation);
152 if ( !g_VMStat.pfnGlobalMemoryStatusEx
153 || !g_VMStat.pfnNtQuerySystemInformation)
154 return;
155
156 /* Clear the report so we don't report garbage should NtQuerySystemInformation
157 behave in an unexpected manner. */
158 VMMDevReportGuestStats req;
159 RT_ZERO(req);
160
161 /* Query and report guest statistics */
162 SYSTEM_INFO systemInfo;
163 GetSystemInfo(&systemInfo);
164
165 MEMORYSTATUSEX memStatus;
166 memStatus.dwLength = sizeof(memStatus);
167 g_VMStat.pfnGlobalMemoryStatusEx(&memStatus);
168
169 req.guestStats.u32PageSize = systemInfo.dwPageSize;
170 req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / _4K);
171 req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / _4K);
172 /* The current size of the committed memory limit, in bytes. This is physical
173 memory plus the size of the page file, minus a small overhead. */
174 req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / _4K) - req.guestStats.u32PhysMemTotal;
175 req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad;
176 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
177 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
178 | VBOX_GUEST_STAT_PAGE_FILE_SIZE
179 | VBOX_GUEST_STAT_MEMORY_LOAD;
180# ifdef VBOX_WITH_MEMBALLOON
181 req.guestStats.u32PhysMemBalloon = VGSvcBalloonQueryPages(_4K);
182 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
183# else
184 req.guestStats.u32PhysMemBalloon = 0;
185# endif
186
187 if (g_VMStat.pfnGetPerformanceInfo)
188 {
189 PERFORMANCE_INFORMATION perfInfo;
190
191 if (g_VMStat.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
192 {
193 req.guestStats.u32Processes = perfInfo.ProcessCount;
194 req.guestStats.u32Threads = perfInfo.ThreadCount;
195 req.guestStats.u32Handles = perfInfo.HandleCount;
196 req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
197 req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
198 req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
199 req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
200 req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
201 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES
202 | VBOX_GUEST_STAT_MEM_COMMIT_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_TOTAL
203 | VBOX_GUEST_STAT_MEM_KERNEL_PAGED | VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED
204 | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE;
205 }
206 else
207 VGSvcVerbose(3, "vgsvcVMStatsReport: GetPerformanceInfo failed with %d\n", GetLastError());
208 }
209
210 /* Query CPU load information */
211 uint32_t cbStruct = systemInfo.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
212 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo;
213 pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)RTMemAlloc(cbStruct);
214 if (!pProcInfo)
215 return;
216
217 /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
218 bool fCpuInfoAvail = false;
219 DWORD cbReturned;
220 NTSTATUS rc = g_VMStat.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
221 if ( !rc
222 && cbReturned == cbStruct)
223 {
224 for (uint32_t i = 0; i < systemInfo.dwNumberOfProcessors; i++)
225 {
226 if (i >= VMM_MAX_CPU_COUNT)
227 {
228 VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPUs %u..%u\n", i, systemInfo.dwNumberOfProcessors);
229 break;
230 }
231
232 if (g_VMStat.au64LastCpuLoad_Kernel[i] == 0)
233 {
234 /* first time */
235 g_VMStat.au64LastCpuLoad_Idle[i] = pProcInfo[i].IdleTime.QuadPart;
236 g_VMStat.au64LastCpuLoad_Kernel[i] = pProcInfo[i].KernelTime.QuadPart;
237 g_VMStat.au64LastCpuLoad_User[i] = pProcInfo[i].UserTime.QuadPart;
238
239 Sleep(250);
240
241 rc = g_VMStat.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
242 Assert(!rc);
243 }
244
245 uint64_t deltaIdle = (pProcInfo[i].IdleTime.QuadPart - g_VMStat.au64LastCpuLoad_Idle[i]);
246 uint64_t deltaKernel = (pProcInfo[i].KernelTime.QuadPart - g_VMStat.au64LastCpuLoad_Kernel[i]);
247 uint64_t deltaUser = (pProcInfo[i].UserTime.QuadPart - g_VMStat.au64LastCpuLoad_User[i]);
248 deltaKernel -= deltaIdle; /* idle time is added to kernel time */
249 uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
250 if (ullTotalTime == 0) /* Prevent division through zero. */
251 ullTotalTime = 1;
252
253 req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
254 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
255 req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
256
257 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
258 | VBOX_GUEST_STAT_CPU_LOAD_USER;
259 req.guestStats.u32CpuId = i;
260 rc = VbglR3StatReport(&req);
261 if (RT_SUCCESS(rc))
262 VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", i);
263 else
264 VGSvcVerbose(3, "vgsvcVMStatsReport: VbglR3StatReport failed with rc=%Rrc\n", rc);
265
266 g_VMStat.au64LastCpuLoad_Idle[i] = pProcInfo[i].IdleTime.QuadPart;
267 g_VMStat.au64LastCpuLoad_Kernel[i] = pProcInfo[i].KernelTime.QuadPart;
268 g_VMStat.au64LastCpuLoad_User[i] = pProcInfo[i].UserTime.QuadPart;
269 }
270 }
271 RTMemFree(pProcInfo);
272
273 if (!fCpuInfoAvail)
274 {
275 VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
276 rc = VbglR3StatReport(&req);
277 if (RT_SUCCESS(rc))
278 VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
279 else
280 VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
281 }
282
283#elif defined(RT_OS_LINUX)
284 VMMDevReportGuestStats req;
285 RT_ZERO(req);
286 PRTSTREAM pStrm;
287 char szLine[256];
288 char *psz;
289
290 int rc = RTStrmOpen("/proc/meminfo", "r", &pStrm);
291 if (RT_SUCCESS(rc))
292 {
293 uint64_t u64Kb;
294 uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0;
295 for (;;)
296 {
297 rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine));
298 if (RT_FAILURE(rc))
299 break;
300 if (strstr(szLine, "MemTotal:") == szLine)
301 {
302 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[9]), &psz, 0, &u64Kb);
303 if (RT_SUCCESS(rc))
304 u64Total = u64Kb * _1K;
305 }
306 else if (strstr(szLine, "MemFree:") == szLine)
307 {
308 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb);
309 if (RT_SUCCESS(rc))
310 u64Free = u64Kb * _1K;
311 }
312 else if (strstr(szLine, "Buffers:") == szLine)
313 {
314 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb);
315 if (RT_SUCCESS(rc))
316 u64Buffers = u64Kb * _1K;
317 }
318 else if (strstr(szLine, "Cached:") == szLine)
319 {
320 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[7]), &psz, 0, &u64Kb);
321 if (RT_SUCCESS(rc))
322 u64Cached = u64Kb * _1K;
323 }
324 else if (strstr(szLine, "SwapTotal:") == szLine)
325 {
326 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[10]), &psz, 0, &u64Kb);
327 if (RT_SUCCESS(rc))
328 u64PagedTotal = u64Kb * _1K;
329 }
330 }
331 req.guestStats.u32PhysMemTotal = u64Total / _4K;
332 req.guestStats.u32PhysMemAvail = (u64Free + u64Buffers + u64Cached) / _4K;
333 req.guestStats.u32MemSystemCache = (u64Buffers + u64Cached) / _4K;
334 req.guestStats.u32PageFileSize = u64PagedTotal / _4K;
335 RTStrmClose(pStrm);
336 }
337 else
338 VGSvcVerbose(3, "vgsvcVMStatsReport: memory info not available!\n");
339
340 req.guestStats.u32PageSize = getpagesize();
341 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
342 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
343 | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE
344 | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
345# ifdef VBOX_WITH_MEMBALLOON
346 req.guestStats.u32PhysMemBalloon = VGSvcBalloonQueryPages(_4K);
347 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
348# else
349 req.guestStats.u32PhysMemBalloon = 0;
350# endif
351
352
353 /** @todo req.guestStats.u32Threads */
354 /** @todo req.guestStats.u32Processes */
355 /* req.guestStats.u32Handles doesn't make sense here. */
356 /** @todo req.guestStats.u32MemoryLoad */
357 /** @todo req.guestStats.u32MemCommitTotal */
358 /** @todo req.guestStats.u32MemKernelTotal */
359 /** @todo req.guestStats.u32MemKernelPaged, make any sense? = u32MemKernelTotal? */
360 /** @todo req.guestStats.u32MemKernelNonPaged, make any sense? = 0? */
361
362 bool fCpuInfoAvail = false;
363 rc = RTStrmOpen("/proc/stat", "r", &pStrm);
364 if (RT_SUCCESS(rc))
365 {
366 for (;;)
367 {
368 rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine));
369 if (RT_FAILURE(rc))
370 break;
371 if ( strstr(szLine, "cpu") == szLine
372 && strlen(szLine) > 3
373 && RT_C_IS_DIGIT(szLine[3]))
374 {
375 uint32_t u32CpuId;
376 rc = RTStrToUInt32Ex(&szLine[3], &psz, 0, &u32CpuId);
377 if (u32CpuId < VMM_MAX_CPU_COUNT)
378 {
379 uint64_t u64User = 0;
380 if (RT_SUCCESS(rc))
381 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64User);
382
383 uint64_t u64Nice = 0;
384 if (RT_SUCCESS(rc))
385 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Nice);
386
387 uint64_t u64System = 0;
388 if (RT_SUCCESS(rc))
389 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64System);
390
391 uint64_t u64Idle = 0;
392 if (RT_SUCCESS(rc))
393 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Idle);
394
395 uint64_t u64DeltaIdle = u64Idle - g_VMStat.au64LastCpuLoad_Idle[u32CpuId];
396 uint64_t u64DeltaSystem = u64System - g_VMStat.au64LastCpuLoad_Kernel[u32CpuId];
397 uint64_t u64DeltaUser = u64User - g_VMStat.au64LastCpuLoad_User[u32CpuId];
398 uint64_t u64DeltaNice = u64Nice - g_VMStat.au64LastCpuLoad_Nice[u32CpuId];
399
400 uint64_t u64DeltaAll = u64DeltaIdle
401 + u64DeltaSystem
402 + u64DeltaUser
403 + u64DeltaNice;
404 if (u64DeltaAll == 0) /* Prevent division through zero. */
405 u64DeltaAll = 1;
406
407 g_VMStat.au64LastCpuLoad_Idle[u32CpuId] = u64Idle;
408 g_VMStat.au64LastCpuLoad_Kernel[u32CpuId] = u64System;
409 g_VMStat.au64LastCpuLoad_User[u32CpuId] = u64User;
410 g_VMStat.au64LastCpuLoad_Nice[u32CpuId] = u64Nice;
411
412 req.guestStats.u32CpuId = u32CpuId;
413 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
414 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
415 req.guestStats.u32CpuLoad_User = (uint32_t)((u64DeltaUser
416 + u64DeltaNice) * 100 / u64DeltaAll);
417 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE
418 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
419 | VBOX_GUEST_STAT_CPU_LOAD_USER;
420 fCpuInfoAvail = true;
421 rc = VbglR3StatReport(&req);
422 if (RT_SUCCESS(rc))
423 VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", u32CpuId);
424 else
425 VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
426 }
427 else
428 VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPU%u\n", u32CpuId);
429 }
430 }
431 RTStrmClose(pStrm);
432 }
433 if (!fCpuInfoAvail)
434 {
435 VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
436 rc = VbglR3StatReport(&req);
437 if (RT_SUCCESS(rc))
438 VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
439 else
440 VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
441 }
442
443#elif defined(RT_OS_SOLARIS)
444 VMMDevReportGuestStats req;
445 RT_ZERO(req);
446 kstat_ctl_t *pStatKern = kstat_open();
447 if (pStatKern)
448 {
449 /*
450 * Memory statistics.
451 */
452 uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0;
453 int rc = -1;
454 kstat_t *pStatPages = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"system_pages");
455 if (pStatPages)
456 {
457 rc = kstat_read(pStatKern, pStatPages, NULL /* optional-copy-buf */);
458 if (rc != -1)
459 {
460 kstat_named_t *pStat = NULL;
461 pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"pagestotal");
462 if (pStat)
463 u64Total = pStat->value.ul;
464
465 pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"freemem");
466 if (pStat)
467 u64Free = pStat->value.ul;
468 }
469 }
470
471 kstat_t *pStatZFS = kstat_lookup(pStatKern, (char *)"zfs", 0 /* instance */, (char *)"arcstats");
472 if (pStatZFS)
473 {
474 rc = kstat_read(pStatKern, pStatZFS, NULL /* optional-copy-buf */);
475 if (rc != -1)
476 {
477 kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, (char *)"size");
478 if (pStat)
479 u64Cached = pStat->value.ul;
480 }
481 }
482
483 /*
484 * The vminfo are accumulative counters updated every "N" ticks. Let's get the
485 * number of stat updates so far and use that to divide the swap counter.
486 */
487 kstat_t *pStatInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"sysinfo");
488 if (pStatInfo)
489 {
490 sysinfo_t SysInfo;
491 rc = kstat_read(pStatKern, pStatInfo, &SysInfo);
492 if (rc != -1)
493 {
494 kstat_t *pStatVMInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"vminfo");
495 if (pStatVMInfo)
496 {
497 vminfo_t VMInfo;
498 rc = kstat_read(pStatKern, pStatVMInfo, &VMInfo);
499 if (rc != -1)
500 {
501 Assert(SysInfo.updates != 0);
502 u64PagedTotal = VMInfo.swap_avail / SysInfo.updates;
503 }
504 }
505 }
506 }
507
508 req.guestStats.u32PhysMemTotal = u64Total; /* already in pages */
509 req.guestStats.u32PhysMemAvail = u64Free; /* already in pages */
510 req.guestStats.u32MemSystemCache = u64Cached / _4K;
511 req.guestStats.u32PageFileSize = u64PagedTotal; /* already in pages */
512 /** @todo req.guestStats.u32Threads */
513 /** @todo req.guestStats.u32Processes */
514 /** @todo req.guestStats.u32Handles -- ??? */
515 /** @todo req.guestStats.u32MemoryLoad */
516 /** @todo req.guestStats.u32MemCommitTotal */
517 /** @todo req.guestStats.u32MemKernelTotal */
518 /** @todo req.guestStats.u32MemKernelPaged */
519 /** @todo req.guestStats.u32MemKernelNonPaged */
520 req.guestStats.u32PageSize = getpagesize();
521
522 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
523 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
524 | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE
525 | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
526#ifdef VBOX_WITH_MEMBALLOON
527 req.guestStats.u32PhysMemBalloon = VGSvcBalloonQueryPages(_4K);
528 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
529#else
530 req.guestStats.u32PhysMemBalloon = 0;
531#endif
532
533 /*
534 * CPU statistics.
535 */
536 cpu_stat_t StatCPU;
537 RT_ZERO(StatCPU);
538 kstat_t *pStatNode = NULL;
539 uint32_t cCPUs = 0;
540 bool fCpuInfoAvail = false;
541 for (pStatNode = pStatKern->kc_chain; pStatNode != NULL; pStatNode = pStatNode->ks_next)
542 {
543 if (!strcmp(pStatNode->ks_module, "cpu_stat"))
544 {
545 rc = kstat_read(pStatKern, pStatNode, &StatCPU);
546 if (rc == -1)
547 break;
548
549 if (cCPUs < VMM_MAX_CPU_COUNT)
550 {
551 uint64_t u64Idle = StatCPU.cpu_sysinfo.cpu[CPU_IDLE];
552 uint64_t u64User = StatCPU.cpu_sysinfo.cpu[CPU_USER];
553 uint64_t u64System = StatCPU.cpu_sysinfo.cpu[CPU_KERNEL];
554
555 uint64_t u64DeltaIdle = u64Idle - g_VMStat.au64LastCpuLoad_Idle[cCPUs];
556 uint64_t u64DeltaSystem = u64System - g_VMStat.au64LastCpuLoad_Kernel[cCPUs];
557 uint64_t u64DeltaUser = u64User - g_VMStat.au64LastCpuLoad_User[cCPUs];
558
559 uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser;
560 if (u64DeltaAll == 0) /* Prevent division through zero. */
561 u64DeltaAll = 1;
562
563 g_VMStat.au64LastCpuLoad_Idle[cCPUs] = u64Idle;
564 g_VMStat.au64LastCpuLoad_Kernel[cCPUs] = u64System;
565 g_VMStat.au64LastCpuLoad_User[cCPUs] = u64User;
566
567 req.guestStats.u32CpuId = cCPUs;
568 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
569 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
570 req.guestStats.u32CpuLoad_User = (uint32_t)(u64DeltaUser * 100 / u64DeltaAll);
571
572 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE
573 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
574 | VBOX_GUEST_STAT_CPU_LOAD_USER;
575 fCpuInfoAvail = true;
576
577 rc = VbglR3StatReport(&req);
578 if (RT_SUCCESS(rc))
579 VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics (CPU %u) reported successfully!\n", cCPUs);
580 else
581 VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
582 cCPUs++;
583 }
584 else
585 VGSvcVerbose(3, "vgsvcVMStatsReport: skipping information for CPU%u\n", cCPUs);
586 }
587 }
588
589 /*
590 * Report whatever statistics were collected.
591 */
592 if (!fCpuInfoAvail)
593 {
594 VGSvcVerbose(3, "vgsvcVMStatsReport: CPU info not available!\n");
595 rc = VbglR3StatReport(&req);
596 if (RT_SUCCESS(rc))
597 VGSvcVerbose(3, "vgsvcVMStatsReport: new statistics reported successfully!\n");
598 else
599 VGSvcVerbose(3, "vgsvcVMStatsReport: stats report failed with rc=%Rrc\n", rc);
600 }
601
602 kstat_close(pStatKern);
603 }
604
605#else
606 /* todo: implement for other platforms. */
607
608#endif
609}
610
611
612/**
613 * @interface_method_impl{VBOXSERVICE,pfnWorker}
614 */
615DECLCALLBACK(int) vgsvcVMStatsWorker(bool volatile *pfShutdown)
616{
617 int rc = VINF_SUCCESS;
618
619 /* Start monitoring of the stat event change event. */
620 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0);
621 if (RT_FAILURE(rc))
622 {
623 VGSvcVerbose(3, "vgsvcVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
624 return rc;
625 }
626
627 /*
628 * Tell the control thread that it can continue
629 * spawning services.
630 */
631 RTThreadUserSignal(RTThreadSelf());
632
633 /*
634 * Now enter the loop retrieving runtime data continuously.
635 */
636 for (;;)
637 {
638 uint32_t fEvents = 0;
639 RTMSINTERVAL cWaitMillies;
640
641 /* Check if an update interval change is pending. */
642 rc = VbglR3WaitEvent(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
643 if ( RT_SUCCESS(rc)
644 && (fEvents & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST))
645 VbglR3StatQueryInterval(&g_VMStat.cMsStatInterval);
646
647 if (g_VMStat.cMsStatInterval)
648 {
649 vgsvcVMStatsReport();
650 cWaitMillies = g_VMStat.cMsStatInterval;
651 }
652 else
653 cWaitMillies = 3000;
654
655 /*
656 * Block for a while.
657 *
658 * The event semaphore takes care of ignoring interruptions and it
659 * allows us to implement service wakeup later.
660 */
661 if (*pfShutdown)
662 break;
663 int rc2 = RTSemEventMultiWait(g_VMStatEvent, cWaitMillies);
664 if (*pfShutdown)
665 break;
666 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
667 {
668 VGSvcError("vgsvcVMStatsWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
669 rc = rc2;
670 break;
671 }
672 }
673
674 /* Cancel monitoring of the stat event change event. */
675 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST);
676 if (RT_FAILURE(rc))
677 VGSvcVerbose(3, "vgsvcVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
678
679 RTSemEventMultiDestroy(g_VMStatEvent);
680 g_VMStatEvent = NIL_RTSEMEVENTMULTI;
681
682 VGSvcVerbose(3, "VBoxStatsThread: finished statistics change request thread\n");
683 return 0;
684}
685
686
687/**
688 * @interface_method_impl{VBOXSERVICE,pfnStop}
689 */
690static DECLCALLBACK(void) vgsvcVMStatsStop(void)
691{
692 RTSemEventMultiSignal(g_VMStatEvent);
693}
694
695
696/**
697 * The 'vminfo' service description.
698 */
699VBOXSERVICE g_VMStatistics =
700{
701 /* pszName. */
702 "vmstats",
703 /* pszDescription. */
704 "Virtual Machine Statistics",
705 /* pszUsage. */
706 NULL,
707 /* pszOptions. */
708 NULL,
709 /* methods */
710 VGSvcDefaultPreInit,
711 VGSvcDefaultOption,
712 vgsvcVMStatsInit,
713 vgsvcVMStatsWorker,
714 vgsvcVMStatsStop,
715 VGSvcDefaultTerm
716};
717
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette