summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
diff options
context:
space:
mode:
authorFelix Geyer <fgeyer@debian.org>2013-01-25 18:09:04 +0100
committerFelix Geyer <fgeyer@debian.org>2013-01-25 18:09:04 +0100
commit490244144bf10ecd165f2f81f2c88b7781c91d85 (patch)
tree7bc392b380dda58cfee860a4db82fc1b133ac663 /src/VBox/Main/src-server/linux/PerformanceLinux.cpp
parentb0bc77b9da451781ff6b93f0e1b470f2bd41537c (diff)
downloadvirtualbox-490244144bf10ecd165f2f81f2c88b7781c91d85.tar.gz
Imported Upstream version 4.2.6-dfsgupstream/4.2.6-dfsg
Diffstat (limited to 'src/VBox/Main/src-server/linux/PerformanceLinux.cpp')
-rw-r--r--src/VBox/Main/src-server/linux/PerformanceLinux.cpp310
1 files changed, 302 insertions, 8 deletions
diff --git a/src/VBox/Main/src-server/linux/PerformanceLinux.cpp b/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
index f5589995c..63116c149 100644
--- a/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
+++ b/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
@@ -18,10 +18,18 @@
*/
#include <stdio.h>
+#include <unistd.h>
+#include <sys/statvfs.h>
+#include <errno.h>
+#include <mntent.h>
#include <iprt/alloc.h>
+#include <iprt/cdefs.h>
+#include <iprt/ctype.h>
#include <iprt/err.h>
#include <iprt/param.h>
+#include <iprt/path.h>
#include <iprt/string.h>
+#include <iprt/mp.h>
#include <map>
#include <vector>
@@ -29,20 +37,32 @@
#include "Logging.h"
#include "Performance.h"
+#define VBOXVOLINFO_NAME "VBoxVolInfo"
+
namespace pm {
class CollectorLinux : public CollectorHAL
{
public:
+ CollectorLinux();
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& list);
private:
- virtual int _getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
+ virtual int _getRawHostCpuLoad();
int getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed);
+ char *getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits);
+ void addVolumeDependencies(const char *pcszVolume, DiskList& listDisks);
+ char *trimTrailingDigits(char *pszName);
struct VMProcessStats
{
@@ -55,6 +75,8 @@ private:
VMProcessMap mProcessStats;
uint64_t mUser, mKernel, mIdle;
+ uint64_t mSingleUser, mSingleKernel, mSingleIdle;
+ uint32_t mHZ;
};
CollectorHAL *createHAL()
@@ -64,6 +86,19 @@ CollectorHAL *createHAL()
// Collector HAL for Linux
+CollectorLinux::CollectorLinux()
+{
+ long hz = sysconf(_SC_CLK_TCK);
+ if (hz == -1)
+ {
+ LogRel(("CollectorLinux failed to obtain HZ from kernel, assuming 100.\n"));
+ mHZ = 100;
+ }
+ else
+ mHZ = hz;
+ LogFlowThisFunc(("mHZ=%u\n", mHZ));
+}
+
int CollectorLinux::preCollect(const CollectorHints& hints, uint64_t /* iTick */)
{
std::vector<RTPROCESS> processes;
@@ -82,24 +117,52 @@ int CollectorLinux::preCollect(const CollectorHints& hints, uint64_t /* iTick */
}
if (hints.isHostCpuLoadCollected() || mProcessStats.size())
{
- _getRawHostCpuLoad(&mUser, &mKernel, &mIdle);
+ _getRawHostCpuLoad();
}
return VINF_SUCCESS;
}
-int CollectorLinux::_getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
+int CollectorLinux::_getRawHostCpuLoad()
{
int rc = VINF_SUCCESS;
- ULONG u32user, u32nice, u32kernel, u32idle;
+ long long unsigned uUser, uNice, uKernel, uIdle, uIowait, uIrq, uSoftirq;
FILE *f = fopen("/proc/stat", "r");
if (f)
{
- if (fscanf(f, "cpu %u %u %u %u", &u32user, &u32nice, &u32kernel, &u32idle) == 4)
+ char szBuf[128];
+ if (fgets(szBuf, sizeof(szBuf), f))
{
- *user = (uint64_t)u32user + u32nice;
- *kernel = u32kernel;
- *idle = u32idle;
+ if (sscanf(szBuf, "cpu %llu %llu %llu %llu %llu %llu %llu",
+ &uUser, &uNice, &uKernel, &uIdle, &uIowait,
+ &uIrq, &uSoftirq) == 7)
+ {
+ mUser = uUser + uNice;
+ mKernel = uKernel + uIrq + uSoftirq;
+ mIdle = uIdle + uIowait;
+ }
+ /* Try to get single CPU stats. */
+ if (fgets(szBuf, sizeof(szBuf), f))
+ {
+ if (sscanf(szBuf, "cpu0 %llu %llu %llu %llu %llu %llu %llu",
+ &uUser, &uNice, &uKernel, &uIdle, &uIowait,
+ &uIrq, &uSoftirq) == 7)
+ {
+ mSingleUser = uUser + uNice;
+ mSingleKernel = uKernel + uIrq + uSoftirq;
+ mSingleIdle = uIdle + uIowait;
+ }
+ else
+ {
+ /* Assume that this is not an SMP system. */
+ Assert(RTMpGetCount() == 1);
+ mSingleUser = mUser;
+ mSingleKernel = mKernel;
+ mSingleIdle = mIdle;
+ }
+ }
+ else
+ rc = VERR_FILE_IO_ERROR;
}
else
rc = VERR_FILE_IO_ERROR;
@@ -161,6 +224,49 @@ int CollectorLinux::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *availab
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)
+ {
+ LogRel(("Failed to collect %s filesystem usage: errno=%d.\n", path, errno));
+ 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);
+
+ return VINF_SUCCESS;
+}
+
+int CollectorLinux::getHostDiskSize(const char *name, uint64_t *size)
+{
+ int rc = VINF_SUCCESS;
+ char *pszName = NULL;
+ long long unsigned int u64Size;
+
+ RTStrAPrintf(&pszName, "/sys/block/%s/size", name);
+ Assert(pszName);
+ FILE *f = fopen(pszName, "r");
+ RTMemFree(pszName);
+
+ if (f)
+ {
+ if (fscanf(f, "%llu", &u64Size) == 1)
+ *size = u64Size * 512;
+ else
+ rc = VERR_FILE_IO_ERROR;
+ fclose(f);
+ }
+ else
+ rc = VERR_ACCESS_DENIED;
+
+ return rc;
+}
+
int CollectorLinux::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
{
VMProcessMap::const_iterator it = mProcessStats.find(process);
@@ -216,5 +322,193 @@ int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uin
return rc;
}
+int CollectorLinux::getRawHostNetworkLoad(const char *name, 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;
+
+ return rc;
+}
+
+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;
+
+ RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/block/%s/stat", name);
+ FILE *f = fopen(szIfName, "r");
+ if (f)
+ {
+ if (fscanf(f, "%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;
+ }
+ else
+ rc = VERR_FILE_IO_ERROR;
+ fclose(f);
+ }
+ 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;
+}
+
+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;
+}
+
+char *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;
+ 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::addVolumeDependencies(const char *pcszVolume, DiskList& listDisks)
+{
+ char szVolInfo[RTPATH_MAX];
+ int rc = RTPathExecDir(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))
+ listDisks.push_back(RTCString(trimTrailingDigits(szBuf)));
+
+ pclose(fp);
+ }
+ else
+ listDisks.push_back(RTCString(pcszVolume));
+}
+
+int CollectorLinux::getDiskListByFs(const char *pszPath, DiskList& listDisks)
+{
+ FILE *mtab = setmntent("/etc/mtab", "r");
+ if (mtab)
+ {
+ struct mntent *mntent;
+ while ((mntent = getmntent(mtab)))
+ {
+ if (strcmp(pszPath, mntent->mnt_dir) == 0)
+ {
+ char szDevName[128];
+ if (strncmp(mntent->mnt_fsname, "/dev/mapper", 11))
+ {
+ getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, true);
+ listDisks.push_back(RTCString(szDevName));
+ }
+ else
+ {
+ getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, false);
+ addVolumeDependencies(szDevName, listDisks);
+ }
+ break;
+ }
+ }
+ endmntent(mtab);
+ }
+ return VINF_SUCCESS;
+}
+
}