VirtualBox

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

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

Main/Metrics: Disk and filesystem metrics for Solaris (#6345)

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