summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/linux/PerformanceLinux.cpp')
-rw-r--r--src/VBox/Main/src-server/linux/PerformanceLinux.cpp336
1 files changed, 274 insertions, 62 deletions
diff --git a/src/VBox/Main/src-server/linux/PerformanceLinux.cpp b/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
index 4e165ee3..77ccb9b8 100644
--- a/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
+++ b/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
@@ -6,7 +6,7 @@
*/
/*
- * Copyright (C) 2008-2011 Oracle Corporation
+ * Copyright (C) 2008-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -27,8 +27,11 @@
#include <iprt/ctype.h>
#include <iprt/err.h>
#include <iprt/param.h>
+#include <iprt/path.h>
#include <iprt/string.h>
+#include <iprt/system.h>
#include <iprt/mp.h>
+#include <iprt/linux/sysfs.h>
#include <map>
#include <vector>
@@ -36,6 +39,8 @@
#include "Logging.h"
#include "Performance.h"
+#define VBOXVOLINFO_NAME "VBoxVolInfo"
+
namespace pm {
class CollectorLinux : public CollectorHAL
@@ -45,15 +50,23 @@ public:
virtual int preCollect(const CollectorHints& hints, uint64_t /* iTick */);
virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
virtual int getHostFilesystemUsage(const char *name, ULONG *total, ULONG *used, ULONG *available);
+ virtual int getHostDiskSize(const char *name, uint64_t *size);
virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used);
virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
virtual int getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx);
virtual int getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms);
virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
+
+ virtual int getDiskListByFs(const char *name, DiskList& listUsage, DiskList& listLoad);
private:
virtual int _getRawHostCpuLoad();
int getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed);
+ void getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits);
+ void addVolumeDependencies(const char *pcszVolume, DiskList& listDisks);
+ void addRaidDisks(const char *pcszDevice, DiskList& listDisks);
+ char *trimTrailingDigits(char *pszName);
+ char *trimNewline(char *pszName);
struct VMProcessStats
{
@@ -68,6 +81,7 @@ private:
uint64_t mUser, mKernel, mIdle;
uint64_t mSingleUser, mSingleKernel, mSingleIdle;
uint32_t mHZ;
+ ULONG totalRAM;
};
CollectorHAL *createHAL()
@@ -88,6 +102,13 @@ CollectorLinux::CollectorLinux()
else
mHZ = hz;
LogFlowThisFunc(("mHZ=%u\n", mHZ));
+
+ uint64_t cb;
+ int rc = RTSystemQueryTotalRam(&cb);
+ if (RT_FAILURE(rc))
+ totalRAM = 0;
+ else
+ totalRAM = (ULONG)(cb / 1024);
}
int CollectorLinux::preCollect(const CollectorHints& hints, uint64_t /* iTick */)
@@ -190,35 +211,21 @@ int CollectorLinux::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint
int CollectorLinux::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available)
{
- int rc = VINF_SUCCESS;
- ULONG buffers, cached;
- FILE *f = fopen("/proc/meminfo", "r");
-
- if (f)
+ AssertReturn(totalRAM, VERR_INTERNAL_ERROR);
+ uint64_t cb;
+ int rc = RTSystemQueryAvailableRam(&cb);
+ if (RT_SUCCESS(rc))
{
- int processed = fscanf(f, "MemTotal: %u kB\n", total);
- processed += fscanf(f, "MemFree: %u kB\n", available);
- processed += fscanf(f, "Buffers: %u kB\n", &buffers);
- processed += fscanf(f, "Cached: %u kB\n", &cached);
- if (processed == 4)
- {
- *available += buffers + cached;
- *used = *total - *available;
- }
- else
- rc = VERR_FILE_IO_ERROR;
- fclose(f);
+ *total = totalRAM;
+ *available = cb / 1024;
+ *used = *total - *available;
}
- else
- rc = VERR_ACCESS_DENIED;
-
return rc;
}
int CollectorLinux::getHostFilesystemUsage(const char *path, ULONG *total, ULONG *used, ULONG *available)
{
struct statvfs stats;
- const unsigned _MB = 1024 * 1024;
if (statvfs(path, &stats) == -1)
{
@@ -226,13 +233,35 @@ int CollectorLinux::getHostFilesystemUsage(const char *path, ULONG *total, ULONG
return VERR_ACCESS_DENIED;
}
uint64_t cbBlock = stats.f_frsize ? stats.f_frsize : stats.f_bsize;
- *total = (ULONG)(cbBlock * stats.f_blocks / _MB);
- *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _MB);
- *available = (ULONG)(cbBlock * stats.f_bavail / _MB);
+ *total = (ULONG)(cbBlock * stats.f_blocks / _1M);
+ *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _1M);
+ *available = (ULONG)(cbBlock * stats.f_bavail / _1M);
return VINF_SUCCESS;
}
+int CollectorLinux::getHostDiskSize(const char *pszFile, uint64_t *size)
+{
+ char *pszPath = NULL;
+
+ RTStrAPrintf(&pszPath, "/sys/block/%s/size", pszFile);
+ Assert(pszPath);
+
+ int rc = VINF_SUCCESS;
+ if (!RTLinuxSysFsExists(pszPath))
+ rc = VERR_FILE_NOT_FOUND;
+ else
+ {
+ int64_t cSize = RTLinuxSysFsReadIntFile(0, pszPath);
+ if (cSize < 0)
+ rc = VERR_ACCESS_DENIED;
+ else
+ *size = cSize * 512;
+ }
+ RTStrFree(pszPath);
+ return rc;
+}
+
int CollectorLinux::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
{
VMProcessMap::const_iterator it = mProcessStats.find(process);
@@ -261,9 +290,8 @@ int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uin
char buf[80]; /* @todo: this should be tied to max allowed proc name. */
RTStrAPrintf(&pszName, "/proc/%d/stat", process);
- //printf("Opening %s...\n", pszName);
FILE *f = fopen(pszName, "r");
- RTMemFree(pszName);
+ RTStrFree(pszName);
if (f)
{
@@ -288,42 +316,35 @@ int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uin
return rc;
}
-int CollectorLinux::getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx)
+int CollectorLinux::getRawHostNetworkLoad(const char *pszFile, uint64_t *rx, uint64_t *tx)
{
- int rc = VINF_SUCCESS;
char szIfName[/*IFNAMSIZ*/ 16 + 36];
- long long unsigned int u64Rx, u64Tx;
- RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/rx_bytes", name);
- FILE *f = fopen(szIfName, "r");
- if (f)
- {
- if (fscanf(f, "%llu", &u64Rx) == 1)
- *rx = u64Rx;
- else
- rc = VERR_FILE_IO_ERROR;
- fclose(f);
- RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/tx_bytes", name);
- f = fopen(szIfName, "r");
- if (f)
- {
- if (fscanf(f, "%llu", &u64Tx) == 1)
- *tx = u64Tx;
- else
- rc = VERR_FILE_IO_ERROR;
- fclose(f);
- }
- else
- rc = VERR_ACCESS_DENIED;
- }
- else
- rc = VERR_ACCESS_DENIED;
+ RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/rx_bytes", pszFile);
+ if (!RTLinuxSysFsExists(szIfName))
+ return VERR_FILE_NOT_FOUND;
- return rc;
+ int64_t cSize = RTLinuxSysFsReadIntFile(0, szIfName);
+ if (cSize < 0)
+ return VERR_ACCESS_DENIED;
+
+ *rx = cSize;
+
+ RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/tx_bytes", pszFile);
+ if (!RTLinuxSysFsExists(szIfName))
+ return VERR_FILE_NOT_FOUND;
+
+ cSize = RTLinuxSysFsReadIntFile(0, szIfName);
+ if (cSize < 0)
+ return VERR_ACCESS_DENIED;
+
+ *tx = cSize;
+ return VINF_SUCCESS;
}
int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms)
{
+#if 0
int rc = VINF_SUCCESS;
char szIfName[/*IFNAMSIZ*/ 16 + 36];
long long unsigned int u64Busy, tmp;
@@ -344,28 +365,184 @@ int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint
}
else
rc = VERR_ACCESS_DENIED;
+#else
+ int rc = VERR_MISSING;
+ FILE *f = fopen("/proc/diskstats", "r");
+ if (f)
+ {
+ char szBuf[128];
+ while (fgets(szBuf, sizeof(szBuf), f))
+ {
+ char *pszBufName = szBuf;
+ while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */
+ while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip major */
+ while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */
+ while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip minor */
+ while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */
+
+ char *pszBufData = strchr(pszBufName, ' ');
+ if (!pszBufData)
+ {
+ LogRel(("CollectorLinux::getRawHostDiskLoad() failed to parse disk stats: %s\n", szBuf));
+ continue;
+ }
+ *pszBufData++ = '\0';
+ if (!strcmp(name, pszBufName))
+ {
+ long long unsigned int u64Busy, tmp;
+
+ if (sscanf(pszBufData, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
+ &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &u64Busy, &tmp) == 11)
+ {
+ *disk_ms = u64Busy;
+ *total_ms = (uint64_t)(mSingleUser + mSingleKernel + mSingleIdle) * 1000 / mHZ;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_FILE_IO_ERROR;
+ break;
+ }
+ }
+ fclose(f);
+ }
+#endif
return rc;
}
-static char *getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName)
+char *CollectorLinux::trimNewline(char *pszName)
+{
+ unsigned cbName = strlen(pszName);
+ if (cbName == 0)
+ return pszName;
+
+ char *pszEnd = pszName + cbName - 1;
+ while (pszEnd > pszName && *pszEnd == '\n')
+ pszEnd--;
+ pszEnd[1] = '\0';
+
+ return pszName;
+}
+
+char *CollectorLinux::trimTrailingDigits(char *pszName)
+{
+ unsigned cbName = strlen(pszName);
+ if (cbName == 0)
+ return pszName;
+
+ char *pszEnd = pszName + cbName - 1;
+ while (pszEnd > pszName && (RT_C_IS_DIGIT(*pszEnd) || *pszEnd == '\n'))
+ pszEnd--;
+ pszEnd[1] = '\0';
+
+ return pszName;
+}
+
+/**
+ * Use the partition name to get the name of the disk. Any path component is stripped.
+ * if fTrimDigits is true, trailing digits are stripped as well, for example '/dev/sda5'
+ * is converted to 'sda'.
+ *
+ * @param pszDiskName Where to store the name of the disk.
+ * @param cbDiskName The size of the buffer pszDiskName points to.
+ * @param pszDevName The device name used to get the disk name.
+ * @param fTrimDigits Trim trailing digits (e.g. /dev/sda5)
+ */
+void CollectorLinux::getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits)
{
unsigned cbName = 0;
unsigned cbDevName = strlen(pszDevName);
const char *pszEnd = pszDevName + cbDevName - 1;
- while (pszEnd > pszDevName && RT_C_IS_DIGIT(*pszEnd))
- pszEnd--;
+ if (fTrimDigits)
+ while (pszEnd > pszDevName && RT_C_IS_DIGIT(*pszEnd))
+ pszEnd--;
while (pszEnd > pszDevName && *pszEnd != '/')
{
cbName++;
pszEnd--;
}
RTStrCopy(pszDiskName, RT_MIN(cbName + 1, cbDiskName), pszEnd + 1);
- return pszDiskName;
}
+void CollectorLinux::addRaidDisks(const char *pcszDevice, DiskList& listDisks)
+{
+ FILE *f = fopen("/proc/mdstat", "r");
+ if (f)
+ {
+ char szBuf[128];
+ while (fgets(szBuf, sizeof(szBuf), f))
+ {
+ char *pszBufName = szBuf;
-int getDiskListByFs(const char *pszPath, DiskList& listDisks)
+ char *pszBufData = strchr(pszBufName, ' ');
+ if (!pszBufData)
+ {
+ LogRel(("CollectorLinux::addRaidDisks() failed to parse disk stats: %s\n", szBuf));
+ continue;
+ }
+ *pszBufData++ = '\0';
+ if (!strcmp(pcszDevice, pszBufName))
+ {
+ while (*pszBufData == ':') ++pszBufData; /* Skip delimiter */
+ while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */
+ while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip status */
+ while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */
+ while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip type */
+
+ while (*pszBufData != '\0')
+ {
+ while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */
+ char *pszDisk = pszBufData;
+ while (RT_C_IS_ALPHA(*pszBufData))
+ ++pszBufData;
+ if (*pszBufData)
+ {
+ *pszBufData++ = '\0';
+ listDisks.push_back(RTCString(pszDisk));
+ while (*pszBufData != '\0' && *pszBufData != ' ')
+ ++pszBufData;
+ }
+ else
+ listDisks.push_back(RTCString(pszDisk));
+ }
+ break;
+ }
+ }
+ fclose(f);
+ }
+}
+
+void CollectorLinux::addVolumeDependencies(const char *pcszVolume, DiskList& listDisks)
+{
+ char szVolInfo[RTPATH_MAX];
+ int rc = RTPathAppPrivateArch(szVolInfo,
+ sizeof(szVolInfo) - sizeof("/" VBOXVOLINFO_NAME " ") - strlen(pcszVolume));
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VolInfo: Failed to get program path, rc=%Rrc\n", rc));
+ return;
+ }
+ strcat(szVolInfo, "/" VBOXVOLINFO_NAME " ");
+ strcat(szVolInfo, pcszVolume);
+
+ FILE *fp = popen(szVolInfo, "r");
+ if (fp)
+ {
+ char szBuf[128];
+
+ while (fgets(szBuf, sizeof(szBuf), fp))
+ if (strncmp(szBuf, RT_STR_TUPLE("dm-")))
+ listDisks.push_back(RTCString(trimTrailingDigits(szBuf)));
+ else
+ listDisks.push_back(RTCString(trimNewline(szBuf)));
+
+ pclose(fp);
+ }
+ else
+ listDisks.push_back(RTCString(pcszVolume));
+}
+
+int CollectorLinux::getDiskListByFs(const char *pszPath, DiskList& listUsage, DiskList& listLoad)
{
FILE *mtab = setmntent("/etc/mtab", "r");
if (mtab)
@@ -373,10 +550,45 @@ int getDiskListByFs(const char *pszPath, DiskList& listDisks)
struct mntent *mntent;
while ((mntent = getmntent(mtab)))
{
+ /* Skip rootfs entry, there must be another root mount. */
+ if (strcmp(mntent->mnt_fsname, "rootfs") == 0)
+ continue;
if (strcmp(pszPath, mntent->mnt_dir) == 0)
{
- char szDevName[32];
- listDisks.push_back(RTCString(getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname)));
+ char szDevName[128];
+ char szFsName[1024];
+ /* Try to resolve symbolic link if necessary. Yes, we access the file system here! */
+ int rc = RTPathReal(mntent->mnt_fsname, szFsName, sizeof(szFsName));
+ if (RT_FAILURE(rc))
+ continue; /* something got wrong, just ignore this path */
+ /* check against the actual mtab entry, NOT the real path as /dev/mapper/xyz is
+ * often a symlink to something else */
+ if (!strncmp(mntent->mnt_fsname, RT_STR_TUPLE("/dev/mapper")))
+ {
+ /* LVM */
+ getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, false /*=fTrimDigits*/);
+ addVolumeDependencies(szDevName, listUsage);
+ listLoad = listUsage;
+ }
+ else if (!strncmp(szFsName, RT_STR_TUPLE("/dev/md")))
+ {
+ /* Software RAID */
+ getDiskName(szDevName, sizeof(szDevName), szFsName, false /*=fTrimDigits*/);
+ listUsage.push_back(RTCString(szDevName));
+ addRaidDisks(szDevName, listLoad);
+ }
+ else
+ {
+ /* Plain disk partition. Trim the trailing digits to get the drive name */
+ getDiskName(szDevName, sizeof(szDevName), szFsName, true /*=fTrimDigits*/);
+ listUsage.push_back(RTCString(szDevName));
+ listLoad.push_back(RTCString(szDevName));
+ }
+ if (listUsage.empty() || listLoad.empty())
+ {
+ LogRel(("Failed to retrive disk info: getDiskName(%s) --> %s\n",
+ mntent->mnt_fsname, szDevName));
+ }
break;
}
}