VirtualBox

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

Last change on this file was 103149, checked in by vboxsync, 4 months ago

Additions: Some warning fixes about externally visible functions which should be static, bugref:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use