VirtualBox

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

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

Additions/VBoxService: Solaris VM statistics.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 26.1 KB
Line 
1/* $Id: VBoxServiceStats.cpp 28009 2010-04-06 14:23:05Z 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 reported successfully!\n");
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
347 req.guestStats.u32PageSize = getpagesize();
348 req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K);
349 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL \
350 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL \
351 | VBOX_GUEST_STAT_PHYS_MEM_BALLOON \
352 | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
353
354 /** @todo req.guestStats.u32Threads */
355 /** @todo req.guestStats.u32Processes */
356 /* req.guestStats.u32Handles doesn't make sense here. */
357 /** @todo req.guestStats.u32MemoryLoad */
358 /** @todo req.guestStats.u32MemCommitTotal */
359 /** @todo req.guestStats.u32MemKernelTotal */
360 /** @todo req.guestStats.u32MemKernelPaged, make any sense? = u32MemKernelTotal? */
361 /** @todo req.guestStats.u32MemKernelNonPaged, make any sense? = 0? */
362
363 rc = RTStrmOpen("/proc/stat", "r", &pStrm);
364 if (RT_SUCCESS(rc))
365 {
366 for (;;)
367 {
368 rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine));
369 if (RT_FAILURE(rc))
370 break;
371 if ( strstr(szLine, "cpu") == szLine
372 && strlen(szLine) > 3
373 && RT_C_IS_DIGIT(szLine[3]))
374 {
375 uint32_t u32CpuId;
376 rc = RTStrToUInt32Ex(&szLine[3], &psz, 0, &u32CpuId);
377 if (u32CpuId < VMM_MAX_CPU_COUNT)
378 {
379 uint64_t u64User = 0;
380 if (RT_SUCCESS(rc))
381 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64User);
382
383 uint64_t u64Nice = 0;
384 if (RT_SUCCESS(rc))
385 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Nice);
386
387 uint64_t u64System = 0;
388 if (RT_SUCCESS(rc))
389 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64System);
390
391 uint64_t u64Idle = 0;
392 if (RT_SUCCESS(rc))
393 rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Idle);
394
395 uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[u32CpuId];
396 uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[u32CpuId];
397 uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[u32CpuId];
398 uint64_t u64DeltaNice = u64Nice - gCtx.au64LastCpuLoad_Nice[u32CpuId];
399
400 uint64_t u64DeltaAll = u64DeltaIdle
401 + u64DeltaSystem
402 + u64DeltaUser
403 + u64DeltaNice;
404
405 gCtx.au64LastCpuLoad_Idle[u32CpuId] = u64Idle;
406 gCtx.au64LastCpuLoad_Kernel[u32CpuId] = u64System;
407 gCtx.au64LastCpuLoad_User[u32CpuId] = u64User;
408 gCtx.au64LastCpuLoad_Nice[u32CpuId] = u64Nice;
409
410 req.guestStats.u32CpuId = u32CpuId;
411 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
412 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
413 req.guestStats.u32CpuLoad_User = (uint32_t)((u64DeltaUser
414 + u64DeltaNice) * 100 / u64DeltaAll);
415 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE \
416 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL \
417 | VBOX_GUEST_STAT_CPU_LOAD_USER;
418 rc = VbglR3StatReport(&req);
419 if (RT_SUCCESS(rc))
420 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n");
421 else
422 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
423 }
424 }
425 }
426 RTStrmClose(pStrm);
427 }
428
429#elif defined(RT_OS_SOLARIS)
430 VMMDevReportGuestStats req;
431 RT_ZERO(req);
432 kstat_ctl_t *pStatKern = kstat_open();
433 if (pStatKern)
434 {
435 /*
436 * Memory statistics.
437 */
438 uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0;
439 int rc = -1;
440 kstat_t *pStatPages = kstat_lookup(pStatKern, "unix", 0 /* instance */, "system_pages");
441 if (pStatPages)
442 {
443 rc = kstat_read(pStatKern, pStatPages, NULL /* optional-copy-buf */);
444 if (rc != -1)
445 {
446 kstat_named_t *pStat = NULL;
447 pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, "pagestotal");
448 if (pStat)
449 u64Total = pStat->value.ul;
450
451 pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, "freemem");
452 if (pStat)
453 u64Free = pStat->value.ul;
454 }
455 }
456
457 kstat_t *pStatZFS = kstat_lookup(pStatKern, "zfs", 0 /* instance */, "arcstats");
458 if (pStatZFS)
459 {
460 rc = kstat_read(pStatKern, pStatZFS, NULL /* optional-copy-buf */);
461 if (rc != -1)
462 {
463 kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, "size");
464 if (pStat)
465 u64Cached = pStat->value.ul;
466 }
467 }
468
469 /*
470 * The vminfo are accumulative counters updated every "N" ticks. Let's get the
471 * number of stat updates so far and use that to divide the swap counter.
472 */
473 kstat_t *pStatInfo = kstat_lookup(pStatKern, "unix", 0 /* instance */, "sysinfo");
474 if (pStatInfo)
475 {
476 sysinfo_t SysInfo;
477 rc = kstat_read(pStatKern, pStatInfo, &SysInfo);
478 if (rc != -1)
479 {
480 kstat_t *pStatVMInfo = kstat_lookup(pStatKern, "unix", 0 /* instance */, "vminfo");
481 if (pStatVMInfo)
482 {
483 vminfo_t VMInfo;
484 rc = kstat_read(pStatKern, pStatVMInfo, &VMInfo);
485 if (rc != -1)
486 {
487 u64PagedTotal = VMInfo.swap_avail / SysInfo.updates;
488 }
489 }
490 }
491 }
492
493 req.guestStats.u32PhysMemTotal = u64Total; /* already in pages */
494 req.guestStats.u32PhysMemAvail = u64Free; /* already in pages */
495 req.guestStats.u32MemSystemCache = u64Cached / _4K;
496 req.guestStats.u32PageFileSize = u64PagedTotal; /* already in pages */
497 /** @todo req.guestStats.u32Threads */
498 /** @todo req.guestStats.u32Processes */
499 /** @todo req.guestStats.u32Handles -- ??? */
500 /** @todo req.guestStats.u32MemoryLoad */
501 /** @todo req.guestStats.u32MemCommitTotal */
502 /** @todo req.guestStats.u32MemKernelTotal */
503 /** @todo req.guestStats.u32MemKernelPaged */
504 /** @todo req.guestStats.u32MemKernelNonPaged */
505 req.guestStats.u32PageSize = getpagesize();
506 req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K);
507 req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL \
508 | VBOX_GUEST_STAT_PHYS_MEM_AVAIL \
509 | VBOX_GUEST_STAT_PHYS_MEM_BALLOON \
510 | VBOX_GUEST_STAT_PAGE_FILE_SIZE;
511
512 /*
513 * CPU statistics.
514 */
515 cpu_stat_t StatCPU;
516 RT_ZERO(StatCPU);
517 kstat_t *pStatNode = NULL;
518 uint32_t cCPUs = 0;
519 for (pStatNode = pStatKern->kc_chain; pStatNode != NULL; pStatNode = pStatNode->ks_next)
520 {
521 if (!strcmp(pStatNode->ks_module, "cpu_stat"))
522 {
523 rc = kstat_read(pStatKern, pStatNode, &StatCPU);
524 if (rc == -1)
525 break;
526
527 uint64_t u64Idle = StatCPU.cpu_sysinfo.cpu[CPU_IDLE];
528 uint64_t u64User = StatCPU.cpu_sysinfo.cpu[CPU_USER];
529 uint64_t u64System = StatCPU.cpu_sysinfo.cpu[CPU_KERNEL];
530
531 uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[cCPUs];
532 uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[cCPUs];
533 uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[cCPUs];
534
535 uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser;
536
537 gCtx.au64LastCpuLoad_Idle[cCPUs] = u64Idle;
538 gCtx.au64LastCpuLoad_Kernel[cCPUs] = u64System;
539 gCtx.au64LastCpuLoad_User[cCPUs] = u64User;
540
541 req.guestStats.u32CpuId = cCPUs;
542 req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll);
543 req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll);
544
545 req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE \
546 | VBOX_GUEST_STAT_CPU_LOAD_KERNEL \
547 | VBOX_GUEST_STAT_CPU_LOAD_USER;
548
549 cCPUs++;
550 }
551 }
552
553 /*
554 * Report whatever statistics were collected.
555 */
556 rc = VbglR3StatReport(&req);
557 if (RT_SUCCESS(rc))
558 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n");
559 else
560 VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc);
561
562 kstat_close(pStatKern);
563 }
564
565#else
566 /* todo: implement for other platforms. */
567
568#endif
569}
570
571/** @copydoc VBOXSERVICE::pfnWorker */
572DECLCALLBACK(int) VBoxServiceVMStatsWorker(bool volatile *pfShutdown)
573{
574 int rc = VINF_SUCCESS;
575
576 /* Start monitoring of the stat event change event. */
577 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0);
578 if (RT_FAILURE(rc))
579 {
580 VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
581 return rc;
582 }
583
584 /*
585 * Tell the control thread that it can continue
586 * spawning services.
587 */
588 RTThreadUserSignal(RTThreadSelf());
589
590 /*
591 * Now enter the loop retrieving runtime data continuously.
592 */
593 for (;;)
594 {
595 uint32_t fEvents = 0;
596 RTMSINTERVAL cWaitMillies;
597
598 /* Check if an update interval change is pending. */
599 rc = VbglR3WaitEvent(VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
600 if ( RT_SUCCESS(rc)
601 && (fEvents & VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST))
602 {
603 VbglR3StatQueryInterval(&gCtx.cMsStatInterval);
604 }
605
606 if (gCtx.cMsStatInterval)
607 {
608 VBoxServiceVMStatsReport();
609 cWaitMillies = gCtx.cMsStatInterval;
610 }
611 else
612 cWaitMillies = 3000;
613
614 /*
615 * Block for a while.
616 *
617 * The event semaphore takes care of ignoring interruptions and it
618 * allows us to implement service wakeup later.
619 */
620 if (*pfShutdown)
621 break;
622 int rc2 = RTSemEventMultiWait(g_VMStatEvent, cWaitMillies);
623 if (*pfShutdown)
624 break;
625 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
626 {
627 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
628 rc = rc2;
629 break;
630 }
631 }
632
633 /* Cancel monitoring of the stat event change event. */
634 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST);
635 if (RT_FAILURE(rc))
636 VBoxServiceVerbose(3, "VBoxServiceVMStatsWorker: VbglR3CtlFilterMask failed with %d\n", rc);
637
638 RTSemEventMultiDestroy(g_VMStatEvent);
639 g_VMStatEvent = NIL_RTSEMEVENTMULTI;
640
641 VBoxServiceVerbose(3, "VBoxStatsThread: finished statistics change request thread\n");
642 return 0;
643}
644
645
646/** @copydoc VBOXSERVICE::pfnTerm */
647static DECLCALLBACK(void) VBoxServiceVMStatsTerm(void)
648{
649 VBoxServiceVerbose(3, "VBoxServiceVMStatsTerm\n");
650 return;
651}
652
653
654/** @copydoc VBOXSERVICE::pfnStop */
655static DECLCALLBACK(void) VBoxServiceVMStatsStop(void)
656{
657 RTSemEventMultiSignal(g_VMStatEvent);
658}
659
660
661/**
662 * The 'vminfo' service description.
663 */
664VBOXSERVICE g_VMStatistics =
665{
666 /* pszName. */
667 "vmstats",
668 /* pszDescription. */
669 "Virtual Machine Statistics",
670 /* pszUsage. */
671 NULL,
672 /* pszOptions. */
673 NULL,
674 /* methods */
675 VBoxServiceVMStatsPreInit,
676 VBoxServiceVMStatsOption,
677 VBoxServiceVMStatsInit,
678 VBoxServiceVMStatsWorker,
679 VBoxServiceVMStatsStop,
680 VBoxServiceVMStatsTerm
681};
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