VirtualBox

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

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

*: scm --update-copyright-year

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