]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Add new guest metrics to be consumed by vROps.
authorOliver Kurth <okurth@vmware.com>
Tue, 24 Apr 2018 00:08:18 +0000 (17:08 -0700)
committerOliver Kurth <okurth@vmware.com>
Tue, 24 Apr 2018 00:08:18 +0000 (17:08 -0700)
Windows:
* ProcessorQueueLength
* CurrentDiskQueueLength
* AvgDiskQueueLength

Linux:
* CPU run queue size
* Current disk queue size
* Average disk queue size

open-vm-tools/lib/include/guestStats.h
open-vm-tools/services/plugins/guestInfo/guestInfoServer.c
open-vm-tools/services/plugins/guestInfo/perfMonLinux.c

index d5af939cf29d188261c753354b535a2fdffd5d29..c59b97c884e139d6cf4955f1f76d10c5b271859c 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 2008-2018 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -33,6 +33,8 @@
 #include "vm_assert.h"
 #include "vm_basic_types.h"
 
+#define PUBLISH_EXPERIMENTAL_STATS   0
+
 /*
  * Version 1: Legacy data
  * Version 2: Dead
@@ -244,7 +246,7 @@ typedef enum {
 
 /*
  * Defined stat IDs for guest tools builtin query.
- * See vmx/vigorapi/guestStats.java for documentation
+ * See vmx/vigorapi/GuestStats.java for documentation
  *
  * NOTE: These IDs are relative to GUEST_TOOLS_NAMESPACE
  * NOTE: DO NOT re-order or remove the IDs. IDs can only be added to the end,
@@ -252,7 +254,7 @@ typedef enum {
  *       of bumping the namespace version.
  */
 #define GUEST_STAT_TOOLS_IDS \
-   /* 2015u1 stats */ \
+   /* 6.0u1 stats */ \
    DEFINE_GUEST_STAT(GuestStatID_Invalid,                         0,  "__INVALID__") \
    DEFINE_GUEST_STAT(GuestStatID_None,                            1,  "__NONE__") \
    DEFINE_GUEST_STAT(GuestStatID_ContextSwapRate,                 2,  "guest.contextSwapRate") \
@@ -266,7 +268,7 @@ typedef enum {
    DEFINE_GUEST_STAT(GuestStatID_PhysicalPageSize,                10, "guest.page.size") \
    DEFINE_GUEST_STAT(GuestStatID_HugePageSize,                    11, "guest.hugePage.size") \
    DEFINE_GUEST_STAT(GuestStatID_Linux_HugePagesTotal,            12, "guest.hugePage.total") \
-   /* 2016 stats */ \
+   /* 6.5 stats */ \
    DEFINE_GUEST_STAT(GuestStatID_MemNeededReservation,            13, "guest.mem.neededReservation") \
    DEFINE_GUEST_STAT(GuestStatID_PageSwapInRate,                  14, "guest.swap.pageInRate") \
    DEFINE_GUEST_STAT(GuestStatID_PageSwapOutRate,                 15, "guest.swap.pageOutRate") \
@@ -315,7 +317,14 @@ typedef enum {
    DEFINE_GUEST_STAT(GuestStatID_Windows_DiskWriteRate,           58, "guest.disk.writeRate") \
    DEFINE_GUEST_STAT(GuestStatID_Windows_AutomaticSwapFileMax,    59, "guest.swap.automaticFileMax") \
    DEFINE_GUEST_STAT(GuestStatID_Linux_MemTotal,                  60, "guest.mem.total") \
-   DEFINE_GUEST_STAT(GuestStatID_Max,                             61, "__MAX__")
+   /* (6.7, ] stats */ \
+   DEFINE_GUEST_STAT(GuestStatID_Linux_CpuRunQueue,               61, "guest.cpu.runQueue") \
+   DEFINE_GUEST_STAT(GuestStatID_Linux_DiskRequestQueue,          62, "guest.disk.requestQueue") \
+   DEFINE_GUEST_STAT(GuestStatID_Linux_DiskRequestQueueAvg,       63, "guest.disk.requestQueueAvg") \
+   DEFINE_GUEST_STAT(GuestStatID_Windows_ProcessorQueue,          64, "guest.processor.queue") \
+   DEFINE_GUEST_STAT(GuestStatID_Windows_DiskQueue,               65, "guest.disk.queue") \
+   DEFINE_GUEST_STAT(GuestStatID_Windows_DiskQueueAvg,            66, "guest.disk.queueAvg") \
+   DEFINE_GUEST_STAT(GuestStatID_Max,                             67, "__MAX__")
 
 /*
  * Define stats enumeration
index fe55037a0c7bb317b72a1036f22223d430d32a90..731b9b008d39eb51553b445522078517ce4c4275 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 1998-2018 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -1700,7 +1700,7 @@ GuestInfoServerShutdown(gpointer src,
       gatherStatsTimeoutSource = NULL;
    }
 
-#if !defined(__APPLE__)
+#if defined(__linux__) || defined(USERWORLD) || defined(_WIN32)
    GuestInfo_StatProviderShutdown();
 #endif
 
index d5ea9513657e68f1be70ff8ae0297fbc3e06e528..e6c551e4298192a5f827f722188aa5fccb20cad2 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 2008-2018 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
 
 #include "vm_basic_defs.h"
 #include "vmware.h"
+#include "str.h"
 #include "strutil.h"
 #include "debug.h"
 #include "guestInfoInt.h"
 #include "guestStats.h"
 #include "posix.h"
 #include "hashTable.h"
+#include "conf.h"
 
 #define GUEST_INFO_PREALLOC_SIZE 4096
 #define INT_AS_HASHKEY(x) ((const void *)(uintptr_t)(x))
+#define KEY_FORMAT  "%s|%s"
 
 #define STAT_FILE        "/proc/stat"
 #define VMSTAT_FILE      "/proc/vmstat"
 #define MEMINFO_FILE     "/proc/meminfo"
 #define ZONEINFO_FILE    "/proc/zoneinfo"
 #define SWAPPINESS_FILE  "/proc/sys/vm/swappiness"
+#define DISKSTATS_FILE   "/proc/diskstats"
 
-#define PUBLISH_P1_2015_STATS         1
-#define PUBLISH_P1_2016_STATS         1
-#define PUBLISH_P2_STATS              1
+#define SYSFS_BLOCK_FOLDER  "/sys/block"
 
 /*
  * For now, all data collection is of uint64 values. Rates are always returned
  * as a double, derived from the uint64 data.
- *
- * TODO: Deal with collected and reported data types being different.
  */
 
-#define STAT_FLAG(x) PUBLISH_##x##_STATS
-#define DECLARE_STAT(collect, publish, file, isRegExp, locatorString, reportID, units, dataType) \
-   { file, STAT_FLAG(collect), STAT_FLAG(publish), isRegExp, locatorString, reportID, units, dataType }
+static Bool gReleased = TRUE;
+static Bool gInternal = FALSE;
+#if PUBLISH_EXPERIMENTAL_STATS
+static Bool gExperimental = PUBLISH_EXPERIMENTAL_STATS;
+#endif
+static Bool gUnstable = FALSE;
+
+#define DECLARE_STAT(publish, file, isRegExp, locatorString, reportID, units, dataType) \
+   { file, publish, isRegExp, locatorString, reportID, units, dataType }
 
 typedef struct {
    const char         *sourceFile;
-   Bool                collect;
-   Bool                publish;
+   const Bool         *publish;
    Bool                isRegExp;
    const char         *locatorString;
    GuestStatToolsID    reportID;
@@ -69,54 +74,63 @@ typedef struct {
 } GuestInfoQuery;
 
 GuestInfoQuery guestInfoQuerySpecTable[] = {
-   DECLARE_STAT(P1_2015, P1_2015, MEMINFO_FILE, FALSE, "Hugepagesize",    GuestStatID_HugePageSize,             GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2015, ZONEINFO_FILE,TRUE,  "present",         GuestStatID_MemPhysUsable,            GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2015, MEMINFO_FILE, FALSE, "MemFree",         GuestStatID_MemFree,                  GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2015, MEMINFO_FILE, FALSE, "Active(file)",    GuestStatID_MemActiveFileCache,       GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2015, MEMINFO_FILE, FALSE, "SwapFree",        GuestStatID_SwapSpaceRemaining,       GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2015, MEMINFO_FILE, FALSE, "HugePages_Total", GuestStatID_Linux_HugePagesTotal,     GuestUnitsHugePages, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2015, VMSTAT_FILE,  FALSE, "pgpgin",          GuestStatID_PageInRate,               GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P1_2015, P1_2015, VMSTAT_FILE,  FALSE, "pgpgout",         GuestStatID_PageOutRate,              GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P1_2015, P1_2015, STAT_FILE,    FALSE, "ctxt",            GuestStatID_ContextSwapRate,          GuestUnitsNumberPerSecond, GuestTypeDouble),
-   DECLARE_STAT(P1_2015, P1_2015, NULL,         FALSE, NULL,              GuestStatID_PhysicalPageSize,         GuestUnitsBytes, GuestTypeUint64),
-
-   DECLARE_STAT(P1_2015, P1_2016, MEMINFO_FILE, FALSE, "MemAvailable",    GuestStatID_Linux_MemAvailable,       GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2016, MEMINFO_FILE, FALSE, "Inactive(file)",  GuestStatID_Linux_MemInactiveFile,    GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2016, MEMINFO_FILE, FALSE, "SReclaimable",    GuestStatID_Linux_MemSlabReclaim,     GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2016, MEMINFO_FILE, FALSE, "Buffers",         GuestStatID_Linux_MemBuffers,         GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2016, MEMINFO_FILE, FALSE, "Cached",          GuestStatID_Linux_MemCached,          GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P1_2016, NULL,         FALSE, NULL,              GuestStatID_SwapSpaceUsed,            GuestUnitsKiB, GuestTypeUint64),
-
-   DECLARE_STAT(P1_2015, P2,      MEMINFO_FILE, FALSE, "MemTotal",        GuestStatID_Linux_MemTotal,           GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P2,      MEMINFO_FILE,  FALSE, "SwapTotal",      GuestStatID_SwapFilesCurrent,         GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P2,      NULL,          FALSE,  NULL,            GuestStatID_SwapFilesMax,             GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2015, P2,      ZONEINFO_FILE, TRUE,  "low",            GuestStatID_Linux_LowWaterMark,       GuestUnitsPages, GuestTypeUint64),
-
-#if PUBLISH_P1_2016_STATS
-   DECLARE_STAT(P1_2016, P1_2016, MEMINFO_FILE, FALSE, "Active(anon)",    GuestStatID_Linux_MemActiveAnon,      GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2016, P1_2016, MEMINFO_FILE, FALSE, "Inactive(anon)",  GuestStatID_Linux_MemInactiveAnon,    GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2016, P1_2016, MEMINFO_FILE, FALSE, "Inactive",        GuestStatID_Linux_MemInactive,        GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2016, P1_2016, MEMINFO_FILE, FALSE, "Active",          GuestStatID_Linux_MemActive,          GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2016, P1_2016, MEMINFO_FILE, FALSE, "Unevictable",     GuestStatID_Linux_MemPinned,          GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2016, P1_2016, MEMINFO_FILE, FALSE, "Dirty",           GuestStatID_Linux_MemDirty,           GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P1_2016, P1_2016, VMSTAT_FILE,  FALSE, "pswpin",          GuestStatID_PageSwapInRate,           GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P1_2016, P1_2016, VMSTAT_FILE,  FALSE, "pswpout",         GuestStatID_PageSwapOutRate,          GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P1_2016, P1_2016, NULL,         FALSE, NULL,              GuestStatID_ThreadCreationRate,       GuestUnitsNumberPerSecond, GuestTypeDouble),
-   DECLARE_STAT(P1_2016, P1_2016, SWAPPINESS_FILE, FALSE, NULL,           GuestStatID_Linux_Swappiness,         GuestUnitsPercent, GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "Hugepagesize",    GuestStatID_HugePageSize,              GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  ZONEINFO_FILE,   TRUE,  "present",         GuestStatID_MemPhysUsable,             GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "MemFree",         GuestStatID_MemFree,                   GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "Active(file)",    GuestStatID_MemActiveFileCache,        GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "SwapFree",        GuestStatID_SwapSpaceRemaining,        GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "HugePages_Total", GuestStatID_Linux_HugePagesTotal,      GuestUnitsHugePages,       GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  VMSTAT_FILE,     FALSE, "pgpgin",          GuestStatID_PageInRate,                GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gReleased,  VMSTAT_FILE,     FALSE, "pgpgout",         GuestStatID_PageOutRate,               GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gReleased,  STAT_FILE,       FALSE, "ctxt",            GuestStatID_ContextSwapRate,           GuestUnitsNumberPerSecond, GuestTypeDouble),
+   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_PhysicalPageSize,          GuestUnitsBytes,           GuestTypeUint64),
+   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_MemNeeded,                 GuestUnitsKiB,             GuestTypeUint64),
+
+   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_MemNeededReservation,      GuestUnitsKiB,             GuestTypeUint64),
+
+   DECLARE_STAT(&gInternal,  MEMINFO_FILE,    FALSE, "MemAvailable",    GuestStatID_Linux_MemAvailable,        GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gInternal,  MEMINFO_FILE,    FALSE, "Inactive(file)",  GuestStatID_Linux_MemInactiveFile,     GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gInternal,  MEMINFO_FILE,    FALSE, "SReclaimable",    GuestStatID_Linux_MemSlabReclaim,      GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gInternal,  MEMINFO_FILE,    FALSE, "Buffers",         GuestStatID_Linux_MemBuffers,          GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gInternal,  MEMINFO_FILE,    FALSE, "Cached",          GuestStatID_Linux_MemCached,           GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gInternal,  ZONEINFO_FILE,   TRUE,  "low",             GuestStatID_Linux_LowWaterMark,        GuestUnitsPages,           GuestTypeUint64),
+   DECLARE_STAT(&gInternal,  MEMINFO_FILE,    FALSE, "MemTotal",        GuestStatID_Linux_MemTotal,            GuestUnitsKiB,             GuestTypeUint64),
+
+#if PUBLISH_EXPERIMENTAL_STATS
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "SwapTotal",       GuestStatID_SwapFilesCurrent,          GuestUnitsKiB,             GuestTypeUint64),
+   /* GuestStatID_SwapSpaceUsed depends on GuestStatID_SwapFilesCurrent */
+   DECLARE_STAT(&gExperimental,  NULL,            FALSE, NULL,              GuestStatID_SwapSpaceUsed,             GuestUnitsKiB,             GuestTypeUint64),
+   /* GuestStatID_SwapFilesMax depends on GuestStatID_SwapFilesCurrent */
+   DECLARE_STAT(&gExperimental,  NULL,            FALSE, NULL,              GuestStatID_SwapFilesMax,              GuestUnitsKiB,             GuestTypeUint64),
+
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Active(anon)",    GuestStatID_Linux_MemActiveAnon,       GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Inactive(anon)",  GuestStatID_Linux_MemInactiveAnon,     GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Inactive",        GuestStatID_Linux_MemInactive,         GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Active",          GuestStatID_Linux_MemActive,           GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Unevictable",     GuestStatID_Linux_MemPinned,           GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Dirty",           GuestStatID_Linux_MemDirty,            GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pswpin",          GuestStatID_PageSwapInRate,            GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pswpout",         GuestStatID_PageSwapOutRate,           GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   /* Not implemented
+   DECLARE_STAT(&gExperimental,  NULL,            FALSE, NULL,              GuestStatID_ThreadCreationRate,        GuestUnitsNumberPerSecond, GuestTypeDouble),
+    */
+   DECLARE_STAT(&gExperimental,  SWAPPINESS_FILE, FALSE, NULL,              GuestStatID_Linux_Swappiness,          GuestUnitsPercent,         GuestTypeUint64),
+
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "SwapCached",      GuestStatID_Linux_MemSwapCached,       GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Committed_AS",    GuestStatID_Linux_MemCommitted,        GuestUnitsKiB,             GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "HugePages_Free",  GuestStatID_Linux_HugePagesFree,       GuestUnitsHugePages,       GuestTypeUint64),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pgfault",         GuestStatID_Linux_PageFaultRate,       GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pgmajfault",      GuestStatID_Linux_PageMajorFaultRate,  GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pgfree",          GuestStatID_Linux_PageFreeRate,        GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     TRUE,  "pgsteal_",        GuestStatID_Linux_PageStealRate,       GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     TRUE,  "pgscan_kswapd_",  GuestStatID_Linux_PageSwapScanRate,    GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     TRUE,  "pgscan_direct_",  GuestStatID_Linux_PageDirectScanRate,  GuestUnitsPagesPerSecond,  GuestTypeDouble),
+   DECLARE_STAT(&gExperimental,  STAT_FILE,       FALSE, "processes",       GuestStatID_ProcessCreationRate,       GuestUnitsNumberPerSecond, GuestTypeDouble),
 #endif
 
-#if PUBLISH_P2_STATS
-   DECLARE_STAT(P2,      P2,      MEMINFO_FILE, FALSE, "SwapCached",      GuestStatID_Linux_MemSwapCached,      GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P2,      P2,      MEMINFO_FILE, FALSE, "Committed_AS",    GuestStatID_Linux_MemCommitted,       GuestUnitsKiB, GuestTypeUint64),
-   DECLARE_STAT(P2,      P2,      MEMINFO_FILE, FALSE, "HugePages_Free",  GuestStatID_Linux_HugePagesFree,      GuestUnitsHugePages, GuestTypeUint64),
-   DECLARE_STAT(P2,      P2,      VMSTAT_FILE,  FALSE, "pgfault",         GuestStatID_Linux_PageFaultRate,      GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P2,      P2,      VMSTAT_FILE,  FALSE, "pgmajfault",      GuestStatID_Linux_PageMajorFaultRate, GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P2,      P2,      VMSTAT_FILE,  FALSE, "pgfree",          GuestStatID_Linux_PageFreeRate,       GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P2,      P2,      VMSTAT_FILE,  TRUE,  "pgsteal_",        GuestStatID_Linux_PageStealRate,      GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P2,      P2,      VMSTAT_FILE,  TRUE,  "pgscan_kswapd_",  GuestStatID_Linux_PageSwapScanRate,   GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P2,      P2,      VMSTAT_FILE,  TRUE,  "pgscan_direct_",  GuestStatID_Linux_PageDirectScanRate, GuestUnitsPagesPerSecond,  GuestTypeDouble),
-   DECLARE_STAT(P2,      P2,      STAT_FILE,    FALSE, "processes",       GuestStatID_ProcessCreationRate,      GuestUnitsNumberPerSecond, GuestTypeDouble),
-#endif
+   DECLARE_STAT(&gUnstable,  STAT_FILE,       FALSE, "procs_running",   GuestStatID_Linux_CpuRunQueue,         GuestUnitsNumber,          GuestTypeUint64),
+   DECLARE_STAT(&gUnstable,  NULL,            FALSE, NULL,              GuestStatID_Linux_DiskRequestQueue,    GuestUnitsNumber,          GuestTypeUint64),
+   DECLARE_STAT(&gUnstable,  NULL,            FALSE, NULL,              GuestStatID_Linux_DiskRequestQueueAvg, GuestUnitsNumber,          GuestTypeDouble),
 };
 
 #define N_QUERIES (sizeof guestInfoQuerySpecTable / sizeof(GuestInfoQuery))
@@ -143,6 +157,78 @@ typedef struct {
    double           timeStamp;
 } GuestInfoCollector;
 
+static GuestInfoCollector *gCurrentCollector = NULL;
+static GuestInfoCollector *gPreviousCollector = NULL;
+
+static void
+GuestInfoDeriveMemNeeded(GuestInfoCollector *collector);
+
+typedef struct GuestInfoDiskStatsList {
+   struct GuestInfoDiskStatsList *next;
+   char                          *diskName;
+   unsigned int                   weightedTime[2];  // In milliseconds
+} GuestInfoDiskStatsList;
+
+static GuestInfoDiskStatsList *gDiskStatsList = NULL;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GuestInfoDeleteDiskStatsList --
+ *
+ *      Delete disk device stats list.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GuestInfoDeleteDiskStatsList(GuestInfoDiskStatsList *head)  // IN/OUT:
+{
+   GuestInfoDiskStatsList *curr = head;
+
+   while (curr != NULL) {
+      GuestInfoDiskStatsList *next = curr->next;
+      free(curr->diskName);
+      free(curr);
+      curr = next;
+   }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GuestInfoIsBlockDevice --
+ *
+ *      Verify block device name.
+ *
+ * Results:
+ *      TRUE   name is block device name
+ *      FALSE  otherwise
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Bool
+GuestInfoIsBlockDevice(const char *name)  // IN:
+{
+   char path[PATH_MAX]; // PATH_MAX is defined to 4096
+
+   Str_Sprintf(path, sizeof path, "%s/%s", SYSFS_BLOCK_FOLDER, name);
+
+   return (access(path, F_OK) == 0);
+}
+
 
 /*
  *----------------------------------------------------------------------
@@ -172,7 +258,7 @@ GuestInfoGetUpTime(double *now)  // OUT:
       return result;
    }
 
-   if (fgets(line, sizeof line, fp) != NULL) {
+   if (fgets(line, sizeof line, fp) == line) {
       double idle;
 
       if (sscanf(line, "%lf %lf", now, &idle) == 2) {
@@ -203,39 +289,35 @@ GuestInfoGetUpTime(double *now)  // OUT:
  */
 
 static void
-GuestInfoStoreStat(const char *pathName,  // IN: file stat is in
-                   GuestInfoStat *stat,   // IN/OUT: stat
+GuestInfoStoreStat(GuestInfoStat *stat,   // IN/OUT: stat
                    uint64 value)          // IN: value to be added to stat
 {
    ASSERT(stat);
-   ASSERT(stat->query);
 
    // TODO: consider supporting regexp here.
-   if (strcmp(stat->query->sourceFile, pathName) == 0) {
-      switch (stat->err) {
-      case 0:
-         ASSERT(stat->count != 0);
-
-         if (((stat->count + 1) < stat->count) ||
-             ((stat->value + value) < stat->value)) {
-            stat->err = EOVERFLOW;
-         } else {
-            stat->count++;
-            stat->value += value;
-         }
-         break;
+   switch (stat->err) {
+   case 0:
+      ASSERT(stat->count != 0);
 
-      case ENOENT:
-         ASSERT(stat->count == 0);
+      if (((stat->count + 1) < stat->count) ||
+          ((stat->value + value) < stat->value)) {
+         stat->err = EOVERFLOW;
+      } else {
+         stat->count++;
+         stat->value += value;
+      }
+      break;
 
-         stat->err = 0;
-         stat->count = 1;
-         stat->value = value;
-         break;
+   case ENOENT:
+      ASSERT(stat->count == 0);
 
-      default:  // Some sort of error - sorry, thank you for playing...
-         break;
-      }
+      stat->err = 0;
+      stat->count = 1;
+      stat->value = value;
+      break;
+
+   default:  // Some sort of error - sorry, thank you for playing...
+      break;
    }
 }
 
@@ -243,13 +325,9 @@ GuestInfoStoreStat(const char *pathName,  // IN: file stat is in
 /*
  *----------------------------------------------------------------------
  *
- * GuestInfoCollectStat --
- *
- *      Collect a stat.
+ * GuestInfoStoreStatByID --
  *
- *      NOTE: Exact match data cannot be used in a regExp. This is a
- *            performance choice. We can discuss this when we have full
- *            programmability.
+ *      Store a stat value by its ID.
  *
  * Results:
  *      None.
@@ -261,41 +339,32 @@ GuestInfoStoreStat(const char *pathName,  // IN: file stat is in
  */
 
 static void
-GuestInfoCollectStat(const char *pathName,           // IN:
-                     GuestInfoCollector *collector,  // IN/OUT:
-                     const char *fieldName,          // IN:
-                     uint64 value)                   // IN:
+GuestInfoStoreStatByID(GuestStatToolsID reportID,      // IN:
+                       GuestInfoCollector *collector,  // IN/OUT:
+                       uint64 value)                   // IN:
 {
    GuestInfoStat *stat = NULL;
 
-   if (!HashTable_Lookup(collector->exactMatches, fieldName, (void **) &stat)) {
-      uint32 i;
-
-      for (i = 0; i < collector->numRegExps; i++) {
-         GuestInfoStat *thisOne = collector->regExps[i];
-
-         if (StrUtil_StartsWith(fieldName, thisOne->query->locatorString)) {
-            stat = thisOne;
-         }
-      }
-   }
+   HashTable_Lookup(collector->reportMap,
+                    INT_AS_HASHKEY(reportID),
+                    (void **) &stat);
 
-   if (stat != NULL) {
-      GuestInfoStoreStat(pathName, stat, value);
-   }
+   GuestInfoStoreStat(stat, value);
 }
 
 
 /*
  *----------------------------------------------------------------------
  *
- * GuestInfoReadProcMemInfoData --
+ * GuestInfoCollectStat --
  *
- *      Reads /proc/meminfo to contribute to a collection.
+ *      Collect a stat.
+ *
+ *      NOTE: Exact match data cannot be used in a regExp. This is a
+ *            performance choice.
  *
  * Results:
- *      TRUE   Success!
- *      FALSE  Failure!
+ *      None.
  *
  * Side effects:
  *      None.
@@ -303,45 +372,34 @@ GuestInfoCollectStat(const char *pathName,           // IN:
  *----------------------------------------------------------------------
  */
 
-static Bool
-GuestInfoProcMemInfoData(GuestInfoCollector *collector)  // IN:
+static void
+GuestInfoCollectStat(const char *pathName,           // IN:
+                     GuestInfoCollector *collector,  // IN/OUT:
+                     const char *fieldName,          // IN:
+                     uint64 value)                   // IN:
 {
-   char line[512];
-   FILE *fp = Posix_Fopen(MEMINFO_FILE, "r");
-
-   if (fp == NULL) {
-      g_warning("%s: Error opening " MEMINFO_FILE ".\n", __FUNCTION__);
-      return FALSE;
-   }
-
-   while (fgets(line, sizeof line, fp) == line) {
-      char *p;
-      uint64 value = 0;
-      char *fieldName = strtok(line, " \t");
-      char *fieldData = strtok(NULL, " \t");
+   GuestInfoStat *stat = NULL;
+   char *key = Str_SafeAsprintf(NULL, KEY_FORMAT, pathName, fieldName);
 
-      if (fieldName == NULL) {
-         continue;
-      }
+   if (!HashTable_Lookup(collector->exactMatches, key, (void **) &stat)) {
+      uint32 i;
 
-      p = strrchr(fieldName, ':');
-      if (p == NULL) {
-         continue;
-      } else {
-        *p = '\0';
-      }
+      for (i = 0; i < collector->numRegExps; i++) {
+         GuestInfoStat *thisOne = collector->regExps[i];
 
-      if ((fieldData == NULL) ||
-          (sscanf(fieldData, "%"FMT64"u", &value) != 1)) {
-         continue;
+         if (strcmp(pathName, thisOne->query->sourceFile) == 0 &&
+             StrUtil_StartsWith(fieldName, thisOne->query->locatorString)) {
+            stat = thisOne;
+            break;
+         }
       }
-
-      GuestInfoCollectStat(MEMINFO_FILE, collector, fieldName, value);
    }
 
-   fclose(fp);
+   free(key);
 
-   return TRUE;
+   if (stat != NULL) {
+      GuestInfoStoreStat(stat, value);
+   }
 }
 
 
@@ -350,7 +408,11 @@ GuestInfoProcMemInfoData(GuestInfoCollector *collector)  // IN:
  *
  * GuestInfoProcData --
  *
- *      Reads a "stat file" and contribute to the collection.
+ *      Reads a "stat file" and contributes to the collection.
+ *
+ *      NOTE: If caller specifies a fieldSeparator, it has to be present
+ *            in the fieldName being parsed. '\0' represents an unspecified
+ *            fieldSeparator.
  *
  * Results:
  *      TRUE   Success!
@@ -364,9 +426,10 @@ GuestInfoProcMemInfoData(GuestInfoCollector *collector)  // IN:
 
 static Bool
 GuestInfoProcData(const char *pathName,           // IN: path name
-                  GuestInfoCollector *collector)  // IN:
+                  char fieldSeparator,            // IN/OPT:
+                  GuestInfoCollector *collector)  // IN/OUT:
 {
-   char line[4096];
+   char line[4096];  // Close to length of /proc/stat intr line
    FILE *fp = Posix_Fopen(pathName, "r");
 
    if (fp == NULL) {
@@ -374,15 +437,34 @@ GuestInfoProcData(const char *pathName,           // IN: path name
       return FALSE;
    }
 
-   while (fgets(line, sizeof line, fp) != NULL) {
+   /*
+    * If a line inside the file is longer than what the buffer can hold,
+    * fgets still succeeds, the next fgets call continues at the previously
+    * stopped location in the line.
+    */
+   while (fgets(line, sizeof line, fp) == line) {
       uint64 value = 0;
-      char *fieldName = strtok(line, " \t");
-      char *fieldData = strtok(NULL, " \t");
+      char *savedPtr = NULL;
+      char *fieldName = strtok_r(line, " \t", &savedPtr);
+      char *fieldData = strtok_r(NULL, " \t", &savedPtr);
 
       if (fieldName == NULL) {
          continue;
       }
 
+      if (fieldSeparator != '\0') {
+         char *p = strrchr(fieldName, fieldSeparator);
+         if (p == NULL) {
+            /*
+             * When fieldSeparator is specified, fieldName is expected
+             * to have it.
+             */
+            continue;
+         } else {
+            *p = '\0';
+         }
+      }
+
       if ((fieldData == NULL) ||
           (sscanf(fieldData, "%"FMT64"u", &value) != 1)) {
          continue;
@@ -402,7 +484,7 @@ GuestInfoProcData(const char *pathName,           // IN: path name
  *
  * GuestInfoProcSimpleValue --
  *
- *      Reads the specified /proc file, extracts a single, simple value and
+ *      Reads the stat /proc file, extracts a single, simple value and
  *      adds it to the collection.
  *
  * Results:
@@ -414,42 +496,47 @@ GuestInfoProcData(const char *pathName,           // IN: path name
  *
  *----------------------------------------------------------------------
  */
-#if PUBLISH_P1_2016_STATS
+#if PUBLISH_EXPERIMENTAL_STATS
 
 static Bool
-GuestInfoProcSimpleValue(const char *pathName,           // IN:
-                         GuestStatToolsID reportID,      // IN:
+GuestInfoProcSimpleValue(GuestStatToolsID reportID,      // IN:
                          GuestInfoCollector *collector)  // IN/OUT:
 {
-   char line[4096];
-   uint64 value = 0;
-   FILE *fp = Posix_Fopen(pathName, "r");
+   char line[512];
+   uint64 value;
+   FILE *fp;
    Bool success = FALSE;
+   GuestInfoStat *stat = NULL;
 
+   HashTable_Lookup(collector->reportMap, INT_AS_HASHKEY(reportID),
+                    (void **) &stat);
+   ASSERT(stat);
+   if (stat == NULL) {
+      g_warning("%s: Error stat ID %d not found.\n", __FUNCTION__, reportID);
+      return success;
+   }
+
+   ASSERT(stat->query->sourceFile);
+   fp = Posix_Fopen(stat->query->sourceFile, "r");
    if (fp == NULL) {
-      g_warning("%s: Error opening %s.\n", __FUNCTION__, pathName);
+      g_warning("%s: Error opening %s.\n",
+                __FUNCTION__, stat->query->sourceFile);
       return success;
    }
 
-   if (fgets(line, sizeof line, fp) != NULL) {
+   if (fgets(line, sizeof line, fp) == line) {
+      value = 0;
       if (sscanf(line, "%"FMT64"u", &value) == 1) {
+         stat->err = 0;
+         stat->count = 1;
+         stat->value = value;
+
          success = TRUE;
       }
    }
 
    fclose(fp);
 
-   if (success) {
-      GuestInfoStat *stat = NULL;
-
-      HashTable_Lookup(collector->reportMap, INT_AS_HASHKEY(reportID),
-                       (void **) &stat);
-
-      if (stat != NULL) {
-         GuestInfoStoreStat(pathName, stat, value);
-      }
-   }
-
    return success;
 }
 #endif
@@ -470,9 +557,10 @@ GuestInfoProcSimpleValue(const char *pathName,           // IN:
  *
  *----------------------------------------------------------------------
  */
+#if PUBLISH_EXPERIMENTAL_STATS
 
 static void
-GuestInfoDeriveSwapData(GuestInfoCollector *collector)  // IN: current collection
+GuestInfoDeriveSwapData(GuestInfoCollector *collector)  // IN/OUT:
 {
    uint64 swapFree = 0;
    uint64 swapTotal = 0;
@@ -521,7 +609,6 @@ GuestInfoDeriveSwapData(GuestInfoCollector *collector)  // IN: current collectio
 
          ASSERT(swapTotal >= swapFree);
          swapUsed = (swapTotal >= swapFree) ? swapTotal - swapFree : 0;
-
          if ((swapSpaceUsed != NULL) && (swapSpaceUsed->err != 0)) {
             swapSpaceUsed->value = swapUsed;
             swapSpaceUsed->count = 1;
@@ -530,6 +617,177 @@ GuestInfoDeriveSwapData(GuestInfoCollector *collector)  // IN: current collectio
       }
    }
 }
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GuestInfoDecreaseCpuRunQueueByOne --
+ *
+ *      Exclude the collector thread, make the result be consistent with
+ *      "sar -q".
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GuestInfoDecreaseCpuRunQueueByOne(GuestInfoCollector *collector)  // IN/OUT:
+{
+   GuestInfoStat *stat = NULL;
+
+   HashTable_Lookup(collector->reportMap,
+                    INT_AS_HASHKEY(GuestStatID_Linux_CpuRunQueue),
+                    (void **) &stat);
+
+   ASSERT(stat != NULL);
+   ASSERT(stat->err == 0);
+   ASSERT(stat->count == 1);
+   if (stat != NULL && stat->err == 0 && stat->count == 1) {
+      if (stat->value > 0) {
+         stat->value--;
+      }
+   }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GuestInfoProcDiskStatsData --
+ *
+ *      Reads /proc/diskstats, extract disk request queue stats and
+ *      adds them to the collection.
+ *
+ * Results:
+ *      TRUE   Success!
+ *      FALSE  Failure!
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Bool
+GuestInfoProcDiskStatsData(GuestInfoCollector *collector)  // IN/OUT:
+{
+   static int curr = 0;
+
+   int prev;
+   GuestInfoDiskStatsList **listItem;
+   uint64 inflightIOsSum;
+   Bool setStats; // Only when no disk device change in between
+
+   char line[512];
+   FILE *fp = Posix_Fopen(DISKSTATS_FILE, "r");
+
+   if (fp == NULL) {
+      g_warning("%s: Error opening " DISKSTATS_FILE ".\n", __FUNCTION__);
+      return FALSE;
+   }
+
+   prev = curr ^ 1;  // curr = 0 => prev = 1; curr = 1 => prev = 0
+   listItem = &gDiskStatsList;
+   inflightIOsSum = 0;
+   setStats = (gDiskStatsList != NULL) ? TRUE : FALSE;
+
+   while (fgets(line, sizeof line, fp) == line) {
+      /*
+       * Linux kernel diskstats_show format string:
+       * "%4d %7d %s %lu %lu %lu %u %lu %lu %lu %u %u %u %u\n"
+       *
+       * Matching data types are selected to calculate accumulating fields'
+       * increment correctly in case of overflow for both 32 and 64 bit
+       * Linux systems.
+       */
+      int assignedCount;
+      char diskName[NAME_MAX + 1]; // NAME_MAX is defined to 255
+      long unsigned int readIOs;   // # of reads completed
+      long unsigned int writeIOs;  // # of writes completed
+      unsigned int inflightIOs;    // # of I/Os currently in progress
+      unsigned int weightedTime;   // Weighted # of milliseconds
+                                   // spent in doing I/Os
+      assignedCount = sscanf(line,
+                             "%*d %*d %" XSTR(NAME_MAX) "s "
+                             "%lu %*u %*u %*u "
+                             "%lu %*u %*u %*u "
+                             "%u %*u %u",
+                             diskName, // '\0' is added automatically
+                             &readIOs,
+                             &writeIOs,
+                             &inflightIOs, &weightedTime);
+      if (assignedCount != 5 ||
+          (readIOs == 0 && writeIOs == 0) ||
+          !GuestInfoIsBlockDevice(diskName)) {
+         continue;
+      }
+
+      inflightIOsSum += inflightIOs;
+
+      if (*listItem != NULL) {
+         if (strcmp((*listItem)->diskName, diskName) == 0) {
+            (*listItem)->weightedTime[curr] = weightedTime;
+         } else {
+            /* Disk hot plug/unplug, rebuild the rest of the list. */
+            GuestInfoDeleteDiskStatsList(*listItem);
+            *listItem = NULL;
+         }
+      }
+
+      if (*listItem == NULL) {
+         *listItem = (GuestInfoDiskStatsList *)
+                         Util_SafeMalloc(sizeof **listItem);
+         (*listItem)->next = NULL;
+         (*listItem)->diskName = Util_SafeStrdup(diskName);
+         (*listItem)->weightedTime[curr] = weightedTime;
+         (*listItem)->weightedTime[prev] = 0;
+
+         /* Also covers disk hot plug at the end of the list. */
+         setStats = FALSE;
+      }
+
+      listItem = &((*listItem)->next);
+   }
+
+   fclose(fp);
+
+   if (listItem == &gDiskStatsList // No qualified disk device found
+       || *listItem != NULL) {     // Disk hot unplug at the end of the list
+      GuestInfoDeleteDiskStatsList(*listItem);
+      *listItem = NULL;
+      setStats = FALSE;
+   }
+
+   if (setStats) {
+      GuestInfoDiskStatsList *currDiskStats = gDiskStatsList;
+      uint64 weightedTimeDeltaSum = 0;
+
+      while (currDiskStats != NULL) {
+         unsigned int weightedTimeDelta = currDiskStats->weightedTime[curr] -
+                                          currDiskStats->weightedTime[prev];
+         weightedTimeDeltaSum += weightedTimeDelta;
+         currDiskStats = currDiskStats->next;
+      }
+
+      GuestInfoStoreStatByID(GuestStatID_Linux_DiskRequestQueue,
+                             collector,
+                             inflightIOsSum);
+      GuestInfoStoreStatByID(GuestStatID_Linux_DiskRequestQueueAvg,
+                             collector,
+                             weightedTimeDeltaSum);
+   }
+
+   curr = prev;
+
+   return TRUE;
+}
 
 
 /*
@@ -549,7 +807,7 @@ GuestInfoDeriveSwapData(GuestInfoCollector *collector)  // IN: current collectio
  */
 
 static void
-GuestInfoCollect(GuestInfoCollector *collector)  // IN:
+GuestInfoCollect(GuestInfoCollector *collector)  // IN/OUT:
 {
    uint32 i;
    GuestInfoStat *stat;
@@ -565,32 +823,23 @@ GuestInfoCollect(GuestInfoCollector *collector)  // IN:
    }
 
    /* Collect new values */
-   GuestInfoProcMemInfoData(collector);
-   GuestInfoProcData(VMSTAT_FILE, collector);
-   GuestInfoProcData(STAT_FILE, collector);
-   GuestInfoProcData(ZONEINFO_FILE, collector);
-#if PUBLISH_P1_2016_STATS
-   GuestInfoProcSimpleValue(SWAPPINESS_FILE, GuestStatID_Linux_Swappiness,
-                            collector);
-#endif
+   GuestInfoProcData(MEMINFO_FILE, ':', collector);
+   GuestInfoProcData(VMSTAT_FILE, '\0', collector);
+   GuestInfoProcData(STAT_FILE, '\0', collector);
+   GuestInfoProcData(ZONEINFO_FILE, '\0', collector);
+#if PUBLISH_EXPERIMENTAL_STATS
+   GuestInfoProcSimpleValue(GuestStatID_Linux_Swappiness, collector);
    GuestInfoDeriveSwapData(collector);
+#endif
 
    collector->timeData = GuestInfoGetUpTime(&collector->timeStamp);
 
    /*
     * We make sure physical page size is always present.
     */
-
-   stat = NULL;
-   HashTable_Lookup(collector->reportMap,
-                    INT_AS_HASHKEY(GuestStatID_PhysicalPageSize),
-                    (void **) &stat);
-
-   if ((stat != NULL) && (stat->err != 0)) {
-      stat->value = pageSize;
-      stat->count = 1;
-      stat->err = 0;
-   }
+   GuestInfoStoreStatByID(GuestStatID_PhysicalPageSize,
+                          collector,
+                          pageSize);
 
    /*
     * Attempt to fix up memPhysUsable if it is not available.
@@ -618,6 +867,12 @@ GuestInfoCollect(GuestInfoCollector *collector)  // IN:
          stat->value = memTotal->value;
       }
    }
+
+   GuestInfoDeriveMemNeeded(collector);
+   if (gUnstable) {
+      GuestInfoDecreaseCpuRunQueueByOne(collector);
+      GuestInfoProcDiskStatsData(collector);
+   }
 }
 
 
@@ -798,7 +1053,7 @@ GuestInfoAppendStat(int errnoValue,                // IN:
 
 static void
 GuestInfoAppendRate(Bool emitNameSpace,             // IN:
-                    GuestStatToolsID reportID,      // IN: Id of the stat
+                    GuestStatToolsID reportID,      // IN: ID of the stat
                     GuestInfoCollector *current,    // IN: current collection
                     GuestInfoCollector *previous,   // IN: previous collection
                     DynBuf *statBuf)                // IN/OUT: stat data
@@ -821,9 +1076,28 @@ GuestInfoAppendRate(Bool emitNameSpace,             // IN:
        ((currentStat != NULL) && (currentStat->err == 0)) &&
        ((previousStat != NULL) && (previousStat->err == 0))) {
       double timeDelta = current->timeStamp - previous->timeStamp;
-      double valueDelta = currentStat->value - previousStat->value;
+      double valueDelta;
+
+      /*
+       * DiskRequestQueueAvg GuestInfoStat::value is weighted number of
+       * milliseconds delta in uint64 type, need to divide it by 1000 to
+       * turn the number in seconds.
+       *
+       * Host side drops the fraction part of double data type. Therefore,
+       * we preserve 2 decimal points by scaling up the value 100x.
+       * The consumers of this stat need to divide it by 100 to retrieve
+       * two digits after decimal point.
+       *
+       * (value / 1000) * 100 = value / 10
+       */
+      if (reportID == GuestStatID_Linux_DiskRequestQueueAvg) {
+         valueDelta = ((double)(currentStat->value)) / 10;
+      } else {
+         valueDelta = currentStat->value - previousStat->value;
+      }
 
       valueDouble = valueDelta / timeDelta;
+
       errnoValue = 0;
    }
 
@@ -856,9 +1130,10 @@ GuestInfoAppendRate(Bool emitNameSpace,             // IN:
 /*
  *----------------------------------------------------------------------
  *
- * GuestInfoAppendMemNeeded --
+ * GuestInfoDeriveMemNeeded --
  *
- *      Synthesize memNeeded and append it to the stat buffer.
+ *      Update memory needed stats that are calculated
+ *      rather than fetched.
  *
  * Results:
  *      None.
@@ -870,9 +1145,7 @@ GuestInfoAppendRate(Bool emitNameSpace,             // IN:
  */
 
 static void
-GuestInfoAppendMemNeeded(GuestInfoCollector *current,  // IN: current collection
-                         Bool emitNameSpace,           // IN:
-                         DynBuf *statBuf)              // IN/OUT: stats data
+GuestInfoDeriveMemNeeded(GuestInfoCollector *collector)  // IN/OUT:
 {
    uint64 memNeeded;
    uint64 memNeededReservation;
@@ -880,13 +1153,13 @@ GuestInfoAppendMemNeeded(GuestInfoCollector *current,  // IN: current collection
    GuestInfoStat *memAvail = NULL;
    GuestInfoStat *memPhysUsable = NULL;
 
-   HashTable_Lookup(current->reportMap,
+   HashTable_Lookup(collector->reportMap,
                     INT_AS_HASHKEY(GuestStatID_MemPhysUsable),
                     (void **) &memPhysUsable);
 
    ASSERT(memPhysUsable != NULL);
 
-   HashTable_Lookup(current->reportMap,
+   HashTable_Lookup(collector->reportMap,
                     INT_AS_HASHKEY(GuestStatID_Linux_MemAvailable),
                     (void **) &memAvail);
 
@@ -901,25 +1174,25 @@ GuestInfoAppendMemNeeded(GuestInfoCollector *current,  // IN: current collection
       GuestInfoStat *memInactiveFile = NULL;
       GuestInfoStat *lowWaterMark = NULL;
 
-      HashTable_Lookup(current->reportMap,
+      HashTable_Lookup(collector->reportMap,
                        INT_AS_HASHKEY(GuestStatID_MemFree),
                        (void **) &memFree);
-      HashTable_Lookup(current->reportMap,
+      HashTable_Lookup(collector->reportMap,
                        INT_AS_HASHKEY(GuestStatID_Linux_MemCached),
                        (void **) &memCache);
-      HashTable_Lookup(current->reportMap,
+      HashTable_Lookup(collector->reportMap,
                        INT_AS_HASHKEY(GuestStatID_Linux_MemBuffers),
                        (void **) &memBuffers);
-      HashTable_Lookup(current->reportMap,
+      HashTable_Lookup(collector->reportMap,
                        INT_AS_HASHKEY(GuestStatID_MemActiveFileCache),
                        (void **) &memActiveFile);
-      HashTable_Lookup(current->reportMap,
+      HashTable_Lookup(collector->reportMap,
                        INT_AS_HASHKEY(GuestStatID_Linux_MemSlabReclaim),
                        (void **) &memSlabReclaim);
-      HashTable_Lookup(current->reportMap,
+      HashTable_Lookup(collector->reportMap,
                        INT_AS_HASHKEY(GuestStatID_Linux_MemInactiveFile),
                        (void **) &memInactiveFile);
-      HashTable_Lookup(current->reportMap,
+      HashTable_Lookup(collector->reportMap,
                        INT_AS_HASHKEY(GuestStatID_Linux_LowWaterMark),
                        (void **) &lowWaterMark);
 
@@ -983,51 +1256,13 @@ GuestInfoAppendMemNeeded(GuestInfoCollector *current,  // IN: current collection
       memNeededReservation = 0;
    }
 
-#if PUBLISH_P1_2015_STATS
-   GuestInfoAppendStat(0,
-                       emitNameSpace,
-                       GuestStatID_MemNeeded,
-                       GuestUnitsKiB, GuestTypeUint64,
-                       &memNeeded,
-                       GuestInfoBytesNeededUIntDatum(memNeeded),
-                       statBuf);
-
-   emitNameSpace = FALSE;
-#endif
-
-#if PUBLISH_P2_STATS
-   GuestInfoAppendStat(0,
-                       emitNameSpace,
-                       GuestStatID_MemNeededReservation,
-                       GuestUnitsKiB, GuestTypeUint64,
-                       &memNeededReservation,
-                       GuestInfoBytesNeededUIntDatum(memNeededReservation),
-                       statBuf);
-#endif
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * GuestInfoIsRate --
- *
- *      Is the specified unit a rate?
- *
- * Results:
- *      TRUE  Yes
- *      FALSE No
- *
- * Side effects:
- *      None.
- *
- *----------------------------------------------------------------------
- */
+   GuestInfoStoreStatByID(GuestStatID_MemNeeded,
+                          collector,
+                          memNeeded);
 
-static Bool
-GuestInfoIsRate(GuestValueUnits units)  // IN:
-{
-   return ((units & GuestUnitsModifier_Rate) != 0);
+   GuestInfoStoreStatByID(GuestStatID_MemNeededReservation,
+                          collector,
+                          memNeededReservation);
 }
 
 
@@ -1065,16 +1300,16 @@ GuestInfoEncodeStats(GuestInfoCollector *current,   // IN: current collection
    for (i = 0; i < current->numStats; i++) {
       GuestInfoStat *stat = &current->stats[i];
 
-      if (!stat->query->publish) {
+      if (!*(stat->query->publish)) {
          continue;
       }
 
-      if (GuestInfoIsRate(stat->query->units)) {
-         ASSERT(stat->query->dataType == GuestTypeDouble);
+      if (stat->query->dataType == GuestTypeDouble) {
          GuestInfoAppendRate(emitNameSpace, stat->query->reportID,
                              current, previous, statBuf);
       } else {
          ASSERT(stat->query->dataType == GuestTypeUint64);
+         ASSERT((stat->query->units & GuestUnitsModifier_Rate) == 0);
          GuestInfoAppendStat(stat->err,
                              emitNameSpace,
                              stat->query->reportID,
@@ -1087,8 +1322,6 @@ GuestInfoEncodeStats(GuestInfoCollector *current,   // IN: current collection
 
       emitNameSpace = FALSE; // use the smallest representation
    }
-
-   GuestInfoAppendMemNeeded(current, emitNameSpace, statBuf);
 }
 
 
@@ -1144,7 +1377,7 @@ GuestInfoConstructCollector(GuestInfoQuery *queries,  // IN:
 {
    uint32 i;
    uint32 regExp = 0;
-   GuestInfoCollector *collector = calloc(1, sizeof *collector);
+   GuestInfoCollector *collector = Util_SafeCalloc(1, sizeof *collector);
 
    if (collector == NULL) {
       return NULL;
@@ -1158,14 +1391,15 @@ GuestInfoConstructCollector(GuestInfoQuery *queries,  // IN:
 
    collector->numRegExps = 0;
    for (i = 0; i < numQueries; i++) {
-      if (queries[i].isRegExp && queries[i].collect) {
+      if (queries[i].isRegExp) {
          collector->numRegExps++;
       }
    }
 
    collector->numStats = numQueries;
-   collector->stats = calloc(numQueries, sizeof *collector->stats);
-   collector->regExps = calloc(collector->numRegExps, sizeof(GuestInfoStat *));
+   collector->stats = Util_SafeCalloc(numQueries, sizeof *collector->stats);
+   collector->regExps = Util_SafeCalloc(collector->numRegExps,
+                                        sizeof(GuestInfoStat *));
 
    if ((collector->exactMatches == NULL) ||
        (collector->reportMap == NULL) ||
@@ -1185,18 +1419,17 @@ GuestInfoConstructCollector(GuestInfoQuery *queries,  // IN:
 
       stat->query = query;
 
-      if (!query->collect) {
-         continue;
-      }
-
       if (query->isRegExp) {
+         ASSERT(query->sourceFile);
          ASSERT(query->locatorString);
 
          collector->regExps[regExp++] = stat;
       } else {
-         if (query->locatorString != NULL) {
-            HashTable_Insert(collector->exactMatches, query->locatorString,
-                              stat);
+         if (query->sourceFile != NULL && query->locatorString != NULL) {
+            char *key = Str_SafeAsprintf(NULL, KEY_FORMAT, query->sourceFile,
+                                         query->locatorString);
+            HashTable_Insert(collector->exactMatches, key, stat);
+            free(key);
          }
       }
 
@@ -1230,8 +1463,6 @@ Bool
 GuestInfoTakeSample(DynBuf *statBuf)  // IN/OUT: inited, ready to fill
 {
    GuestInfoCollector *temp;
-   static GuestInfoCollector *current = NULL;
-   static GuestInfoCollector *previous = NULL;
 
    ASSERT(statBuf && DynBuf_GetSize(statBuf) == 0);
 
@@ -1241,33 +1472,33 @@ GuestInfoTakeSample(DynBuf *statBuf)  // IN/OUT: inited, ready to fill
    }
 
    /* First time through, allocate all necessary memory */
-   if (previous == NULL) {
-      current = GuestInfoConstructCollector(guestInfoQuerySpecTable,
+   if (gPreviousCollector == NULL) {
+      gCurrentCollector = GuestInfoConstructCollector(guestInfoQuerySpecTable,
                                             N_QUERIES);
 
-      previous = GuestInfoConstructCollector(guestInfoQuerySpecTable,
+      gPreviousCollector = GuestInfoConstructCollector(guestInfoQuerySpecTable,
                                              N_QUERIES);
    }
 
-   if ((current == NULL) ||
-       (previous == NULL)) {
-      GuestInfoDestroyCollector(current);
-      current = NULL;
-      GuestInfoDestroyCollector(previous);
-      previous = NULL;
+   if ((gCurrentCollector == NULL) ||
+       (gPreviousCollector == NULL)) {
+      GuestInfoDestroyCollector(gCurrentCollector);
+      gCurrentCollector = NULL;
+      GuestInfoDestroyCollector(gPreviousCollector);
+      gPreviousCollector = NULL;
       return FALSE;
    }
 
    /* Collect the current data */
-   GuestInfoCollect(current);
+   GuestInfoCollect(gCurrentCollector);
 
    /* Encode the captured data */
-   GuestInfoEncodeStats(current, previous, statBuf);
+   GuestInfoEncodeStats(gCurrentCollector, gPreviousCollector, statBuf);
 
    /* Switch the collections for next time. */
-   temp = current;
-   current = previous;
-   previous = temp;
+   temp = gCurrentCollector;
+   gCurrentCollector = gPreviousCollector;
+   gPreviousCollector = temp;
 
    return TRUE;
 }
@@ -1297,6 +1528,11 @@ GuestInfo_StatProviderPoll(gpointer data)
 
    g_debug("Entered guest info stats gather.\n");
 
+   gUnstable = g_key_file_get_boolean(ctx->config,
+                                      CONFGROUPNAME_GUESTINFO,
+                                      "enable-unstable-stats",
+                                      NULL);
+
    /* Send the vmstats to the VMX. */
    DynBuf_Init(&stats);
 
@@ -1331,5 +1567,11 @@ GuestInfo_StatProviderPoll(gpointer data)
 void
 GuestInfo_StatProviderShutdown(void)
 {
-   // Nothing to do here for now
+   GuestInfoDeleteDiskStatsList(gDiskStatsList);
+   gDiskStatsList = NULL;
+
+   GuestInfoDestroyCollector(gCurrentCollector);
+   gCurrentCollector = NULL;
+   GuestInfoDestroyCollector(gPreviousCollector);
+   gPreviousCollector = NULL;
 }