VirtualBox

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

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

Main/Metrics: devmapper support in Linux (#6345)

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