VirtualBox

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

Last change on this file since 48798 was 48311, checked in by vboxsync, 11 years ago

Warnings.

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