diff options
Diffstat (limited to 'src/VBox/Main/src-server/linux/PerformanceLinux.cpp')
-rw-r--r-- | src/VBox/Main/src-server/linux/PerformanceLinux.cpp | 336 |
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; } } |