VirtualBox

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

Last change on this file since 29250 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 27.7 KB
Line 
1/* $Id: VBoxServiceStats.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * VBoxStats - Guest statistics notification
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#if defined(RT_OS_WINDOWS)
22# ifdef TARGET_NT4
23# undef _WIN32_WINNT
24# define _WIN32_WINNT 0x501
25# endif
26# include <windows.h>
27# include <psapi.h>
28# include <winternl.h>
29
30#elif defined(RT_OS_LINUX)
31# include <iprt/ctype.h>
32# include <iprt/stream.h>
33# include <unistd.h>
34
35#elif defined(RT_OS_SOLARIS)
36# include <kstat.h>
37# include <sys/sysinfo.h>
38# include <unistd.h>
39#else
40/** @todo port me. */
41
42#endif
43
44#include <iprt/assert.h>
45#include <iprt/mem.h>
46#include <VBox/param.h>
47#include <iprt/semaphore.h>
48#include <iprt/string.h>
49#include <iprt/system.h>
50#include <iprt/time.h>
51#include <iprt/thread.h>
52#include <VBox/VBoxGuestLib.h>
53#include "VBoxServiceInternal.h"
54#include "VBoxServiceUtils.h"
55
56
57/*******************************************************************************
58* Structures and Typedefs *
59*******************************************************************************/
60typedef struct _VBOXSTATSCONTEXT
61{
62 RTMSINTERVAL cMsStatInterval;
63
64 uint64_t au64LastCpuLoad_Idle[VMM_MAX_CPU_COUNT];
65 uint64_t au64LastCpuLoad_Kernel[VMM_MAX_CPU_COUNT];
66 uint64_t au64LastCpuLoad_User[VMM_MAX_CPU_COUNT];
67 uint64_t au64LastCpuLoad_Nice[VMM_MAX_CPU_COUNT];
68
69#ifdef RT_OS_WINDOWS
70 NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
71 void (WINAPI *pfnGlobalMemoryStatusEx)(LPMEMORYSTATUSEX lpBuffer);
72 BOOL (WINAPI *pfnGetPerformanceInfo)(PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb);
73#endif
74} VBOXSTATSCONTEXT;
75
76
77/*******************************************************************************
78* Global Variables *
79*******************************************************************************/
80static VBOXSTATSCONTEXT gCtx = {0};
81
82/** The semaphore we're blocking on. */
83static RTSEMEVENTMULTI g_VMStatEvent = NIL_RTSEMEVENTMULTI;
84
85
86/** @copydoc VBOXSERVICE::pfnPreInit */
87static DECLCALLBACK(int) VBoxServiceVMStatsPreInit(void)
88{
89 return VINF_SUCCESS;
90}
91
92
93/** @copydoc VBOXSERVICE::pfnOption */
94static DECLCALLBACK(int) VBoxServiceVMStatsOption(const char **ppszShort, int argc, char **argv, int *pi)
95{
96 NOREF(ppszShort);
97 NOREF(argc);
98 NOREF(argv);
99 NOREF(pi);
100 return VINF_SUCCESS;
101}
102
103
104/** @copydoc VBOXSERVICE::pfnInit */
105static DECLCALLBACK(int) VBoxServiceVMStatsInit(void)
106{
107 VBoxServiceVerbose(3, "VBoxServiceVMStatsInit\n");
108
109 int rc = RTSemEventMultiCreate(&g_VMStatEvent);
110 AssertRCReturn(rc, rc);
111
112 gCtx.cMsStatInterval = 0; /* default; update disabled */
113 RT_ZERO(gCtx.au64LastCpuLoad_Idle);
114 RT_ZERO(gCtx.au64LastCpuLoad_Kernel);
115 RT_ZERO(gCtx.au64LastCpuLoad_User);
116 RT_ZERO(gCtx.au64LastCpuLoad_Nice);
117
118 rc = VbglR3StatQueryInterval(&gCtx.cMsStatInterval);
119 if (RT_SUCCESS(rc))
120 VBoxServiceVerbose(3, "VBoxStatsInit: new statistics interval %u seconds\n", gCtx.cMsStatInterval);
121 else
122 VBoxServiceVerbose(3, "VBoxStatsInit: DeviceIoControl failed with %d\n", rc);
123
124#ifdef RT_OS_WINDOWS
125 /* NtQuerySystemInformation might be dropped in future releases, so load it dynamically as per Microsoft's recommendation */
126 HMODULE hMod = LoadLibrary("NTDLL.DLL");
127 if (hMod)
128 {
129 *(uintptr_t *)&gCtx.pfnNtQuerySystemInformation = (uintptr_t)GetProcAddress(hMod, "NtQuerySystemInformation");
130 if (gCtx.pfnNtQuerySystemInformation)
131 VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnNtQuerySystemInformation = %x\n", gCtx.pfnNtQuerySystemInformation);
132 else
133 {
134 VBoxServiceError("VBoxStatsInit: NTDLL.NtQuerySystemInformation not found!!\n");
135 return VERR_NOT_IMPLEMENTED;
136 }
137 }
138
139 /* GlobalMemoryStatus is win2k and up, so load it dynamically */
140 hMod = LoadLibrary("KERNEL32.DLL");
141 if (hMod)
142 {
143 *(uintptr_t *)&gCtx.pfnGlobalMemoryStatusEx = (uintptr_t)GetProcAddress(hMod, "GlobalMemoryStatusEx");
144 if (gCtx.pfnGlobalMemoryStatusEx)
145 VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.GlobalMemoryStatusEx = %x\n", gCtx.pfnGlobalMemoryStatusEx);
146 else
147 {
148 /** @todo now fails in NT4; do we care? */
149 VBoxServiceError("VBoxStatsInit: KERNEL32.GlobalMemoryStatusEx not found!!\n");
150 return VERR_NOT_IMPLEMENTED;
151 }
152 }
153 /* GetPerformanceInfo is xp and up, so load it dynamically */
154 hMod = LoadLibrary("PSAPI.DLL");
155 if (hMod)
156 {
157 *(uintptr_t *)&gCtx.pfnGetPerformanceInfo = (uintptr_t)GetProcAddress(hMod, "GetPerformanceInfo");
158 if (gCtx.pfnGetPerformanceInfo)
159 VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnGetPerformanceInfo= %x\n", gCtx.pfnGetPerformanceInfo);
160 /* failure is not fatal */
161 }
162#endif /* RT_OS_WINDOWS */
163
164 return VINF_SUCCESS;
165}
166
167
168/**
169 * Gathers VM statistics and reports them to the host.
170 *
171 * @returns
172 * @param oid .
173 */
174static void VBoxServiceVMStatsReport(void)
175{
176#if defined(RT_OS_WINDOWS)
177 SYSTEM_INFO systemInfo;
178 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo;
179 MEMORYSTATUSEX memStatus;
180 uint32_t cbStruct;
181 DWORD cbReturned;
182
183 Assert(gCtx.pfnGlobalMemoryStatusEx && gCtx.pfnNtQuerySystemInformation);
184 if ( !gCtx.pfnGlobalMemoryStatusEx
185 || !gCtx.pfnNtQuerySystemInformation)
186 return;
187
188 /* Clear the report so we don't report garbage should NtQuerySystemInformation
189 behave in an unexpected manner. */
190 VMMDevReportGuestStats req;
191 RT_ZERO(req);
192
193 /* Query and report guest statistics */
194 GetSystemInfo(&systemInfo);
195
196 memStatus.dwLength = sizeof(memStatus);
197 gCtx.pfnGlobalMemoryStatusEx(&memStatus);
198
199 req.guestStats.u32PageSize = systemInfo.dwPageSize;
200 req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / _4K);
201 req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / _4K);
202 /* The current size of the committed memory limit, in bytes. This is physical
203 memory plus the size of the page file, minus a small overhead. */
204 req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / _4K) - req.guestStats.u32PhysMemTotal;
205 req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad;
206 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
207 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
208 | VBOX_GUEST_STAT_PAGE_FILE_SIZE
209 | VBOX_GUEST_STAT_MEMORY_LOAD;
210#ifdef VBOX_WITH_MEMBALLOON
211 req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K);
212 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
213#else
214 req.guestStats.u32PhysMemBalloon = 0;
215#endif
216
217 if (gCtx.pfnGetPerformanceInfo)
218 {
219 PERFORMANCE_INFORMATION perfInfo;
220
221 if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
222 {
223 req.guestStats.u32Processes = perfInfo.ProcessCount;
224 req.guestStats.u32Threads = perfInfo.ThreadCount;
225 req.guestStats.u32Handles = perfInfo.HandleCount;
226 req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
227 req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
228 req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
229 req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
230 req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
231 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES
232 | VBOX_GUEST_STAT_MEM_COMMIT_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_TOTAL
233 | VBOX_GUEST_STAT_MEM_KERNEL_PAGED | VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED
234 | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE;
235 }
236 else
237 VBoxServiceVerbose(3, "GetPerformanceInfo failed with %d\n", GetLastError());
238 }
239
240 /* Query CPU load information */
241 cbStruct = systemInfo.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
242 pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)RTMemAlloc(cbStruct);
243 if (!pProcInfo)
244 return;
245
246 /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
247 NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
248 if ( !rc
249 && cbReturned == cbStruct)
250 {
251 if (gCtx.au64LastCpuLoad_Kernel == 0)
252 {
253 /* first time */
254 gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart;
255 gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart;
256 gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart;
257
258 Sleep(250);
259
260 rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
261 Assert(!rc);
262 }
263
264 uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.au64LastCpuLoad_Idle[0]);
265 uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.au64LastCpuLoad_Kernel[0]);
266 uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.au64LastCpuLoad_User[0]);
267 deltaKernel -= deltaIdle; /* idle time is added to kernel time */
268 uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
269
270 req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
271 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
272 req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
273
274 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER;
275
276 gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart;
277 gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart;
278 gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart;
279 /** @todo SMP: report details for each CPU? */
280 }
281
282 for (uint32_t i = 0; i < systemInfo.dwNumberOfProcessors; i++)
283 {
284 req.guestStats.u32CpuId = i;
285
286 rc = VbglR3StatReport(&req);
287 if (RT_SUCCESS(rc))
288 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", i);
289 else
290 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: DeviceIoControl (stats report) failed with %d\n", GetLastError());
291 }
292
293 RTMemFree(pProcInfo);
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 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: 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_PAGE_FILE_SIZE;
356#ifdef VBOX_WITH_MEMBALLOON
357 req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_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 - gCtx.au64LastCpuLoad_Idle[u32CpuId];
407 uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[u32CpuId];
408 uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[u32CpuId];
409 uint64_t u64DeltaNice = u64Nice - gCtx.au64LastCpuLoad_Nice[u32CpuId];
410
411 uint64_t u64DeltaAll = u64DeltaIdle
412 + u64DeltaSystem
413 + u64DeltaUser
414 + u64DeltaNice;
415
416 gCtx.au64LastCpuLoad_Idle[u32CpuId] = u64Idle;
417 gCtx.au64LastCpuLoad_Kernel[u32CpuId] = u64System;
418 gCtx.au64LastCpuLoad_User[u32CpuId] = u64User;
419 gCtx.au64LastCpuLoad_Nice[u32CpuId] = u64Nice;
420
421 req.guestStats.u32CpuId = u32CpuId;
422 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
423 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
424 req.guestStats.u32CpuLoad_User = (uint32_t)((u64DeltaUser
425 + u64DeltaNice) * 100 / u64DeltaAll);
426 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE
427 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
428 | VBOX_GUEST_STAT_CPU_LOAD_USER;
429 fCpuInfoAvail = true;
430 rc = VbglR3StatReport(&req);
431 if (RT_SUCCESS(rc))
432 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", u32CpuId);
433 else
434 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
435 }
436 else
437 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: skipping information for CPU%u\n", u32CpuId);
438 }
439 }
440 RTStrmClose(pStrm);
441 }
442 if (!fCpuInfoAvail)
443 {
444 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n");
445 rc = VbglR3StatReport(&req);
446 if (RT_SUCCESS(rc))
447 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n");
448 else
449 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: 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, "unix", 0 /* instance */, "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, "pagestotal");
471 if (pStat)
472 u64Total = pStat->value.ul;
473
474 pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, "freemem");
475 if (pStat)
476 u64Free = pStat->value.ul;
477 }
478 }
479
480 kstat_t *pStatZFS = kstat_lookup(pStatKern, "zfs", 0 /* instance */, "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, "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, "unix", 0 /* instance */, "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, "unix", 0 /* instance */, "vminfo");
504 if (pStatVMInfo)
505 {
506 vminfo_t VMInfo;
507 rc = kstat_read(pStatKern, pStatVMInfo, &VMInfo);
508 if (rc != -1)
509 {
510 u64PagedTotal = VMInfo.swap_avail / SysInfo.updates;
511 }
512 }
513 }
514 }
515
516 req.guestStats.u32PhysMemTotal = u64Total; /* already in pages */
517 req.guestStats.u32PhysMemAvail = u64Free; /* already in pages */
518 req.guestStats.u32MemSystemCache = u64Cached / _4K;
519 req.guestStats.u32PageFileSize = u64PagedTotal; /* already in pages */
520 /** @todo req.guestStats.u32Threads */
521 /** @todo req.guestStats.u32Processes */
522 /** @todo req.guestStats.u32Handles -- ??? */
523 /** @todo req.guestStats.u32MemoryLoad */
524 /** @todo req.guestStats.u32MemCommitTotal */
525 /** @todo req.guestStats.u32MemKernelTotal */
526 /** @todo req.guestStats.u32MemKernelPaged */
527 /** @todo req.guestStats.u32MemKernelNonPaged */
528 req.guestStats.u32PageSize = getpagesize();
529
530 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL
531 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL
532 | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
533#ifdef VBOX_WITH_MEMBALLOON
534 req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K);
535 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
536#else
537 req.guestStats.u32PhysMemBalloon = 0;
538#endif
539
540 /*
541 * CPU statistics.
542 */
543 cpu_stat_t StatCPU;
544 RT_ZERO(StatCPU);
545 kstat_t *pStatNode = NULL;
546 uint32_t cCPUs = 0;
547 bool fCpuInfoAvail = false;
548 for (pStatNode = pStatKern->kc_chain; pStatNode != NULL; pStatNode = pStatNode->ks_next)
549 {
550 if (!strcmp(pStatNode->ks_module, "cpu_stat"))
551 {
552 rc = kstat_read(pStatKern, pStatNode, &StatCPU);
553 if (rc == -1)
554 break;
555
556 uint64_t u64Idle = StatCPU.cpu_sysinfo.cpu[CPU_IDLE];
557 uint64_t u64User = StatCPU.cpu_sysinfo.cpu[CPU_USER];
558 uint64_t u64System = StatCPU.cpu_sysinfo.cpu[CPU_KERNEL];
559
560 uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[cCPUs];
561 uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[cCPUs];
562 uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[cCPUs];
563
564 uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser;
565
566 gCtx.au64LastCpuLoad_Idle[cCPUs] = u64Idle;
567 gCtx.au64LastCpuLoad_Kernel[cCPUs] = u64System;
568 gCtx.au64LastCpuLoad_User[cCPUs] = u64User;
569
570 req.guestStats.u32CpuId = cCPUs;
571 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
572 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
573 req.guestStats.u32CpuLoad_User = (uint32_t)(u64DeltaUser * 100 / u64DeltaAll);
574
575 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE
576 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL
577 | VBOX_GUEST_STAT_CPU_LOAD_USER;
578 fCpuInfoAvail = true;
579
580 rc = VbglR3StatReport(&req);
581 if (RT_SUCCESS(rc))
582 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", cCPUs);
583 else
584 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
585 cCPUs++;
586 }
587 }
588
589 /*
590 * Report whatever statistics were collected.
591 */
592 if (!fCpuInfoAvail)
593 {
594 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n");
595 rc = VbglR3StatReport(&req);
596 if (RT_SUCCESS(rc))
597 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n");
598 else
599 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
600 }
601
602 kstat_close(pStatKern);
603 }
604
605#else
606 /* todo: implement for other platforms. */
607
608#endif
609}
610
611/** @copydoc VBOXSERVICE::pfnWorker */
612DECLCALLBACK(int) VBoxServiceVMStatsWorker(bool volatile *pfShutdown)
613{
614 int rc = VINF_SUCCESS;
615
616 /* Start monitoring of the stat event change event. */
617 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0);
618 if (RT_FAILURE(rc))
619 {
620 VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
621 return rc;
622 }
623
624 /*
625 * Tell the control thread that it can continue
626 * spawning services.
627 */
628 RTThreadUserSignal(RTThreadSelf());
629
630 /*
631 * Now enter the loop retrieving runtime data continuously.
632 */
633 for (;;)
634 {
635 uint32_t fEvents = 0;
636 RTMSINTERVAL cWaitMillies;
637
638 /* Check if an update interval change is pending. */
639 rc = VbglR3WaitEvent(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
640 if ( RT_SUCCESS(rc)
641 && (fEvents & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST))
642 {
643 VbglR3StatQueryInterval(&gCtx.cMsStatInterval);
644 }
645
646 if (gCtx.cMsStatInterval)
647 {
648 VBoxServiceVMStatsReport();
649 cWaitMillies = gCtx.cMsStatInterval;
650 }
651 else
652 cWaitMillies = 3000;
653
654 /*
655 * Block for a while.
656 *
657 * The event semaphore takes care of ignoring interruptions and it
658 * allows us to implement service wakeup later.
659 */
660 if (*pfShutdown)
661 break;
662 int rc2 = RTSemEventMultiWait(g_VMStatEvent, cWaitMillies);
663 if (*pfShutdown)
664 break;
665 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
666 {
667 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
668 rc = rc2;
669 break;
670 }
671 }
672
673 /* Cancel monitoring of the stat event change event. */
674 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST);
675 if (RT_FAILURE(rc))
676 VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
677
678 RTSemEventMultiDestroy(g_VMStatEvent);
679 g_VMStatEvent = NIL_RTSEMEVENTMULTI;
680
681 VBoxServiceVerbose(3, "VBoxStatsThread: finished statistics change request thread\n");
682 return 0;
683}
684
685
686/** @copydoc VBOXSERVICE::pfnTerm */
687static DECLCALLBACK(void) VBoxServiceVMStatsTerm(void)
688{
689 VBoxServiceVerbose(3, "VBoxServiceVMStatsTerm\n");
690 return;
691}
692
693
694/** @copydoc VBOXSERVICE::pfnStop */
695static DECLCALLBACK(void) VBoxServiceVMStatsStop(void)
696{
697 RTSemEventMultiSignal(g_VMStatEvent);
698}
699
700
701/**
702 * The 'vminfo' service description.
703 */
704VBOXSERVICE g_VMStatistics =
705{
706 /* pszName. */
707 "vmstats",
708 /* pszDescription. */
709 "Virtual Machine Statistics",
710 /* pszUsage. */
711 NULL,
712 /* pszOptions. */
713 NULL,
714 /* methods */
715 VBoxServiceVMStatsPreInit,
716 VBoxServiceVMStatsOption,
717 VBoxServiceVMStatsInit,
718 VBoxServiceVMStatsWorker,
719 VBoxServiceVMStatsStop,
720 VBoxServiceVMStatsTerm
721};
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