VirtualBox

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

Last change on this file since 28630 was 28508, checked in by vboxsync, 15 years ago

Solaris additions build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 27.5 KB
Line 
1/* $Id: VBoxServiceStats.cpp 28508 2010-04-20 09:39:17Z 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.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K);
211 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_PAGE_FILE_SIZE
212 | VBOX_GUEST_STAT_MEMORY_LOAD | VBOX_GUEST_STAT_PHYS_MEM_BALLOON;
213
214 if (gCtx.pfnGetPerformanceInfo)
215 {
216 PERFORMANCE_INFORMATION perfInfo;
217
218 if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo)))
219 {
220 req.guestStats.u32Processes = perfInfo.ProcessCount;
221 req.guestStats.u32Threads = perfInfo.ThreadCount;
222 req.guestStats.u32Handles = perfInfo.HandleCount;
223 req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */
224 req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */
225 req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */
226 req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */
227 req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */
228 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES
229 | VBOX_GUEST_STAT_MEM_COMMIT_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_TOTAL
230 | VBOX_GUEST_STAT_MEM_KERNEL_PAGED | VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED
231 | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE;
232 }
233 else
234 VBoxServiceVerbose(3, "GetPerformanceInfo failed with %d\n", GetLastError());
235 }
236
237 /* Query CPU load information */
238 cbStruct = systemInfo.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
239 pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)RTMemAlloc(cbStruct);
240 if (!pProcInfo)
241 return;
242
243 /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */
244 NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
245 if ( !rc
246 && cbReturned == cbStruct)
247 {
248 if (gCtx.au64LastCpuLoad_Kernel == 0)
249 {
250 /* first time */
251 gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart;
252 gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart;
253 gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart;
254
255 Sleep(250);
256
257 rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned);
258 Assert(!rc);
259 }
260
261 uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.au64LastCpuLoad_Idle[0]);
262 uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.au64LastCpuLoad_Kernel[0]);
263 uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.au64LastCpuLoad_User[0]);
264 deltaKernel -= deltaIdle; /* idle time is added to kernel time */
265 uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser;
266
267 req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime);
268 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime);
269 req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime);
270
271 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER;
272
273 gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart;
274 gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart;
275 gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart;
276 /** @todo SMP: report details for each CPU? */
277 }
278
279 for (uint32_t i = 0; i < systemInfo.dwNumberOfProcessors; i++)
280 {
281 req.guestStats.u32CpuId = i;
282
283 rc = VbglR3StatReport(&req);
284 if (RT_SUCCESS(rc))
285 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", i);
286 else
287 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: DeviceIoControl (stats report) failed with %d\n", GetLastError());
288 }
289
290 RTMemFree(pProcInfo);
291
292#elif defined(RT_OS_LINUX)
293 VMMDevReportGuestStats req;
294 RT_ZERO(req);
295 PRTSTREAM pStrm;
296 char szLine[256];
297 char *psz;
298
299 int rc = RTStrmOpen("/proc/meminfo", "r", &pStrm);
300 if (RT_SUCCESS(rc))
301 {
302 uint64_t u64Kb;
303 uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0;
304 for (;;)
305 {
306 rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine));
307 if (RT_FAILURE(rc))
308 break;
309 if (strstr(szLine, "MemTotal:") == szLine)
310 {
311 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[9]), &psz, 0, &u64Kb);
312 if (RT_SUCCESS(rc))
313 u64Total = u64Kb * _1K;
314 }
315 else if (strstr(szLine, "MemFree:") == szLine)
316 {
317 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb);
318 if (RT_SUCCESS(rc))
319 u64Free = u64Kb * _1K;
320 }
321 else if (strstr(szLine, "Buffers:") == szLine)
322 {
323 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb);
324 if (RT_SUCCESS(rc))
325 u64Buffers = u64Kb * _1K;
326 }
327 else if (strstr(szLine, "Cached:") == szLine)
328 {
329 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[7]), &psz, 0, &u64Kb);
330 if (RT_SUCCESS(rc))
331 u64Cached = u64Kb * _1K;
332 }
333 else if (strstr(szLine, "SwapTotal:") == szLine)
334 {
335 rc = RTStrToUInt64Ex(RTStrStripL(&szLine[10]), &psz, 0, &u64Kb);
336 if (RT_SUCCESS(rc))
337 u64PagedTotal = u64Kb * _1K;
338 }
339 }
340 req.guestStats.u32PhysMemTotal = u64Total / _4K;
341 req.guestStats.u32PhysMemAvail = (u64Free + u64Buffers + u64Cached) / _4K;
342 req.guestStats.u32MemSystemCache = (u64Buffers + u64Cached) / _4K;
343 req.guestStats.u32PageFileSize = u64PagedTotal / _4K;
344 RTStrmClose(pStrm);
345 }
346 else
347 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: memory info not available!\n");
348
349 req.guestStats.u32PageSize = getpagesize();
350 req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K);
351 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL \
352 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL \
353 | VBOX_GUEST_STAT_PHYS_MEM_BALLOON \
354 | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
355
356 /** @todo req.guestStats.u32Threads */
357 /** @todo req.guestStats.u32Processes */
358 /* req.guestStats.u32Handles doesn't make sense here. */
359 /** @todo req.guestStats.u32MemoryLoad */
360 /** @todo req.guestStats.u32MemCommitTotal */
361 /** @todo req.guestStats.u32MemKernelTotal */
362 /** @todo req.guestStats.u32MemKernelPaged, make any sense? = u32MemKernelTotal? */
363 /** @todo req.guestStats.u32MemKernelNonPaged, make any sense? = 0? */
364
365 bool fCpuInfoAvail = false;
366 rc = RTStrmOpen("/proc/stat", "r", &pStrm);
367 if (RT_SUCCESS(rc))
368 {
369 for (;;)
370 {
371 rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine));
372 if (RT_FAILURE(rc))
373 break;
374 if ( strstr(szLine, "cpu") == szLine
375 && strlen(szLine) > 3
376 && RT_C_IS_DIGIT(szLine[3]))
377 {
378 uint32_t u32CpuId;
379 rc = RTStrToUInt32Ex(&szLine[3], &psz, 0, &u32CpuId);
380 if (u32CpuId < VMM_MAX_CPU_COUNT)
381 {
382 uint64_t u64User = 0;
383 if (RT_SUCCESS(rc))
384 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64User);
385
386 uint64_t u64Nice = 0;
387 if (RT_SUCCESS(rc))
388 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Nice);
389
390 uint64_t u64System = 0;
391 if (RT_SUCCESS(rc))
392 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64System);
393
394 uint64_t u64Idle = 0;
395 if (RT_SUCCESS(rc))
396 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Idle);
397
398 uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[u32CpuId];
399 uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[u32CpuId];
400 uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[u32CpuId];
401 uint64_t u64DeltaNice = u64Nice - gCtx.au64LastCpuLoad_Nice[u32CpuId];
402
403 uint64_t u64DeltaAll = u64DeltaIdle
404 + u64DeltaSystem
405 + u64DeltaUser
406 + u64DeltaNice;
407
408 gCtx.au64LastCpuLoad_Idle[u32CpuId] = u64Idle;
409 gCtx.au64LastCpuLoad_Kernel[u32CpuId] = u64System;
410 gCtx.au64LastCpuLoad_User[u32CpuId] = u64User;
411 gCtx.au64LastCpuLoad_Nice[u32CpuId] = u64Nice;
412
413 req.guestStats.u32CpuId = u32CpuId;
414 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
415 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
416 req.guestStats.u32CpuLoad_User = (uint32_t)((u64DeltaUser
417 + u64DeltaNice) * 100 / u64DeltaAll);
418 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE \
419 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL \
420 | VBOX_GUEST_STAT_CPU_LOAD_USER;
421 fCpuInfoAvail = true;
422 rc = VbglR3StatReport(&req);
423 if (RT_SUCCESS(rc))
424 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", u32CpuId);
425 else
426 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
427 }
428 else
429 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: skipping information for CPU%u\n", u32CpuId);
430 }
431 }
432 RTStrmClose(pStrm);
433 }
434 if (!fCpuInfoAvail)
435 {
436 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n");
437 rc = VbglR3StatReport(&req);
438 if (RT_SUCCESS(rc))
439 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n");
440 else
441 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
442 }
443
444#elif defined(RT_OS_SOLARIS)
445 VMMDevReportGuestStats req;
446 RT_ZERO(req);
447 kstat_ctl_t *pStatKern = kstat_open();
448 if (pStatKern)
449 {
450 /*
451 * Memory statistics.
452 */
453 uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0;
454 int rc = -1;
455 kstat_t *pStatPages = kstat_lookup(pStatKern, "unix", 0 /* instance */, "system_pages");
456 if (pStatPages)
457 {
458 rc = kstat_read(pStatKern, pStatPages, NULL /* optional-copy-buf */);
459 if (rc != -1)
460 {
461 kstat_named_t *pStat = NULL;
462 pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, "pagestotal");
463 if (pStat)
464 u64Total = pStat->value.ul;
465
466 pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, "freemem");
467 if (pStat)
468 u64Free = pStat->value.ul;
469 }
470 }
471
472 kstat_t *pStatZFS = kstat_lookup(pStatKern, "zfs", 0 /* instance */, "arcstats");
473 if (pStatZFS)
474 {
475 rc = kstat_read(pStatKern, pStatZFS, NULL /* optional-copy-buf */);
476 if (rc != -1)
477 {
478 kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, "size");
479 if (pStat)
480 u64Cached = pStat->value.ul;
481 }
482 }
483
484 /*
485 * The vminfo are accumulative counters updated every "N" ticks. Let's get the
486 * number of stat updates so far and use that to divide the swap counter.
487 */
488 kstat_t *pStatInfo = kstat_lookup(pStatKern, "unix", 0 /* instance */, "sysinfo");
489 if (pStatInfo)
490 {
491 sysinfo_t SysInfo;
492 rc = kstat_read(pStatKern, pStatInfo, &SysInfo);
493 if (rc != -1)
494 {
495 kstat_t *pStatVMInfo = kstat_lookup(pStatKern, "unix", 0 /* instance */, "vminfo");
496 if (pStatVMInfo)
497 {
498 vminfo_t VMInfo;
499 rc = kstat_read(pStatKern, pStatVMInfo, &VMInfo);
500 if (rc != -1)
501 {
502 u64PagedTotal = VMInfo.swap_avail / SysInfo.updates;
503 }
504 }
505 }
506 }
507
508 req.guestStats.u32PhysMemTotal = u64Total; /* already in pages */
509 req.guestStats.u32PhysMemAvail = u64Free; /* already in pages */
510 req.guestStats.u32MemSystemCache = u64Cached / _4K;
511 req.guestStats.u32PageFileSize = u64PagedTotal; /* already in pages */
512 /** @todo req.guestStats.u32Threads */
513 /** @todo req.guestStats.u32Processes */
514 /** @todo req.guestStats.u32Handles -- ??? */
515 /** @todo req.guestStats.u32MemoryLoad */
516 /** @todo req.guestStats.u32MemCommitTotal */
517 /** @todo req.guestStats.u32MemKernelTotal */
518 /** @todo req.guestStats.u32MemKernelPaged */
519 /** @todo req.guestStats.u32MemKernelNonPaged */
520 req.guestStats.u32PageSize = getpagesize();
521 req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K);
522 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL \
523 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL \
524 | VBOX_GUEST_STAT_PHYS_MEM_BALLOON \
525 | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
526
527 /*
528 * CPU statistics.
529 */
530 cpu_stat_t StatCPU;
531 RT_ZERO(StatCPU);
532 kstat_t *pStatNode = NULL;
533 uint32_t cCPUs = 0;
534 bool fCpuInfoAvail = false;
535 for (pStatNode = pStatKern->kc_chain; pStatNode != NULL; pStatNode = pStatNode->ks_next)
536 {
537 if (!strcmp(pStatNode->ks_module, "cpu_stat"))
538 {
539 rc = kstat_read(pStatKern, pStatNode, &StatCPU);
540 if (rc == -1)
541 break;
542
543 uint64_t u64Idle = StatCPU.cpu_sysinfo.cpu[CPU_IDLE];
544 uint64_t u64User = StatCPU.cpu_sysinfo.cpu[CPU_USER];
545 uint64_t u64System = StatCPU.cpu_sysinfo.cpu[CPU_KERNEL];
546
547 uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[cCPUs];
548 uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[cCPUs];
549 uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[cCPUs];
550
551 uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser;
552
553 gCtx.au64LastCpuLoad_Idle[cCPUs] = u64Idle;
554 gCtx.au64LastCpuLoad_Kernel[cCPUs] = u64System;
555 gCtx.au64LastCpuLoad_User[cCPUs] = u64User;
556
557 req.guestStats.u32CpuId = cCPUs;
558 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
559 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
560 req.guestStats.u32CpuLoad_User = (uint32_t)(u64DeltaUser * 100 / u64DeltaAll);
561
562 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE \
563 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL \
564 | VBOX_GUEST_STAT_CPU_LOAD_USER;
565 fCpuInfoAvail = true;
566
567 rc = VbglR3StatReport(&req);
568 if (RT_SUCCESS(rc))
569 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", cCPUs);
570 else
571 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
572 cCPUs++;
573 }
574 }
575
576 /*
577 * Report whatever statistics were collected.
578 */
579 if (!fCpuInfoAvail)
580 {
581 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n");
582 rc = VbglR3StatReport(&req);
583 if (RT_SUCCESS(rc))
584 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n");
585 else
586 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
587 }
588
589 kstat_close(pStatKern);
590 }
591
592#else
593 /* todo: implement for other platforms. */
594
595#endif
596}
597
598/** @copydoc VBOXSERVICE::pfnWorker */
599DECLCALLBACK(int) VBoxServiceVMStatsWorker(bool volatile *pfShutdown)
600{
601 int rc = VINF_SUCCESS;
602
603 /* Start monitoring of the stat event change event. */
604 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0);
605 if (RT_FAILURE(rc))
606 {
607 VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
608 return rc;
609 }
610
611 /*
612 * Tell the control thread that it can continue
613 * spawning services.
614 */
615 RTThreadUserSignal(RTThreadSelf());
616
617 /*
618 * Now enter the loop retrieving runtime data continuously.
619 */
620 for (;;)
621 {
622 uint32_t fEvents = 0;
623 RTMSINTERVAL cWaitMillies;
624
625 /* Check if an update interval change is pending. */
626 rc = VbglR3WaitEvent(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
627 if ( RT_SUCCESS(rc)
628 && (fEvents & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST))
629 {
630 VbglR3StatQueryInterval(&gCtx.cMsStatInterval);
631 }
632
633 if (gCtx.cMsStatInterval)
634 {
635 VBoxServiceVMStatsReport();
636 cWaitMillies = gCtx.cMsStatInterval;
637 }
638 else
639 cWaitMillies = 3000;
640
641 /*
642 * Block for a while.
643 *
644 * The event semaphore takes care of ignoring interruptions and it
645 * allows us to implement service wakeup later.
646 */
647 if (*pfShutdown)
648 break;
649 int rc2 = RTSemEventMultiWait(g_VMStatEvent, cWaitMillies);
650 if (*pfShutdown)
651 break;
652 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
653 {
654 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
655 rc = rc2;
656 break;
657 }
658 }
659
660 /* Cancel monitoring of the stat event change event. */
661 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST);
662 if (RT_FAILURE(rc))
663 VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
664
665 RTSemEventMultiDestroy(g_VMStatEvent);
666 g_VMStatEvent = NIL_RTSEMEVENTMULTI;
667
668 VBoxServiceVerbose(3, "VBoxStatsThread: finished statistics change request thread\n");
669 return 0;
670}
671
672
673/** @copydoc VBOXSERVICE::pfnTerm */
674static DECLCALLBACK(void) VBoxServiceVMStatsTerm(void)
675{
676 VBoxServiceVerbose(3, "VBoxServiceVMStatsTerm\n");
677 return;
678}
679
680
681/** @copydoc VBOXSERVICE::pfnStop */
682static DECLCALLBACK(void) VBoxServiceVMStatsStop(void)
683{
684 RTSemEventMultiSignal(g_VMStatEvent);
685}
686
687
688/**
689 * The 'vminfo' service description.
690 */
691VBOXSERVICE g_VMStatistics =
692{
693 /* pszName. */
694 "vmstats",
695 /* pszDescription. */
696 "Virtual Machine Statistics",
697 /* pszUsage. */
698 NULL,
699 /* pszOptions. */
700 NULL,
701 /* methods */
702 VBoxServiceVMStatsPreInit,
703 VBoxServiceVMStatsOption,
704 VBoxServiceVMStatsInit,
705 VBoxServiceVMStatsWorker,
706 VBoxServiceVMStatsStop,
707 VBoxServiceVMStatsTerm
708};
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