VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/PerformanceLinux.cpp@ 44806

Last change on this file since 44806 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1/* $Id: PerformanceLinux.cpp 44528 2013-02-04 14:27:54Z vboxsync $ */
2
3/** @file
4 *
5 * VBox Linux-specific Performance Classes implementation.
6 */
7
8/*
9 * Copyright (C) 2008-2012 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include <stdio.h>
21#include <unistd.h>
22#include <sys/statvfs.h>
23#include <errno.h>
24#include <mntent.h>
25#include <iprt/alloc.h>
26#include <iprt/cdefs.h>
27#include <iprt/ctype.h>
28#include <iprt/err.h>
29#include <iprt/param.h>
30#include <iprt/path.h>
31#include <iprt/string.h>
32#include <iprt/mp.h>
33
34#include <map>
35#include <vector>
36
37#include "Logging.h"
38#include "Performance.h"
39
40#define VBOXVOLINFO_NAME "VBoxVolInfo"
41
42namespace pm {
43
44class CollectorLinux : public CollectorHAL
45{
46public:
47 CollectorLinux();
48 virtual int preCollect(const CollectorHints& hints, uint64_t /* iTick */);
49 virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
50 virtual int getHostFilesystemUsage(const char *name, ULONG *total, ULONG *used, ULONG *available);
51 virtual int getHostDiskSize(const char *name, uint64_t *size);
52 virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used);
53
54 virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
55 virtual int getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx);
56 virtual int getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms);
57 virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
58
59 virtual int getDiskListByFs(const char *name, DiskList& list);
60private:
61 virtual int _getRawHostCpuLoad();
62 int getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed);
63 char *getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits);
64 void addVolumeDependencies(const char *pcszVolume, DiskList& listDisks);
65 char *trimTrailingDigits(char *pszName);
66
67 struct VMProcessStats
68 {
69 uint64_t cpuUser;
70 uint64_t cpuKernel;
71 ULONG pagesUsed;
72 };
73
74 typedef std::map<RTPROCESS, VMProcessStats> VMProcessMap;
75
76 VMProcessMap mProcessStats;
77 uint64_t mUser, mKernel, mIdle;
78 uint64_t mSingleUser, mSingleKernel, mSingleIdle;
79 uint32_t mHZ;
80};
81
82CollectorHAL *createHAL()
83{
84 return new CollectorLinux();
85}
86
87// Collector HAL for Linux
88
89CollectorLinux::CollectorLinux()
90{
91 long hz = sysconf(_SC_CLK_TCK);
92 if (hz == -1)
93 {
94 LogRel(("CollectorLinux failed to obtain HZ from kernel, assuming 100.\n"));
95 mHZ = 100;
96 }
97 else
98 mHZ = hz;
99 LogFlowThisFunc(("mHZ=%u\n", mHZ));
100}
101
102int CollectorLinux::preCollect(const CollectorHints& hints, uint64_t /* iTick */)
103{
104 std::vector<RTPROCESS> processes;
105 hints.getProcesses(processes);
106
107 std::vector<RTPROCESS>::iterator it;
108 for (it = processes.begin(); it != processes.end(); it++)
109 {
110 VMProcessStats vmStats;
111 int rc = getRawProcessStats(*it, &vmStats.cpuUser, &vmStats.cpuKernel, &vmStats.pagesUsed);
112 /* On failure, do NOT stop. Just skip the entry. Having the stats for
113 * one (probably broken) process frozen/zero is a minor issue compared
114 * to not updating many process stats and the host cpu stats. */
115 if (RT_SUCCESS(rc))
116 mProcessStats[*it] = vmStats;
117 }
118 if (hints.isHostCpuLoadCollected() || mProcessStats.size())
119 {
120 _getRawHostCpuLoad();
121 }
122 return VINF_SUCCESS;
123}
124
125int CollectorLinux::_getRawHostCpuLoad()
126{
127 int rc = VINF_SUCCESS;
128 long long unsigned uUser, uNice, uKernel, uIdle, uIowait, uIrq, uSoftirq;
129 FILE *f = fopen("/proc/stat", "r");
130
131 if (f)
132 {
133 char szBuf[128];
134 if (fgets(szBuf, sizeof(szBuf), f))
135 {
136 if (sscanf(szBuf, "cpu %llu %llu %llu %llu %llu %llu %llu",
137 &uUser, &uNice, &uKernel, &uIdle, &uIowait,
138 &uIrq, &uSoftirq) == 7)
139 {
140 mUser = uUser + uNice;
141 mKernel = uKernel + uIrq + uSoftirq;
142 mIdle = uIdle + uIowait;
143 }
144 /* Try to get single CPU stats. */
145 if (fgets(szBuf, sizeof(szBuf), f))
146 {
147 if (sscanf(szBuf, "cpu0 %llu %llu %llu %llu %llu %llu %llu",
148 &uUser, &uNice, &uKernel, &uIdle, &uIowait,
149 &uIrq, &uSoftirq) == 7)
150 {
151 mSingleUser = uUser + uNice;
152 mSingleKernel = uKernel + uIrq + uSoftirq;
153 mSingleIdle = uIdle + uIowait;
154 }
155 else
156 {
157 /* Assume that this is not an SMP system. */
158 Assert(RTMpGetCount() == 1);
159 mSingleUser = mUser;
160 mSingleKernel = mKernel;
161 mSingleIdle = mIdle;
162 }
163 }
164 else
165 rc = VERR_FILE_IO_ERROR;
166 }
167 else
168 rc = VERR_FILE_IO_ERROR;
169 fclose(f);
170 }
171 else
172 rc = VERR_ACCESS_DENIED;
173
174 return rc;
175}
176
177int CollectorLinux::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
178{
179 *user = mUser;
180 *kernel = mKernel;
181 *idle = mIdle;
182 return VINF_SUCCESS;
183}
184
185int CollectorLinux::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
186{
187 VMProcessMap::const_iterator it = mProcessStats.find(process);
188
189 if (it == mProcessStats.end())
190 {
191 Log (("No stats pre-collected for process %x\n", process));
192 return VERR_INTERNAL_ERROR;
193 }
194 *user = it->second.cpuUser;
195 *kernel = it->second.cpuKernel;
196 *total = mUser + mKernel + mIdle;
197 return VINF_SUCCESS;
198}
199
200int CollectorLinux::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available)
201{
202 int rc = VINF_SUCCESS;
203 ULONG buffers, cached;
204 FILE *f = fopen("/proc/meminfo", "r");
205
206 if (f)
207 {
208 int processed = fscanf(f, "MemTotal: %u kB\n", total);
209 processed += fscanf(f, "MemFree: %u kB\n", available);
210 processed += fscanf(f, "Buffers: %u kB\n", &buffers);
211 processed += fscanf(f, "Cached: %u kB\n", &cached);
212 if (processed == 4)
213 {
214 *available += buffers + cached;
215 *used = *total - *available;
216 }
217 else
218 rc = VERR_FILE_IO_ERROR;
219 fclose(f);
220 }
221 else
222 rc = VERR_ACCESS_DENIED;
223
224 return rc;
225}
226
227int CollectorLinux::getHostFilesystemUsage(const char *path, ULONG *total, ULONG *used, ULONG *available)
228{
229 struct statvfs stats;
230 const unsigned _MB = 1024 * 1024;
231
232 if (statvfs(path, &stats) == -1)
233 {
234 LogRel(("Failed to collect %s filesystem usage: errno=%d.\n", path, errno));
235 return VERR_ACCESS_DENIED;
236 }
237 uint64_t cbBlock = stats.f_frsize ? stats.f_frsize : stats.f_bsize;
238 *total = (ULONG)(cbBlock * stats.f_blocks / _MB);
239 *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _MB);
240 *available = (ULONG)(cbBlock * stats.f_bavail / _MB);
241
242 return VINF_SUCCESS;
243}
244
245int CollectorLinux::getHostDiskSize(const char *name, uint64_t *size)
246{
247 int rc = VINF_SUCCESS;
248 char *pszName = NULL;
249 long long unsigned int u64Size;
250
251 RTStrAPrintf(&pszName, "/sys/block/%s/size", name);
252 Assert(pszName);
253 FILE *f = fopen(pszName, "r");
254 RTMemFree(pszName);
255
256 if (f)
257 {
258 if (fscanf(f, "%llu", &u64Size) == 1)
259 *size = u64Size * 512;
260 else
261 rc = VERR_FILE_IO_ERROR;
262 fclose(f);
263 }
264 else
265 rc = VERR_ACCESS_DENIED;
266
267 return rc;
268}
269
270int CollectorLinux::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
271{
272 VMProcessMap::const_iterator it = mProcessStats.find(process);
273
274 if (it == mProcessStats.end())
275 {
276 Log (("No stats pre-collected for process %x\n", process));
277 return VERR_INTERNAL_ERROR;
278 }
279 *used = it->second.pagesUsed * (PAGE_SIZE / 1024);
280 return VINF_SUCCESS;
281}
282
283int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed)
284{
285 int rc = VINF_SUCCESS;
286 char *pszName;
287 pid_t pid2;
288 char c;
289 int iTmp;
290 long long unsigned int u64Tmp;
291 unsigned uTmp;
292 unsigned long ulTmp;
293 signed long ilTmp;
294 ULONG u32user, u32kernel;
295 char buf[80]; /* @todo: this should be tied to max allowed proc name. */
296
297 RTStrAPrintf(&pszName, "/proc/%d/stat", process);
298 //printf("Opening %s...\n", pszName);
299 FILE *f = fopen(pszName, "r");
300 RTMemFree(pszName);
301
302 if (f)
303 {
304 if (fscanf(f, "%d %79s %c %d %d %d %d %d %u %lu %lu %lu %lu %u %u "
305 "%ld %ld %ld %ld %ld %ld %llu %lu %u",
306 &pid2, buf, &c, &iTmp, &iTmp, &iTmp, &iTmp, &iTmp, &uTmp,
307 &ulTmp, &ulTmp, &ulTmp, &ulTmp, &u32user, &u32kernel,
308 &ilTmp, &ilTmp, &ilTmp, &ilTmp, &ilTmp, &ilTmp, &u64Tmp,
309 &ulTmp, memPagesUsed) == 24)
310 {
311 Assert((pid_t)process == pid2);
312 *cpuUser = u32user;
313 *cpuKernel = u32kernel;
314 }
315 else
316 rc = VERR_FILE_IO_ERROR;
317 fclose(f);
318 }
319 else
320 rc = VERR_ACCESS_DENIED;
321
322 return rc;
323}
324
325int CollectorLinux::getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx)
326{
327 int rc = VINF_SUCCESS;
328 char szIfName[/*IFNAMSIZ*/ 16 + 36];
329 long long unsigned int u64Rx, u64Tx;
330
331 RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/rx_bytes", name);
332 FILE *f = fopen(szIfName, "r");
333 if (f)
334 {
335 if (fscanf(f, "%llu", &u64Rx) == 1)
336 *rx = u64Rx;
337 else
338 rc = VERR_FILE_IO_ERROR;
339 fclose(f);
340 RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/tx_bytes", name);
341 f = fopen(szIfName, "r");
342 if (f)
343 {
344 if (fscanf(f, "%llu", &u64Tx) == 1)
345 *tx = u64Tx;
346 else
347 rc = VERR_FILE_IO_ERROR;
348 fclose(f);
349 }
350 else
351 rc = VERR_ACCESS_DENIED;
352 }
353 else
354 rc = VERR_ACCESS_DENIED;
355
356 return rc;
357}
358
359int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms)
360{
361#if 0
362 int rc = VINF_SUCCESS;
363 char szIfName[/*IFNAMSIZ*/ 16 + 36];
364 long long unsigned int u64Busy, tmp;
365
366 RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/block/%s/stat", name);
367 FILE *f = fopen(szIfName, "r");
368 if (f)
369 {
370 if (fscanf(f, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
371 &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &u64Busy, &tmp) == 11)
372 {
373 *disk_ms = u64Busy;
374 *total_ms = (uint64_t)(mSingleUser + mSingleKernel + mSingleIdle) * 1000 / mHZ;
375 }
376 else
377 rc = VERR_FILE_IO_ERROR;
378 fclose(f);
379 }
380 else
381 rc = VERR_ACCESS_DENIED;
382#else
383 int rc = VERR_MISSING;
384 FILE *f = fopen("/proc/diskstats", "r");
385 if (f)
386 {
387 char szBuf[128];
388 while (fgets(szBuf, sizeof(szBuf), f))
389 {
390 char *pszBufName = szBuf;
391 while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */
392 while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip major */
393 while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */
394 while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip minor */
395 while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */
396
397 char *pszBufData = strchr(pszBufName, ' ');
398 if (!pszBufData)
399 {
400 LogRel(("CollectorLinux::getRawHostDiskLoad() failed to parse disk stats: %s\n", szBuf));
401 continue;
402 }
403 *pszBufData++ = '\0';
404 if (!strcmp(name, pszBufName))
405 {
406 long long unsigned int u64Busy, tmp;
407
408 if (sscanf(pszBufData, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
409 &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &u64Busy, &tmp) == 11)
410 {
411 *disk_ms = u64Busy;
412 *total_ms = (uint64_t)(mSingleUser + mSingleKernel + mSingleIdle) * 1000 / mHZ;
413 rc = VINF_SUCCESS;
414 }
415 else
416 rc = VERR_FILE_IO_ERROR;
417 break;
418 }
419 }
420 fclose(f);
421 }
422#endif
423
424 return rc;
425}
426
427char *CollectorLinux::trimTrailingDigits(char *pszName)
428{
429 unsigned cbName = strlen(pszName);
430 if (cbName == 0)
431 return pszName;
432
433 char *pszEnd = pszName + cbName - 1;
434 while (pszEnd > pszName && (RT_C_IS_DIGIT(*pszEnd) || *pszEnd == '\n'))
435 pszEnd--;
436 pszEnd[1] = '\0';
437
438 return pszName;
439}
440
441char *CollectorLinux::getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits)
442{
443 unsigned cbName = 0;
444 unsigned cbDevName = strlen(pszDevName);
445 const char *pszEnd = pszDevName + cbDevName - 1;
446 if (fTrimDigits)
447 while (pszEnd > pszDevName && RT_C_IS_DIGIT(*pszEnd))
448 pszEnd--;
449 while (pszEnd > pszDevName && *pszEnd != '/')
450 {
451 cbName++;
452 pszEnd--;
453 }
454 RTStrCopy(pszDiskName, RT_MIN(cbName + 1, cbDiskName), pszEnd + 1);
455 return pszDiskName;
456}
457
458void CollectorLinux::addVolumeDependencies(const char *pcszVolume, DiskList& listDisks)
459{
460 char szVolInfo[RTPATH_MAX];
461 int rc = RTPathExecDir(szVolInfo, sizeof(szVolInfo) - sizeof("/" VBOXVOLINFO_NAME " ") - strlen(pcszVolume));
462 if (RT_FAILURE(rc))
463 {
464 LogRel(("VolInfo: Failed to get program path, rc=%Rrc\n", rc));
465 return;
466 }
467 strcat(szVolInfo, "/" VBOXVOLINFO_NAME " ");
468 strcat(szVolInfo, pcszVolume);
469
470 FILE *fp = popen(szVolInfo, "r");
471 if (fp)
472 {
473 char szBuf[128];
474
475 while (fgets(szBuf, sizeof(szBuf), fp))
476 listDisks.push_back(RTCString(trimTrailingDigits(szBuf)));
477
478 pclose(fp);
479 }
480 else
481 listDisks.push_back(RTCString(pcszVolume));
482}
483
484int CollectorLinux::getDiskListByFs(const char *pszPath, DiskList& listDisks)
485{
486 FILE *mtab = setmntent("/etc/mtab", "r");
487 if (mtab)
488 {
489 struct mntent *mntent;
490 while ((mntent = getmntent(mtab)))
491 {
492 if (strcmp(pszPath, mntent->mnt_dir) == 0)
493 {
494 char szDevName[128];
495 if (strncmp(mntent->mnt_fsname, "/dev/mapper", 11))
496 {
497 getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, true);
498 listDisks.push_back(RTCString(szDevName));
499 }
500 else
501 {
502 getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, false);
503 addVolumeDependencies(szDevName, listDisks);
504 }
505 break;
506 }
507 }
508 endmntent(mtab);
509 }
510 return VINF_SUCCESS;
511}
512
513}
514
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette