VirtualBox

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

Last change on this file since 57779 was 57358, checked in by vboxsync, 10 years ago

*: scm cleanup run.

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