VirtualBox

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

Last change on this file since 72097 was 70173, checked in by vboxsync, 7 years ago

VBoxServiceNT: Temporarily a duplicate of VBoxService.

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