VirtualBox

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

Last change on this file since 43840 was 43840, checked in by vboxsync, 13 years ago

Main/Metrics: Fix volume name extraction in Linux (#6345)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: PerformanceLinux.cpp 43840 2012-11-08 13:33:23Z vboxsync $ */
2
3/** @file
4 *
5 * VBox Linux-specific Performance Classes implementation.
6 */
7
8/*
9 * Copyright (C) 2008-2011 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 getProcessMemoryUsage(RTPROCESS process, ULONG *used);
52
53 virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
54 virtual int getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx);
55 virtual int getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms);
56 virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
57
58 virtual int getDiskListByFs(const char *name, DiskList& list);
59private:
60 virtual int _getRawHostCpuLoad();
61 int getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed);
62 char *getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits);
63 void addVolumeDependencies(const char *pcszVolume, DiskList& listDisks);
64
65 struct VMProcessStats
66 {
67 uint64_t cpuUser;
68 uint64_t cpuKernel;
69 ULONG pagesUsed;
70 };
71
72 typedef std::map<RTPROCESS, VMProcessStats> VMProcessMap;
73
74 VMProcessMap mProcessStats;
75 uint64_t mUser, mKernel, mIdle;
76 uint64_t mSingleUser, mSingleKernel, mSingleIdle;
77 uint32_t mHZ;
78};
79
80CollectorHAL *createHAL()
81{
82 return new CollectorLinux();
83}
84
85// Collector HAL for Linux
86
87CollectorLinux::CollectorLinux()
88{
89 long hz = sysconf(_SC_CLK_TCK);
90 if (hz == -1)
91 {
92 LogRel(("CollectorLinux failed to obtain HZ from kernel, assuming 100.\n"));
93 mHZ = 100;
94 }
95 else
96 mHZ = hz;
97 LogFlowThisFunc(("mHZ=%u\n", mHZ));
98}
99
100int CollectorLinux::preCollect(const CollectorHints& hints, uint64_t /* iTick */)
101{
102 std::vector<RTPROCESS> processes;
103 hints.getProcesses(processes);
104
105 std::vector<RTPROCESS>::iterator it;
106 for (it = processes.begin(); it != processes.end(); it++)
107 {
108 VMProcessStats vmStats;
109 int rc = getRawProcessStats(*it, &vmStats.cpuUser, &vmStats.cpuKernel, &vmStats.pagesUsed);
110 /* On failure, do NOT stop. Just skip the entry. Having the stats for
111 * one (probably broken) process frozen/zero is a minor issue compared
112 * to not updating many process stats and the host cpu stats. */
113 if (RT_SUCCESS(rc))
114 mProcessStats[*it] = vmStats;
115 }
116 if (hints.isHostCpuLoadCollected() || mProcessStats.size())
117 {
118 _getRawHostCpuLoad();
119 }
120 return VINF_SUCCESS;
121}
122
123int CollectorLinux::_getRawHostCpuLoad()
124{
125 int rc = VINF_SUCCESS;
126 long long unsigned uUser, uNice, uKernel, uIdle, uIowait, uIrq, uSoftirq;
127 FILE *f = fopen("/proc/stat", "r");
128
129 if (f)
130 {
131 char szBuf[128];
132 if (fgets(szBuf, sizeof(szBuf), f))
133 {
134 if (sscanf(szBuf, "cpu %llu %llu %llu %llu %llu %llu %llu",
135 &uUser, &uNice, &uKernel, &uIdle, &uIowait,
136 &uIrq, &uSoftirq) == 7)
137 {
138 mUser = uUser + uNice;
139 mKernel = uKernel + uIrq + uSoftirq;
140 mIdle = uIdle + uIowait;
141 }
142 /* Try to get single CPU stats. */
143 if (fgets(szBuf, sizeof(szBuf), f))
144 {
145 if (sscanf(szBuf, "cpu0 %llu %llu %llu %llu %llu %llu %llu",
146 &uUser, &uNice, &uKernel, &uIdle, &uIowait,
147 &uIrq, &uSoftirq) == 7)
148 {
149 mSingleUser = uUser + uNice;
150 mSingleKernel = uKernel + uIrq + uSoftirq;
151 mSingleIdle = uIdle + uIowait;
152 }
153 else
154 {
155 /* Assume that this is not an SMP system. */
156 Assert(RTMpGetCount() == 1);
157 mSingleUser = mUser;
158 mSingleKernel = mKernel;
159 mSingleIdle = mIdle;
160 }
161 }
162 else
163 rc = VERR_FILE_IO_ERROR;
164 }
165 else
166 rc = VERR_FILE_IO_ERROR;
167 fclose(f);
168 }
169 else
170 rc = VERR_ACCESS_DENIED;
171
172 return rc;
173}
174
175int CollectorLinux::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
176{
177 *user = mUser;
178 *kernel = mKernel;
179 *idle = mIdle;
180 return VINF_SUCCESS;
181}
182
183int CollectorLinux::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
184{
185 VMProcessMap::const_iterator it = mProcessStats.find(process);
186
187 if (it == mProcessStats.end())
188 {
189 Log (("No stats pre-collected for process %x\n", process));
190 return VERR_INTERNAL_ERROR;
191 }
192 *user = it->second.cpuUser;
193 *kernel = it->second.cpuKernel;
194 *total = mUser + mKernel + mIdle;
195 return VINF_SUCCESS;
196}
197
198int CollectorLinux::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available)
199{
200 int rc = VINF_SUCCESS;
201 ULONG buffers, cached;
202 FILE *f = fopen("/proc/meminfo", "r");
203
204 if (f)
205 {
206 int processed = fscanf(f, "MemTotal: %u kB\n", total);
207 processed += fscanf(f, "MemFree: %u kB\n", available);
208 processed += fscanf(f, "Buffers: %u kB\n", &buffers);
209 processed += fscanf(f, "Cached: %u kB\n", &cached);
210 if (processed == 4)
211 {
212 *available += buffers + cached;
213 *used = *total - *available;
214 }
215 else
216 rc = VERR_FILE_IO_ERROR;
217 fclose(f);
218 }
219 else
220 rc = VERR_ACCESS_DENIED;
221
222 return rc;
223}
224
225int CollectorLinux::getHostFilesystemUsage(const char *path, ULONG *total, ULONG *used, ULONG *available)
226{
227 struct statvfs stats;
228 const unsigned _MB = 1024 * 1024;
229
230 if (statvfs(path, &stats) == -1)
231 {
232 LogRel(("Failed to collect %s filesystem usage: errno=%d.\n", path, errno));
233 return VERR_ACCESS_DENIED;
234 }
235 uint64_t cbBlock = stats.f_frsize ? stats.f_frsize : stats.f_bsize;
236 *total = (ULONG)(cbBlock * stats.f_blocks / _MB);
237 *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _MB);
238 *available = (ULONG)(cbBlock * stats.f_bavail / _MB);
239
240 return VINF_SUCCESS;
241}
242
243int CollectorLinux::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
244{
245 VMProcessMap::const_iterator it = mProcessStats.find(process);
246
247 if (it == mProcessStats.end())
248 {
249 Log (("No stats pre-collected for process %x\n", process));
250 return VERR_INTERNAL_ERROR;
251 }
252 *used = it->second.pagesUsed * (PAGE_SIZE / 1024);
253 return VINF_SUCCESS;
254}
255
256int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed)
257{
258 int rc = VINF_SUCCESS;
259 char *pszName;
260 pid_t pid2;
261 char c;
262 int iTmp;
263 long long unsigned int u64Tmp;
264 unsigned uTmp;
265 unsigned long ulTmp;
266 signed long ilTmp;
267 ULONG u32user, u32kernel;
268 char buf[80]; /* @todo: this should be tied to max allowed proc name. */
269
270 RTStrAPrintf(&pszName, "/proc/%d/stat", process);
271 //printf("Opening %s...\n", pszName);
272 FILE *f = fopen(pszName, "r");
273 RTMemFree(pszName);
274
275 if (f)
276 {
277 if (fscanf(f, "%d %79s %c %d %d %d %d %d %u %lu %lu %lu %lu %u %u "
278 "%ld %ld %ld %ld %ld %ld %llu %lu %u",
279 &pid2, buf, &c, &iTmp, &iTmp, &iTmp, &iTmp, &iTmp, &uTmp,
280 &ulTmp, &ulTmp, &ulTmp, &ulTmp, &u32user, &u32kernel,
281 &ilTmp, &ilTmp, &ilTmp, &ilTmp, &ilTmp, &ilTmp, &u64Tmp,
282 &ulTmp, memPagesUsed) == 24)
283 {
284 Assert((pid_t)process == pid2);
285 *cpuUser = u32user;
286 *cpuKernel = u32kernel;
287 }
288 else
289 rc = VERR_FILE_IO_ERROR;
290 fclose(f);
291 }
292 else
293 rc = VERR_ACCESS_DENIED;
294
295 return rc;
296}
297
298int CollectorLinux::getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx)
299{
300 int rc = VINF_SUCCESS;
301 char szIfName[/*IFNAMSIZ*/ 16 + 36];
302 long long unsigned int u64Rx, u64Tx;
303
304 RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/rx_bytes", name);
305 FILE *f = fopen(szIfName, "r");
306 if (f)
307 {
308 if (fscanf(f, "%llu", &u64Rx) == 1)
309 *rx = u64Rx;
310 else
311 rc = VERR_FILE_IO_ERROR;
312 fclose(f);
313 RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/tx_bytes", name);
314 f = fopen(szIfName, "r");
315 if (f)
316 {
317 if (fscanf(f, "%llu", &u64Tx) == 1)
318 *tx = u64Tx;
319 else
320 rc = VERR_FILE_IO_ERROR;
321 fclose(f);
322 }
323 else
324 rc = VERR_ACCESS_DENIED;
325 }
326 else
327 rc = VERR_ACCESS_DENIED;
328
329 return rc;
330}
331
332int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms)
333{
334 int rc = VINF_SUCCESS;
335 char szIfName[/*IFNAMSIZ*/ 16 + 36];
336 long long unsigned int u64Busy, tmp;
337
338 RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/block/%s/stat", name);
339 FILE *f = fopen(szIfName, "r");
340 if (f)
341 {
342 if (fscanf(f, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
343 &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &u64Busy, &tmp) == 11)
344 {
345 *disk_ms = u64Busy;
346 *total_ms = (uint64_t)(mSingleUser + mSingleKernel + mSingleIdle) * 1000 / mHZ;
347 }
348 else
349 rc = VERR_FILE_IO_ERROR;
350 fclose(f);
351 }
352 else
353 rc = VERR_ACCESS_DENIED;
354
355 return rc;
356}
357
358char *CollectorLinux::getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits)
359{
360 unsigned cbName = 0;
361 unsigned cbDevName = strlen(pszDevName);
362 const char *pszEnd = pszDevName + cbDevName - 1;
363 if (fTrimDigits)
364 while (pszEnd > pszDevName && RT_C_IS_DIGIT(*pszEnd))
365 pszEnd--;
366 while (pszEnd > pszDevName && *pszEnd != '/')
367 {
368 cbName++;
369 pszEnd--;
370 }
371 RTStrCopy(pszDiskName, RT_MIN(cbName + 1, cbDiskName), pszEnd + 1);
372 return pszDiskName;
373}
374
375void CollectorLinux::addVolumeDependencies(const char *pcszVolume, DiskList& listDisks)
376{
377 char szVolInfo[RTPATH_MAX];
378 int rc = RTPathExecDir(szVolInfo, sizeof(szVolInfo) - sizeof("/" VBOXVOLINFO_NAME " ") - strlen(pcszVolume));
379 if (RT_FAILURE(rc))
380 {
381 LogRel(("VolInfo: Failed to get program path, rc=%Rrc\n", rc));
382 return VERR_INVALID_PARAMETER;
383 }
384 strcat(szVolInfo, "/" VBOXVOLINFO_NAME " ");
385 strcat(szVolInfo, pcszVolume);
386
387 FILE *fp = popen(szVolInfo, "r");
388 if (fp)
389 {
390 char szBuf[128];
391
392 while (fgets(szBuf, sizeof(szBuf), fp))
393 listDisks.push_back(RTCString(szBuf));
394
395 pclose(fp);
396 }
397 else
398 listDisks.push_back(RTCString(pcszVolume));
399}
400
401int CollectorLinux::getDiskListByFs(const char *pszPath, DiskList& listDisks)
402{
403 FILE *mtab = setmntent("/etc/mtab", "r");
404 if (mtab)
405 {
406 struct mntent *mntent;
407 while ((mntent = getmntent(mtab)))
408 {
409 if (strcmp(pszPath, mntent->mnt_dir) == 0)
410 {
411 char szDevName[128];
412 if (strncmp(mntent->mnt_fsname, "/dev/mapper", 11))
413 {
414 getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, true);
415 listDisks.push_back(RTCString(szDevName));
416 }
417 else
418 {
419 getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, false);
420 addVolumeDependencies(szDevName, listDisks);
421 }
422 break;
423 }
424 }
425 endmntent(mtab);
426 }
427 return VINF_SUCCESS;
428}
429
430}
431
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