VirtualBox

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

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

Main/Metrics: Linux fs/disk metrics, VBoxManage filtering + minor fixes (#6345)

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