Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp	(revision 43628)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp	(revision 43629)
@@ -34,4 +34,7 @@
 #include <VBox/log.h>
 
+#include <set>
+#include <utility>
+
 #include "VBoxManage.h"
 using namespace com;
@@ -41,56 +44,12 @@
 ///////////////////////////////////////////////////////////////////////////////
 
-
-static bool isLastSlash(const char *str)
-{
-    char c;
-    while ((c = *str++))
-    {
-        if (c == ',')
-            break;
-        if (c == '/')
-            return false;
-    }
-
-    return true;
-}
-
-static char *toBaseMetricNames(const char *metricList)
-{
-    char *newList = (char*)RTMemAlloc(strlen(metricList) + 1);
-    if (newList)
-    {
-        int cSlashes = 0;
-        bool fSkip = false;
-        const char *src = metricList;
-        char c, *dst = newList;
-        while ((c = *src++))
-            if (c == ':')
-                fSkip = true;
-            else if (c == '/' && ++cSlashes >= 2 && isLastSlash(src))
-                fSkip = true;
-            else if (c == ',')
-            {
-                fSkip = false;
-                cSlashes = 0;
-                *dst++ = c;
-            }
-            else
-                if (!fSkip)
-                    *dst++ = c;
-        *dst = 0;
-    }
-    return newList;
-}
 
 static int parseFilterParameters(int argc, char *argv[],
                                  ComPtr<IVirtualBox> aVirtualBox,
                                  ComSafeArrayOut(BSTR, outMetrics),
-                                 ComSafeArrayOut(BSTR, outBaseMetrics),
                                  ComSafeArrayOut(IUnknown *, outObjects))
 {
     HRESULT rc = S_OK;
     com::SafeArray<BSTR> retMetrics(1);
-    com::SafeArray<BSTR> retBaseMetrics(1);
     com::SafeIfaceArray <IUnknown> retObjects;
 
@@ -99,12 +58,5 @@
     /* Metric list */
     if (argc > 1)
-    {
         metricNames = argv[1];
-        char *tmp   = toBaseMetricNames(argv[1]);
-        if (!tmp)
-            return VERR_NO_MEMORY;
-        baseNames   = tmp;
-        RTMemFree(tmp);
-    }
     else
     {
@@ -113,5 +65,4 @@
     }
     metricNames.cloneTo(&retMetrics[0]);
-    baseNames.cloneTo(&retBaseMetrics[0]);
 
     /* Object name */
@@ -145,8 +96,19 @@
 
     retMetrics.detachTo(ComSafeArrayOutArg(outMetrics));
-    retBaseMetrics.detachTo(ComSafeArrayOutArg(outBaseMetrics));
     retObjects.detachTo(ComSafeArrayOutArg(outObjects));
 
     return rc;
+}
+
+static Bstr toBaseName(Utf8Str& aFullName)
+{
+    char *pszRaw = aFullName.mutableRaw();
+    char *pszSlash = strrchr(pszRaw, '/');
+    if (pszSlash)
+    {
+        *pszSlash = 0;
+        aFullName.jolt();
+    }
+    return Bstr(aFullName);
 }
 
@@ -207,10 +169,8 @@
     HRESULT rc;
     com::SafeArray<BSTR>          metrics;
-    com::SafeArray<BSTR>          baseMetrics;
     com::SafeIfaceArray<IUnknown> objects;
 
     rc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox,
                                ComSafeArrayAsOutParam(metrics),
-                               ComSafeArrayAsOutParam(baseMetrics),
                                ComSafeArrayAsOutParam(objects));
     if (FAILED(rc))
@@ -258,5 +218,4 @@
     HRESULT rc;
     com::SafeArray<BSTR>          metrics;
-    com::SafeArray<BSTR>          baseMetrics;
     com::SafeIfaceArray<IUnknown> objects;
     uint32_t period = 1, samples = 1;
@@ -293,5 +252,4 @@
     rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
                                ComSafeArrayAsOutParam(metrics),
-                               ComSafeArrayAsOutParam(baseMetrics),
                                ComSafeArrayAsOutParam(objects));
     if (FAILED(rc))
@@ -322,10 +280,8 @@
     HRESULT rc;
     com::SafeArray<BSTR>          metrics;
-    com::SafeArray<BSTR>          baseMetrics;
     com::SafeIfaceArray<IUnknown> objects;
 
     rc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox,
                                ComSafeArrayAsOutParam(metrics),
-                               ComSafeArrayAsOutParam(baseMetrics),
                                ComSafeArrayAsOutParam(objects));
     if (FAILED(rc))
@@ -430,5 +386,4 @@
     HRESULT rc;
     com::SafeArray<BSTR>          metrics;
-    com::SafeArray<BSTR>          baseMetrics;
     com::SafeIfaceArray<IUnknown> objects;
     uint32_t period = 1, samples = 1;
@@ -467,14 +422,38 @@
     rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
                                ComSafeArrayAsOutParam(metrics),
-                               ComSafeArrayAsOutParam(baseMetrics),
                                ComSafeArrayAsOutParam(objects));
     if (FAILED(rc))
         return 1;
 
-
+    com::SafeIfaceArray<IPerformanceMetric> metricInfo;
+
+    CHECK_ERROR(performanceCollector,
+        GetMetrics(ComSafeArrayAsInParam(metrics),
+                   ComSafeArrayAsInParam(objects),
+                   ComSafeArrayAsOutParam(metricInfo)));
+
+    std::set<std::pair<ComPtr<IUnknown>,Bstr> > baseMetrics;
+    ComPtr<IUnknown> objectFiltered;
+    Bstr metricNameFiltered;
+    for (i = 0; i < (int)metricInfo.size(); i++)
+    {
+        CHECK_ERROR(metricInfo[i], COMGETTER(Object)(objectFiltered.asOutParam()));
+        CHECK_ERROR(metricInfo[i], COMGETTER(MetricName)(metricNameFiltered.asOutParam()));
+        Utf8Str baseMetricName(metricNameFiltered);
+        baseMetrics.insert(std::make_pair(objectFiltered, toBaseName(baseMetricName)));
+    }
+    com::SafeArray<BSTR>          baseMetricsFiltered(baseMetrics.size());
+    com::SafeIfaceArray<IUnknown> objectsFiltered(baseMetrics.size());
+    std::set<std::pair<ComPtr<IUnknown>,Bstr> >::iterator it;
+    i = 0;
+    for (it = baseMetrics.begin(); it != baseMetrics.end(); ++it)
+    {
+        it->first.queryInterfaceTo(&objectsFiltered[i]);
+        Bstr(it->second).detachTo(&baseMetricsFiltered[i++]);
+    }
     com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
     CHECK_ERROR(performanceCollector,
-        SetupMetrics(ComSafeArrayAsInParam(baseMetrics),
-                     ComSafeArrayAsInParam(objects), period, samples,
+        SetupMetrics(ComSafeArrayAsInParam(baseMetricsFiltered),
+                     ComSafeArrayAsInParam(objectsFiltered), period, samples,
                      ComSafeArrayAsOutParam(affectedMetrics)));
     if (FAILED(rc))
@@ -561,5 +540,4 @@
     HRESULT rc;
     com::SafeArray<BSTR>          metrics;
-    com::SafeArray<BSTR>          baseMetrics;
     com::SafeIfaceArray<IUnknown> objects;
     bool listMatches = false;
@@ -577,5 +555,4 @@
     rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
                                ComSafeArrayAsOutParam(metrics),
-                               ComSafeArrayAsOutParam(baseMetrics),
                                ComSafeArrayAsOutParam(objects));
     if (FAILED(rc))
@@ -606,5 +583,4 @@
     HRESULT rc;
     com::SafeArray<BSTR>          metrics;
-    com::SafeArray<BSTR>          baseMetrics;
     com::SafeIfaceArray<IUnknown> objects;
     bool listMatches = false;
@@ -622,5 +598,4 @@
     rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
                                ComSafeArrayAsOutParam(metrics),
-                               ComSafeArrayAsOutParam(baseMetrics),
                                ComSafeArrayAsOutParam(objects));
     if (FAILED(rc))
Index: /trunk/src/VBox/Main/include/HostImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/HostImpl.h	(revision 43628)
+++ /trunk/src/VBox/Main/include/HostImpl.h	(revision 43629)
@@ -149,4 +149,5 @@
 #ifdef VBOX_WITH_RESOURCE_USAGE_API
     void registerMetrics(PerformanceCollector *aCollector);
+    void registerDiskMetrics(PerformanceCollector *aCollector);
     void unregisterMetrics(PerformanceCollector *aCollector);
 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
Index: /trunk/src/VBox/Main/include/Performance.h
===================================================================
--- /trunk/src/VBox/Main/include/Performance.h	(revision 43628)
+++ /trunk/src/VBox/Main/include/Performance.h	(revision 43629)
@@ -42,4 +42,6 @@
     /* Network load is measured in 1/1000 of per cent. */
     const uint64_t PM_NETWORK_LOAD_MULTIPLIER = UINT64_C(100000);
+    /* Disk load is measured in 1/1000 of per cent. */
+    const uint64_t PM_DISK_LOAD_MULTIPLIER = UINT64_C(100000);
     /* Sampler precision in milliseconds. */
     const uint64_t PM_SAMPLER_PRECISION_MS = 50;
@@ -354,4 +356,6 @@
         /** Returns the amount of physical memory in kilobytes. */
         virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
+        /** Returns file system counters in megabytes. */
+        virtual int getHostFilesystemUsage(const char *name, ULONG *total, ULONG *used, ULONG *available);
         /** Returns CPU usage in 1/1000th per cent by a particular process. */
         virtual int getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel);
@@ -363,4 +367,6 @@
         /** Returns received and transmitted bytes. */
         virtual int getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx);
+        /** Returns disk usage counters in platform-specific units. */
+        virtual int getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms);
         /** Returns process' CPU usage counter in platform-specific units. */
         virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
@@ -368,4 +374,7 @@
 
     extern CollectorHAL *createHAL();
+
+    typedef std::list<RTCString> DiskList;
+    extern int getDiskListByFs(const char *name, DiskList& list);
 
     /* Base Metrics *********************************************************/
@@ -518,4 +527,48 @@
         uint64_t      mSpeed;
         int           mRc;
+    };
+
+    class HostFilesystemUsage : public BaseMetric
+    {
+    public:
+        HostFilesystemUsage(CollectorHAL *hal, ComPtr<IUnknown> object, com::Utf8Str name, com::Utf8Str fsname, SubMetric *total, SubMetric *used, SubMetric *available)
+            : BaseMetric(hal, name, object), mFsName(fsname), mTotal(total), mUsed(used), mAvailable(available) {};
+        ~HostFilesystemUsage() { delete mTotal; delete mUsed; delete mAvailable; };
+
+        void init(ULONG period, ULONG length);
+        void preCollect(CollectorHints& hints, uint64_t iTick);
+        void collect();
+        const char *getUnit() { return "mB"; };
+        ULONG getMinValue() { return 0; };
+        ULONG getMaxValue() { return INT32_MAX; };
+        ULONG getScale() { return 1; }
+    private:
+        com::Utf8Str mFsName;
+        SubMetric   *mTotal;
+        SubMetric   *mUsed;
+        SubMetric   *mAvailable;
+    };
+
+    class HostDiskLoadRaw : public BaseMetric
+    {
+    public:
+        HostDiskLoadRaw(CollectorHAL *hal, ComPtr<IUnknown> object, com::Utf8Str name, com::Utf8Str diskname, SubMetric *util)
+            : BaseMetric(hal, name, object), mDiskName(diskname), mUtil(util), mDiskPrev(0), mTotalPrev(0) {};
+        ~HostDiskLoadRaw() { delete mUtil; };
+
+        void init(ULONG period, ULONG length);
+
+        void preCollect(CollectorHints& hints, uint64_t iTick);
+        void collect();
+        const char *getUnit() { return "%"; };
+        ULONG getMinValue() { return 0; };
+        ULONG getMaxValue() { return PM_DISK_LOAD_MULTIPLIER; };
+        ULONG getScale() { return PM_DISK_LOAD_MULTIPLIER / 100; }
+
+    private:
+        com::Utf8Str  mDiskName;
+        SubMetric    *mUtil;
+        uint64_t      mDiskPrev;
+        uint64_t      mTotalPrev;
     };
 
Index: /trunk/src/VBox/Main/src-server/HostImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/HostImpl.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/HostImpl.cpp	(revision 43629)
@@ -2884,4 +2884,73 @@
 #ifdef VBOX_WITH_RESOURCE_USAGE_API
 
+void Host::registerDiskMetrics(PerformanceCollector *aCollector)
+{
+    pm::CollectorHAL *hal = aCollector->getHAL();
+    /* Create sub metrics */
+    Utf8StrFmt fsNameBase("FS/{%s}/Usage", "/");
+    //Utf8StrFmt fsNameBase("Filesystem/[root]/Usage");
+    pm::SubMetric *fsRootUsageTotal  = new pm::SubMetric(fsNameBase + "/Total",
+        "Root file system size.");
+    pm::SubMetric *fsRootUsageUsed   = new pm::SubMetric(fsNameBase + "/Used",
+        "Root file system space currently occupied.");
+    pm::SubMetric *fsRootUsageFree   = new pm::SubMetric(fsNameBase + "/Free",
+        "Root file system space currently empty.");
+
+    pm::BaseMetric *fsRootUsage = new pm::HostFilesystemUsage(hal, this,
+                                                              fsNameBase, "/",
+                                                              fsRootUsageTotal,
+                                                              fsRootUsageUsed,
+                                                              fsRootUsageFree);
+    aCollector->registerBaseMetric (fsRootUsage);
+
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal, 0));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal,
+                                              new pm::AggregateAvg()));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal,
+                                              new pm::AggregateMin()));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal,
+                                              new pm::AggregateMax()));
+
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed, 0));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed,
+                                              new pm::AggregateAvg()));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed,
+                                              new pm::AggregateMin()));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed,
+                                              new pm::AggregateMax()));
+
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree, 0));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree,
+                                              new pm::AggregateAvg()));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree,
+                                              new pm::AggregateMin()));
+    aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree,
+                                              new pm::AggregateMax()));
+
+    /* For now we are concerned with the root file system only. */
+    pm::DiskList disks;
+    int rc = pm::getDiskListByFs("/", disks);
+    if (RT_FAILURE(rc) || disks.empty())
+        return;
+    pm::DiskList::iterator it;
+    for (it = disks.begin(); it != disks.end(); ++it)
+    {
+        Utf8StrFmt strName("Disk/%s/Load", it->c_str());
+        pm::SubMetric *fsLoadUtil   = new pm::SubMetric(strName + "/Util",
+            "Percentage of time disk was busy serving I/O requests.");
+        pm::BaseMetric *fsLoad = new pm::HostDiskLoadRaw(hal, this, strName,
+                                                         *it, fsLoadUtil);
+        aCollector->registerBaseMetric (fsLoad);
+
+        aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil, 0));
+        aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil,
+                                                  new pm::AggregateAvg()));
+        aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil,
+                                                  new pm::AggregateMin()));
+        aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil,
+                                                  new pm::AggregateMax()));
+    }
+}
+
 void Host::registerMetrics(PerformanceCollector *aCollector)
 {
@@ -3017,4 +3086,5 @@
     aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared,
                                               new pm::AggregateMax()));
+    registerDiskMetrics(aCollector);
 }
 
Index: /trunk/src/VBox/Main/src-server/HostNetworkInterfaceImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/HostNetworkInterfaceImpl.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/HostNetworkInterfaceImpl.cpp	(revision 43629)
@@ -95,7 +95,7 @@
     Utf8StrFmt strName("Net/%ls/Load", mShortName.raw());
     pm::SubMetric *networkLoadRx   = new pm::SubMetric(strName + "/Rx",
-        "Percentage of network interface bandwidth used.");
+        "Percentage of network interface receive bandwidth used.");
     pm::SubMetric *networkLoadTx   = new pm::SubMetric(strName + "/Tx",
-        "Percentage of network interface bandwidth used.");
+        "Percentage of network interface transmit bandwidth used.");
 
     /* Create and register base metrics */
Index: /trunk/src/VBox/Main/src-server/Performance.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/Performance.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/Performance.cpp	(revision 43629)
@@ -64,4 +64,9 @@
 }
 
+int CollectorHAL::getRawHostDiskLoad(const char * /* name */, uint64_t * /* disk_ms */, uint64_t * /* total_ms */)
+{
+    return E_NOTIMPL;
+}
+
 int CollectorHAL::getRawProcessCpuLoad(RTPROCESS  /* process */, uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* total */)
 {
@@ -70,4 +75,9 @@
 
 int CollectorHAL::getHostMemoryUsage(ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
+{
+    return E_NOTIMPL;
+}
+
+int CollectorHAL::getHostFilesystemUsage(const char * /* name */, ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
 {
     return E_NOTIMPL;
@@ -705,4 +715,67 @@
 }
 
+void HostDiskLoadRaw::init(ULONG period, ULONG length)
+{
+    mPeriod = period;
+    mLength = length;
+    mUtil->init(mLength);
+    int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &mDiskPrev, &mTotalPrev);
+    AssertRC(rc);
+}
+
+void HostDiskLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
+{
+    hints.collectHostCpuLoad();
+}
+
+void HostDiskLoadRaw::collect()
+{
+    uint64_t disk, total;
+
+    int rc = mHAL->getRawHostDiskLoad(mDiskName.c_str(), &disk, &total);
+    if (RT_SUCCESS(rc))
+    {
+        uint64_t diskDiff = disk - mDiskPrev;
+        uint64_t totalDiff = total - mTotalPrev;
+
+        if (RT_UNLIKELY(totalDiff == 0))
+        {
+            Assert(totalDiff);
+            LogFlowThisFunc(("Improbable! Less than millisecond passed! Disk=%s\n", mDiskName.c_str()));
+            mUtil->put(0);
+        }
+        else if (diskDiff > totalDiff)
+        {
+            /*
+             * It is possible that the disk spent more time than CPU because
+             * CPU measurements are taken during the pre-collect phase. We try
+             * to compensate for than by adding the extra to the next round of
+             * measurements.
+             */
+            mUtil->put(PM_NETWORK_LOAD_MULTIPLIER);
+            Assert((diskDiff - totalDiff) < mPeriod * 1000);
+            if ((diskDiff - totalDiff) > mPeriod * 1000)
+            {
+                LogRel(("Disk utilization time exceeds CPU time by more"
+                        " than the collection period (%llu ms)\n", diskDiff - totalDiff));
+            }
+            else
+            {
+                disk = mDiskPrev + totalDiff;
+                LogFlowThisFunc(("Moved %u milliseconds to the next period.\n", (unsigned)(diskDiff - totalDiff)));
+            }
+        }
+        else
+        {
+            mUtil->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * diskDiff / totalDiff));
+        }
+
+        mDiskPrev = disk;
+        mTotalPrev = total;
+    }
+    else
+        LogFlowThisFunc(("Failed to collect data: %Rrc (%d).\n", rc));
+}
+
 void HostCpuMhz::init(ULONG period, ULONG length)
 {
@@ -738,4 +811,30 @@
     ULONG total, used, available;
     int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
+    if (RT_SUCCESS(rc))
+    {
+        mTotal->put(total);
+        mUsed->put(used);
+        mAvailable->put(available);
+
+    }
+}
+
+void HostFilesystemUsage::init(ULONG period, ULONG length)
+{
+    mPeriod = period;
+    mLength = length;
+    mTotal->init(mLength);
+    mUsed->init(mLength);
+    mAvailable->init(mLength);
+}
+
+void HostFilesystemUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */)
+{
+}
+
+void HostFilesystemUsage::collect()
+{
+    ULONG total, used, available;
+    int rc = mHAL->getHostFilesystemUsage(mFsName.c_str(), &total, &used, &available);
     if (RT_SUCCESS(rc))
     {
Index: /trunk/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp	(revision 43629)
@@ -162,4 +162,9 @@
 }
 
+int getDiskListByFs(const char *name, DiskList& list)
+{
+    return VERR_NOT_IMPLEMENTED;
 }
 
+}
+
Index: /trunk/src/VBox/Main/src-server/linux/PerformanceLinux.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/linux/PerformanceLinux.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/linux/PerformanceLinux.cpp	(revision 43629)
@@ -19,8 +19,15 @@
 
 #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/string.h>
+#include <iprt/mp.h>
 
 #include <map>
@@ -35,13 +42,16 @@
 {
 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 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);
 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);
 
@@ -57,4 +67,6 @@
     VMProcessMap mProcessStats;
     uint64_t     mUser, mKernel, mIdle;
+    uint64_t     mSingleUser, mSingleKernel, mSingleIdle;
+    uint32_t     mHZ;
 };
 
@@ -65,4 +77,17 @@
 
 // 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 */)
@@ -84,22 +109,50 @@
     if (hints.isHostCpuLoadCollected() || mProcessStats.size())
     {
-        _getRawHostCpuLoad(&mUser, &mKernel, &mIdle);
-    }
-    return VINF_SUCCESS;
-}
-
-int CollectorLinux::_getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
-{
-    int rc = VINF_SUCCESS;
-    ULONG u32user, u32nice, u32kernel, u32idle;
+        _getRawHostCpuLoad();
+    }
+    return VINF_SUCCESS;
+}
+
+int CollectorLinux::_getRawHostCpuLoad()
+{
+    int rc = VINF_SUCCESS;
+    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)
-        {
-            *user   = (uint64_t)u32user + u32nice;
-            *kernel = u32kernel;
-            *idle   = u32idle;
+        char szBuf[128];
+        if (fgets(szBuf, sizeof(szBuf), f))
+        {
+            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
@@ -161,4 +214,22 @@
 
     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;
 }
 
@@ -252,4 +323,67 @@
 }
 
-}
-
+int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms)
+{
+    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;
+
+    return rc;
+}
+
+static char *getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName)
+{
+    unsigned cbName = 0;
+    unsigned cbDevName = strlen(pszDevName);
+    const char *pszEnd = pszDevName + cbDevName - 1;
+    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;
+}
+
+
+int 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[32];
+                listDisks.push_back(RTCString(getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname)));
+                break;
+            }
+        }
+        endmntent(mtab);
+    }
+    return VINF_SUCCESS;
+}
+
+}
+
Index: /trunk/src/VBox/Main/src-server/os2/PerformanceOs2.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/os2/PerformanceOs2.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/os2/PerformanceOs2.cpp	(revision 43629)
@@ -63,3 +63,8 @@
 }
 
+int getDiskListByFs(const char *name, DiskList& list)
+{
+    return VERR_NOT_IMPLEMENTED;
 }
+
+}
Index: /trunk/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp	(revision 43629)
@@ -329,3 +329,8 @@
 }
 
-}
+int getDiskListByFs(const char *name, DiskList& list)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+}
Index: /trunk/src/VBox/Main/src-server/win/PerformanceWin.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/win/PerformanceWin.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/src-server/win/PerformanceWin.cpp	(revision 43629)
@@ -350,3 +350,8 @@
 }
 
-}
+int getDiskListByFs(const char *name, DiskList& list)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+}
Index: /trunk/src/VBox/Main/testcase/tstCollector.cpp
===================================================================
--- /trunk/src/VBox/Main/testcase/tstCollector.cpp	(revision 43628)
+++ /trunk/src/VBox/Main/testcase/tstCollector.cpp	(revision 43629)
@@ -153,5 +153,5 @@
     uint64_t hostRxStop, hostTxStop, speed = 125000000; /* Assume 1Gbit/s */
 
-    RTPrintf("\ntstCollector: TESTING - Network load, sleeping for 5 sec...\n");
+    RTPrintf("tstCollector: TESTING - Network load, sleeping for 5 sec...\n");
 
     int rc = collector->preCollect(hints, 0);
@@ -184,13 +184,94 @@
     RTPrintf("tstCollector: host network speed = %llu bytes/sec (%llu mbit/sec)\n",
              speed, speed/(1000000/8));
-    RTPrintf("tstCollector: host network rx    = %llu bytes/sec (%llu mbit/sec, %d %%*100)\n",
+    RTPrintf("tstCollector: host network rx    = %llu bytes/sec (%llu mbit/sec, %u.%u %%)\n",
              (hostRxStop - hostRxStart)/5, (hostRxStop - hostRxStart)/(5000000/8),
-             (hostRxStop - hostRxStart) * 10000 / (speed * 5));
-    RTPrintf("tstCollector: host network tx    = %llu bytes/sec (%llu mbit/sec, %d %%*100)\n",
+             (hostRxStop - hostRxStart) * 100 / (speed * 5),
+             (hostRxStop - hostRxStart) * 10000 / (speed * 5) % 100);
+    RTPrintf("tstCollector: host network tx    = %llu bytes/sec (%llu mbit/sec, %u.%u %%)\n\n",
              (hostTxStop - hostTxStart)/5, (hostTxStop - hostTxStart)/(5000000/8),
-             (hostTxStop - hostTxStart) * 10000 / (speed * 5));
+             (hostTxStop - hostTxStart) * 100 / (speed * 5),
+             (hostTxStop - hostTxStart) * 10000 / (speed * 5) % 100);
 
     return 0;
 }
+
+#define FSNAME "/"
+int testFsUsage(pm::CollectorHAL *collector)
+{
+    RTPrintf("tstCollector: TESTING - File system usage\n");
+
+    ULONG total, used, available;
+
+    int rc = collector->getHostFilesystemUsage(FSNAME, &total, &used, &available);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstCollector: getHostFilesystemUsage() -> %Rrc\n", rc);
+        return 1;
+    }
+    RTPrintf("tstCollector: host root fs total     = %lu mB\n", total);
+    RTPrintf("tstCollector: host root fs used      = %lu mB\n", used);
+    RTPrintf("tstCollector: host root fs available = %lu mB\n\n", available);
+    return 0;
+}
+
+int testDisk(pm::CollectorHAL *collector)
+{
+    pm::CollectorHints hints;
+    uint64_t diskMsStart, totalMsStart;
+    uint64_t diskMsStop, totalMsStop;
+
+    std::list<RTCString> disks;
+    int rc = pm::getDiskListByFs(FSNAME, disks);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstCollector: getDiskListByFs(%s) -> %Rrc\n", FSNAME, rc);
+        return 1;
+    }
+    if (disks.empty())
+    {
+        RTPrintf("tstCollector: getDiskListByFs(%s) returned empty list\n", FSNAME);
+        return 1;
+    }
+
+    RTPrintf("tstCollector: TESTING - Disk utilization, sleeping for 5 sec...\n");
+
+    hints.collectHostCpuLoad();
+    rc = collector->preCollect(hints, 0);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+        return 1;
+    }
+    rc = collector->getRawHostDiskLoad(disks.front().c_str(), &diskMsStart, &totalMsStart);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstCollector: getRawHostNetworkLoad() -> %Rrc\n", rc);
+        return 1;
+    }
+
+    RTThreadSleep(5000); // Sleep for five seconds
+
+    rc = collector->preCollect(hints, 0);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+        return 1;
+    }
+    rc = collector->getRawHostDiskLoad(disks.front().c_str(), &diskMsStop, &totalMsStop);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstCollector: getRawHostNetworkLoad() -> %Rrc\n", rc);
+        return 1;
+    }
+    RTPrintf("tstCollector: host disk util    = %llu msec (%u.%u %%), total = %llu msec\n\n",
+             (diskMsStop - diskMsStart),
+             (unsigned)((diskMsStop - diskMsStart) * 100 / (totalMsStop - totalMsStart)),
+             (unsigned)((diskMsStop - diskMsStart) * 10000 / (totalMsStop - totalMsStart) % 100),
+             totalMsStop - totalMsStart);
+
+    return 0;
+}
+
+
 
 int main(int argc, char *argv[])
@@ -296,9 +377,19 @@
     printf("tstCollector: host cpu idle      = %f sec\n", (hostIdleStop - hostIdleStart) / 10000000.);
     printf("tstCollector: host cpu total     = %f sec\n", hostTotal / 10000000.);*/
-    RTPrintf("tstCollector: host cpu user      = %llu %%\n", (hostUserStop - hostUserStart) * 100 / hostTotal);
-    RTPrintf("tstCollector: host cpu kernel    = %llu %%\n", (hostKernelStop - hostKernelStart) * 100 / hostTotal);
-    RTPrintf("tstCollector: host cpu idle      = %llu %%\n", (hostIdleStop - hostIdleStart) * 100 / hostTotal);
-    RTPrintf("tstCollector: process cpu user   = %llu %%\n", (processUserStop - processUserStart) * 100 / (processTotalStop - processTotalStart));
-    RTPrintf("tstCollector: process cpu kernel = %llu %%\n\n", (processKernelStop - processKernelStart) * 100 / (processTotalStop - processTotalStart));
+    RTPrintf("tstCollector: host cpu user      = %u.%u %%\n",
+             (unsigned)((hostUserStop - hostUserStart) * 100 / hostTotal),
+             (unsigned)((hostUserStop - hostUserStart) * 10000 / hostTotal % 100));
+    RTPrintf("tstCollector: host cpu kernel    = %u.%u %%\n",
+             (unsigned)((hostKernelStop - hostKernelStart) * 100 / hostTotal),
+             (unsigned)((hostKernelStop - hostKernelStart) * 10000 / hostTotal % 100));
+    RTPrintf("tstCollector: host cpu idle      = %u.%u %%\n",
+             (unsigned)((hostIdleStop - hostIdleStart) * 100 / hostTotal),
+             (unsigned)((hostIdleStop - hostIdleStart) * 10000 / hostTotal % 100));
+    RTPrintf("tstCollector: process cpu user   = %u.%u %%\n",
+             (unsigned)((processUserStop - processUserStart) * 100 / (processTotalStop - processTotalStart)),
+             (unsigned)((processUserStop - processUserStart) * 10000 / (processTotalStop - processTotalStart) % 100));
+    RTPrintf("tstCollector: process cpu kernel = %u.%u %%\n\n",
+             (unsigned)((processKernelStop - processKernelStart) * 100 / (processTotalStop - processTotalStart)),
+             (unsigned)((processKernelStop - processKernelStart) * 10000 / (processTotalStop - processTotalStart) % 100));
 
     RTPrintf("tstCollector: TESTING - CPU load, looping for 5 sec\n");
@@ -345,9 +436,19 @@
         + hostKernelStop - hostKernelStart
         + hostIdleStop - hostIdleStart;
-    RTPrintf("tstCollector: host cpu user      = %llu %%\n", (hostUserStop - hostUserStart) * 100 / hostTotal);
-    RTPrintf("tstCollector: host cpu kernel    = %llu %%\n", (hostKernelStop - hostKernelStart) * 100 / hostTotal);
-    RTPrintf("tstCollector: host cpu idle      = %llu %%\n", (hostIdleStop - hostIdleStart) * 100 / hostTotal);
-    RTPrintf("tstCollector: process cpu user   = %llu %%\n", (processUserStop - processUserStart) * 100 / (processTotalStop - processTotalStart));
-    RTPrintf("tstCollector: process cpu kernel = %llu %%\n\n", (processKernelStop - processKernelStart) * 100 / (processTotalStop - processTotalStart));
+    RTPrintf("tstCollector: host cpu user      = %u.%u %%\n",
+             (unsigned)((hostUserStop - hostUserStart) * 100 / hostTotal),
+             (unsigned)((hostUserStop - hostUserStart) * 10000 / hostTotal % 100));
+    RTPrintf("tstCollector: host cpu kernel    = %u.%u %%\n",
+             (unsigned)((hostKernelStop - hostKernelStart) * 100 / hostTotal),
+             (unsigned)((hostKernelStop - hostKernelStart) * 10000 / hostTotal % 100));
+    RTPrintf("tstCollector: host cpu idle      = %u.%u %%\n",
+             (unsigned)((hostIdleStop - hostIdleStart) * 100 / hostTotal),
+             (unsigned)((hostIdleStop - hostIdleStart) * 10000 / hostTotal % 100));
+    RTPrintf("tstCollector: process cpu user   = %u.%u %%\n",
+             (unsigned)((processUserStop - processUserStart) * 100 / (processTotalStop - processTotalStart)),
+             (unsigned)((processUserStop - processUserStart) * 10000 / (processTotalStop - processTotalStart) % 100));
+    RTPrintf("tstCollector: process cpu kernel = %u.%u %%\n\n",
+             (unsigned)((processKernelStop - processKernelStart) * 100 / (processTotalStop - processTotalStart)),
+             (unsigned)((processKernelStop - processKernelStart) * 10000 / (processTotalStop - processTotalStart) % 100));
 
     RTPrintf("tstCollector: TESTING - Memory usage\n");
@@ -370,11 +471,17 @@
     RTPrintf("tstCollector: host mem used      = %lu kB\n", used);
     RTPrintf("tstCollector: host mem available = %lu kB\n", available);
-    RTPrintf("tstCollector: process mem used   = %lu kB\n", processUsed);
+    RTPrintf("tstCollector: process mem used   = %lu kB\n\n", processUsed);
 #endif
 #if 1
     rc = testNetwork(collector);
 #endif
-#if 0
-    RTPrintf("\ntstCollector: TESTING - Performance\n\n");
+#if 1
+    rc = testFsUsage(collector);
+#endif
+#if 1
+    rc = testDisk(collector);
+#endif
+#if 1
+    RTPrintf("tstCollector: TESTING - Performance\n\n");
 
     measurePerformance(collector, argv[0], 100);
