VirtualBox

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

Last change on this file since 57143 was 56458, checked in by vboxsync, 10 years ago

handle return code

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