VirtualBox

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

Last change on this file since 68080 was 63566, checked in by vboxsync, 8 years ago

scm: cleaning up todos

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