VirtualBox

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

Last change on this file since 28739 was 28738, checked in by vboxsync, 15 years ago

VBoxService: no trailing '\' in C/C++ code

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