Index: /trunk/include/VBox/gvm.h
===================================================================
--- /trunk/include/VBox/gvm.h	(revision 5210)
+++ /trunk/include/VBox/gvm.h	(revision 5211)
@@ -55,5 +55,5 @@
         struct GVMMPERVM    s;
 #endif
-        uint8_t             padding[128];
+        uint8_t             padding[256];
     } gvmm;
 
Index: /trunk/include/VBox/gvmm.h
===================================================================
--- /trunk/include/VBox/gvmm.h	(revision 5210)
+++ /trunk/include/VBox/gvmm.h	(revision 5211)
@@ -24,4 +24,6 @@
 #include <VBox/sup.h>
 
+__BEGIN_DECLS
+
 /** @defgroup grp_GVMM  GVMM - The Global VM Manager.
  * @{
@@ -47,9 +49,62 @@
 #define NIL_GVM_HANDLE 0
 
+
+/**
+ * The scheduler statistics
+ */
+typedef struct GVMMSTATSSCHED
+{
+    /** The number of calls to GVMMR0SchedHalt. */
+    uint64_t        cHaltCalls;
+    /** The number of times we did go to sleep in GVMMR0SchedHalt. */
+    uint64_t        cHaltBlocking;
+    /** The number of times we timed out in GVMMR0SchedHalt. */
+    uint64_t        cHaltTimeouts;
+    /** The number of times we didn't go to sleep in GVMMR0SchedHalt. */
+    uint64_t        cHaltNotBlocking;
+    /** The number of wake ups done during GVMMR0SchedHalt. */
+    uint64_t        cHaltWakeUps;
+
+    /** The number of calls to GVMMR0WakeUp. */
+    uint64_t        cWakeUpCalls;
+    /** The number of times the EMT thread wasn't actually halted when GVMMR0WakeUp was called. */
+    uint64_t        cWakeUpNotHalted;
+    /** The number of wake ups done during GVMMR0WakeUp (not counting the explicit one). */
+    uint64_t        cWakeUpWakeUps;
+
+    /** The number of calls to GVMMR0SchedPoll. */
+    uint64_t        cPollCalls;
+    /** The number of times the EMT has halted in a GVMMR0SchedPoll call. */
+    uint64_t        cPollHalts;
+    /** The number of wake ups done during GVMMR0SchedPoll. */
+    uint64_t        cPollWakeUps;
+    uint64_t        u64Alignment; /**< padding */
+} GVMMSTATSSCHED;
+/** Pointer to the GVMM scheduler statistics. */
+typedef GVMMSTATSSCHED *PGVMMSTATSSCHED;
+
+/**
+ * The GMM statistics.
+ */
+typedef struct GVMMSTATS
+{
+    /** The VM statistics if a VM was specified. */
+    GVMMSTATSSCHED  SchedVM;
+    /** The sum statistics of all VMs accessible to the caller. */
+    GVMMSTATSSCHED  SchedSum;
+    /** The number of VMs accessible to the caller. */
+    uint32_t        cVMs;
+    /** Alignment padding. */
+    uint32_t        u32Padding;
+} GVMMSTATS;
+/** Pointer to the GVMM statistics. */
+typedef GVMMSTATS *PGVMMSTATS;
+
+
+
 GVMMR0DECL(int)     GVMMR0Init(void);
 GVMMR0DECL(void)    GVMMR0Term(void);
 
 GVMMR0DECL(int)     GVMMR0CreateVM(PSUPDRVSESSION pSession, PVM *ppVM);
-GVMMR0DECL(int)     GVMMR0CreateVMReq(PSUPVMMR0REQHDR pReqHdr);
 GVMMR0DECL(int)     GVMMR0DisassociateEMTFromVM(PVM pVM);
 GVMMR0DECL(int)     GVMMR0InitVM(PVM pVM);
@@ -64,5 +119,5 @@
 GVMMR0DECL(int)     GVMMR0SchedWakeUp(PVM pVM);
 GVMMR0DECL(int)     GVMMR0SchedPoll(PVM pVM, bool fYield);
-
+GVMMR0DECL(int)     GVMMR0QueryStatistics(PGVMMSTATS pStats, PSUPDRVSESSION pSession, PVM pVM);
 
 
@@ -84,7 +139,31 @@
 typedef GVMMCREATEVMREQ *PGVMMCREATEVMREQ;
 
+GVMMR0DECL(int)     GVMMR0CreateVMReq(PGVMMCREATEVMREQ pReq);
+
+
+/**
+ * Request buffer for GVMMR0QueryStatisticsReq / VMMR0_DO_GVMM_QUERY_STATISTICS.
+ * @see GVMMR0QueryStatistics.
+ */
+typedef struct GVMMQUERYSTATISTICSSREQ
+{
+    /** The header. */
+    SUPVMMR0REQHDR  Hdr;
+    /** The support driver session. */
+    PSUPDRVSESSION  pSession;
+    /** The statistics. */
+    GVMMSTATS       Stats;
+} GVMMQUERYSTATISTICSSREQ;
+/** Pointer to a GMMR0AllocatePagesReq / VMMR0_DO_GMM_ALLOCATE_PAGES request buffer. */
+typedef GVMMQUERYSTATISTICSSREQ *PGVMMQUERYSTATISTICSSREQ;
+
+GVMMR0DECL(int)     GVMMR0QueryStatisticsReq(PVM pVM, PGVMMQUERYSTATISTICSSREQ pReq);
+
+
 
 /** @} */
 
+__END_DECLS
+
 #endif
 
Index: /trunk/include/VBox/vm.h
===================================================================
--- /trunk/include/VBox/vm.h	(revision 5210)
+++ /trunk/include/VBox/vm.h	(revision 5211)
@@ -495,5 +495,5 @@
         struct STAM s;
 #endif
-        char        padding[32];        /* multiple of 32 */
+        char        padding[256];       /* multiple of 32 */
     } stam;
 
Index: /trunk/include/VBox/vmm.h
===================================================================
--- /trunk/include/VBox/vmm.h	(revision 5210)
+++ /trunk/include/VBox/vmm.h	(revision 5211)
@@ -366,4 +366,6 @@
     /** Call GVMMR0SchedPoll(). */
     VMMR0_DO_GVMM_SCHED_POLL,
+    /** Call GVMMR0QueryStatistics(). */
+    VMMR0_DO_GVMM_QUERY_STATISTICS,
 
     /** Call VMMR0 Per VM Init. */
@@ -400,4 +402,7 @@
     VMMR0_DO_GMM_SEED_CHUNK,
 
+    /** Gets the GMM and GVMM stats. */
+    VMMR0_DO_GET_GXX_STATS,
+
     /** The start of the R0 service operations. */
     VMMR0_DO_SRV_START,
Index: /trunk/src/VBox/VMM/STAM.cpp
===================================================================
--- /trunk/src/VBox/VMM/STAM.cpp	(revision 5210)
+++ /trunk/src/VBox/VMM/STAM.cpp	(revision 5211)
@@ -83,4 +83,22 @@
 
 
+/**
+ * Init record for a ring-0 statistic sample.
+ */
+typedef struct STAMINITR0SAMPLE
+{
+    /** The VM structure offset of the variable. */
+    unsigned        offVar;
+    /** The type. */
+    STAMTYPE        enmType;
+    /** The unit. */
+    STAMUNIT        enmUnit;
+    /** The name. */
+    const char     *pszName;
+    /** The description. */
+    const char     *pszDesc;
+} STAMINITR0SAMPLE;
+
+
 /*******************************************************************************
 *   Internal Functions                                                         *
@@ -96,5 +114,8 @@
 static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg);
 static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg);
-static int stamR3Enum(PVM pVM, const char *pszPat, int (pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg);
+static int stamR3Enum(PVM pVM, const char *pszPat, bool fUpdateRing0, int (pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg);
+static void stamR3Ring0StatsRegister(PVM pVM);
+static void stamR3Ring0StatsUpdate(PVM pVM, const char *pszPat);
+static void stamR3Ring0StatsUpdateMulti(PVM pVM, const char * const *papszExpressions, unsigned cExpressions);
 
 #ifdef VBOX_WITH_DEBUGGER
@@ -126,4 +147,36 @@
 
 
+/**
+ * The GVMM init records.
+ */
+static const STAMINITR0SAMPLE g_aGVMMStats[] =
+{
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltCalls),        STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltBlocking),     STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltTimeouts),     STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltNotBlocking),  STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltNotBlocking", "The number of times we didn't go to sleep in GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltWakeUps),      STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cWakeUpCalls),      STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cWakeUpNotHalted),  STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/WakeUpNotHalted", "The number of times the EMT thread wasn't actually halted when GVMMR0WakeUp was called." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cWakeUpWakeUps),    STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/WakeUpWakeUps", "The number of wake ups done during GVMMR0WakeUp (not counting the explicit one)." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cPollCalls),        STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/PollCalls", "The number of calls to GVMMR0SchedPoll." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cPollHalts),        STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/PollHalts", "The number of times the EMT has halted in a GVMMR0SchedPoll call." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cPollWakeUps),      STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
+
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltCalls),       STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltBlocking),    STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltTimeouts),    STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltNotBlocking), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltNotBlocking", "The number of times we didn't go to sleep in GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltWakeUps),     STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cWakeUpCalls),     STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cWakeUpNotHalted), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpNotHalted", "The number of times the EMT thread wasn't actually halted when GVMMR0WakeUp was called." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cWakeUpWakeUps),   STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpWakeUps", "The number of wake ups done during GVMMR0WakeUp (not counting the explicit one)." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cPollCalls),       STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/PollCalls", "The number of calls to GVMMR0SchedPoll." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cPollHalts),       STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/PollHalts", "The number of times the EMT has halted in a GVMMR0SchedPoll call." },
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cPollWakeUps),     STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
+
+    { RT_UOFFSETOF(VM, stam.s.GVMMStats.cVMs),                      STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/VMs", "The number of VMs accessible to the caller." },
+};
+
 
 /**
@@ -151,4 +204,9 @@
     if (VBOX_FAILURE(rc))
         return rc;
+
+    /*
+     * Register the ring-0 statistics (GVMM/GMM).
+     */
+    stamR3Ring0StatsRegister(pVM);
 
 #ifdef VBOX_WITH_DEBUGGER
@@ -502,5 +560,5 @@
 {
     STAM_LOCK_WR(pVM);
-    stamR3Enum(pVM, pszPat, stamR3ResetOne, pVM);
+    stamR3Enum(pVM, pszPat, false /* fUpdateRing0 */, stamR3ResetOne, pVM);
     STAM_UNLOCK_WR(pVM);
     return VINF_SUCCESS;
@@ -613,5 +671,5 @@
     stamR3SnapshotPrintf(&State, "<Statistics>\n");
     STAM_LOCK_RD(pVM);
-    int rc = stamR3Enum(pVM, pszPat, stamR3SnapshotOne, &State);
+    int rc = stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3SnapshotOne, &State);
     STAM_UNLOCK_RD(pVM);
     stamR3SnapshotPrintf(&State, "</Statistics>\n");
@@ -869,5 +927,5 @@
 
     STAM_LOCK_RD(pVM);
-    stamR3Enum(pVM, pszPat, stamR3PrintOne, &Args);
+    stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
     STAM_UNLOCK_RD(pVM);
     return VINF_SUCCESS;
@@ -908,7 +966,6 @@
 
     STAM_LOCK_RD(pVM);
-    stamR3Enum(pVM, pszPat, stamR3PrintOne, &Args);
+    stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
     STAM_UNLOCK_RD(pVM);
-
     return VINF_SUCCESS;
 }
@@ -948,5 +1005,5 @@
 
     STAM_LOCK_RD(pVM);
-    stamR3Enum(pVM, pszPat, stamR3PrintOne, &Args);
+    stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
     STAM_UNLOCK_RD(pVM);
     return VINF_SUCCESS;
@@ -1104,5 +1161,5 @@
 
     STAM_LOCK_RD(pVM);
-    int rc = stamR3Enum(pVM, pszPat, stamR3EnumOne, &Args);
+    int rc = stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3EnumOne, &Args);
     STAM_UNLOCK_RD(pVM);
     return rc;
@@ -1151,6 +1208,8 @@
         switch (chPat)
         {
-            case '\0':
-                return !*pszName;
+            default:
+                if (*pszName != chPat)
+                    return false;
+                break;
 
             case '*':
@@ -1178,8 +1237,6 @@
                 break;
 
-            default:
-                if (*pszName != chPat)
-                    return false;
-                break;
+            case '\0':
+                return !*pszName;
         }
         pszName++;
@@ -1196,6 +1253,5 @@
  * @param   papszExpressions    The array of pattern expressions.
  * @param   cExpressions        The number of array entries.
- * @param   piExpression        Where to read/store the current skip index.
- *                              This is for future use.
+ * @param   piExpression        Where to read/store the current skip index. Optional.
  * @param   pszName             The name to match.
  */
@@ -1203,5 +1259,5 @@
                              unsigned *piExpression, const char *pszName)
 {
-    for (unsigned i = *piExpression; i < cExpressions; i++)
+    for (unsigned i = piExpression ? *piExpression : 0; i < cExpressions; i++)
     {
         const char *pszPat = papszExpressions[i];
@@ -1209,5 +1265,5 @@
         {
             /* later:
-            if (i > *piExpression)
+            if (piExpression && i > *piExpression)
             {
                 check if we can skip some expressions
@@ -1228,12 +1284,13 @@
  *
  * @returns The rc from the callback.
- * @param   pVM         VM handle
- * @param   pszPat      Pattern.
- * @param   pfnCallback Callback function which shall be called for matching nodes.
- *                      If it returns anything but VINF_SUCCESS the enumeration is
- *                      terminated and the status code returned to the caller.
- * @param   pvArg       User parameter for the callback.
- */
-static int stamR3Enum(PVM pVM, const char *pszPat, int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
+ * @param   pVM             VM handle
+ * @param   pszPat          Pattern.
+ * @param   fUpdateRing0    Update the ring-0 .
+ * @param   pfnCallback     Callback function which shall be called for matching nodes.
+ *                          If it returns anything but VINF_SUCCESS the enumeration is
+ *                          terminated and the status code returned to the caller.
+ * @param   pvArg           User parameter for the callback.
+ */
+static int stamR3Enum(PVM pVM, const char *pszPat, bool fUpdateRing0, int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
 {
     int rc = VINF_SUCCESS;
@@ -1244,4 +1301,7 @@
     if (!pszPat || !*pszPat || !strcmp(pszPat, "*"))
     {
+        if (fUpdateRing0)
+            stamR3Ring0StatsUpdate(pVM, "*");
+
         PSTAMDESC   pCur = pVM->stam.s.pHead;
         while (pCur)
@@ -1261,4 +1321,7 @@
     else if (!strchr(pszPat, '|'))
     {
+        if (fUpdateRing0)
+            stamR3Ring0StatsUpdate(pVM, pszPat);
+
         for (PSTAMDESC pCur = pVM->stam.s.pHead; pCur; pCur = pCur->pNext)
             if (stamR3Match(pszPat, pCur->pszName))
@@ -1312,4 +1375,7 @@
          * Perform the enumeration.
          */
+        if (fUpdateRing0)
+            stamR3Ring0StatsUpdateMulti(pVM, papszExpressions, cExpressions);
+
         unsigned iExpression = 0;
         for (PSTAMDESC pCur = pVM->stam.s.pHead; pCur; pCur = pCur->pNext)
@@ -1326,4 +1392,66 @@
 
     return rc;
+}
+
+
+/**
+ * Registers the ring-0 statistics.
+ *
+ * @param   pVM         Pointer to the shared VM structure.
+ */
+static void stamR3Ring0StatsRegister(PVM pVM)
+{
+    /* GVMM */
+    for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
+        stamR3Register(pVM, (uint8_t *)pVM + g_aGVMMStats[i].offVar, NULL, NULL,
+                       g_aGVMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGVMMStats[i].pszName,
+                       g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc);
+}
+
+
+/**
+ * Updates the ring-0 statistics (the copy).
+ *
+ * @param   pVM         Pointer to the shared VM structure.
+ * @param   pszPat      The pattern.
+ */
+static void stamR3Ring0StatsUpdate(PVM pVM, const char *pszPat)
+{
+    stamR3Ring0StatsUpdateMulti(pVM, &pszPat, 1);
+}
+
+
+/**
+ * Updates the ring-0 statistics.
+ *
+ * The ring-0 statistics aren't directly addressable from ring-3 and
+ * must be copied when needed.
+ *
+ * @param   pVM         Pointer to the shared VM structure.
+ * @param   pszPat      The pattern (for knowing when to skip).
+ */
+static void stamR3Ring0StatsUpdateMulti(PVM pVM, const char * const *papszExpressions, unsigned cExpressions)
+{
+    if (!pVM->pSession)
+        return;
+
+    /* GVMM */
+    bool fUpdate = false;
+    for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
+        if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
+        {
+            fUpdate = true;
+            break;
+        }
+    if (fUpdate)
+    {
+        GVMMQUERYSTATISTICSSREQ Req;
+        Req.Hdr.cbReq = sizeof(Req);
+        Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+        Req.pSession = pVM->pSession;
+        int rc = SUPCallVMMR0Ex(pVM->pVMR0, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
+        if (RT_SUCCESS(rc))
+            pVM->stam.s.GVMMStats = Req.Stats;
+    }
 }
 
@@ -1393,5 +1521,5 @@
 
     STAM_LOCK_RD(pVM);
-    int rc = stamR3Enum(pVM, cArgs ? paArgs[0].u.pszString : NULL, stamR3PrintOne, &Args);
+    int rc = stamR3Enum(pVM, cArgs ? paArgs[0].u.pszString : NULL, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
     STAM_UNLOCK_RD(pVM);
 
Index: /trunk/src/VBox/VMM/STAMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/STAMInternal.h	(revision 5210)
+++ /trunk/src/VBox/VMM/STAMInternal.h	(revision 5211)
@@ -22,4 +22,5 @@
 #include <VBox/types.h>
 #include <VBox/stam.h>
+#include <VBox/gvmm.h>
 #include <iprt/semaphore.h>
 
@@ -115,4 +116,7 @@
     /** RW Lock for the list. */
     RTSEMRW                 RWSem;
+
+    /** The copy of the GVMM statistics. */
+    GVMMSTATS               GVMMStats;
 };
 
Index: /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp	(revision 5210)
+++ /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp	(revision 5211)
@@ -168,5 +168,5 @@
 GVMMR0DECL(int) GVMMR0Init(void)
 {
-    SUPR0Printf("GVMMR0Init:\n");
+    LogFlow(("GVMMR0Init:\n"));
 
     /*
@@ -203,5 +203,5 @@
 
             g_pGVMM = pGVMM;
-            SUPR0Printf("GVMMR0Init: pGVMM=%p\n", pGVMM);
+            LogFlow(("GVMMR0Init: pGVMM=%p\n", pGVMM));
             return VINF_SUCCESS;
         }
@@ -224,5 +224,5 @@
 GVMMR0DECL(void) GVMMR0Term(void)
 {
-    SUPR0Printf("GVMMR0Term:\n");
+    LogFlow(("GVMMR0Term:\n"));
 
     PGVMM pGVMM = g_pGVMM;
@@ -256,10 +256,8 @@
  *
  * @returns VBox status code.
- * @param   pReqHdr     The request buffer.
- */
-GVMMR0DECL(int) GVMMR0CreateVMReq(PSUPVMMR0REQHDR pReqHdr)
-{
-    PGVMMCREATEVMREQ pReq = (PGVMMCREATEVMREQ)pReqHdr;
-
+ * @param   pReq        The request buffer.
+ */
+GVMMR0DECL(int) GVMMR0CreateVMReq(PGVMMCREATEVMREQ pReq)
+{
     /*
      * Validate the request.
@@ -421,5 +419,5 @@
 
                                         *ppVM = pVM;
-                                        SUPR0Printf("GVMMR0CreateVM: pVM=%p pVMR3=%p pGVM=%p hGVM=%d\n", pVM, pVM->pVMR3, pGVM, iHandle);
+                                        Log(("GVMMR0CreateVM: pVM=%p pVMR3=%p pGVM=%p hGVM=%d\n", pVM, pVM->pVMR3, pGVM, iHandle));
                                         return VINF_SUCCESS;
                                     }
@@ -858,5 +856,5 @@
     RTSemFastMutexRelease(pGVMM->UsedLock);
     RTSemFastMutexRelease(pGVMM->CreateDestroyLock);
-    SUPR0Printf("gvmmR0HandleObjDestructor: returns\n");
+    LogFlow(("gvmmR0HandleObjDestructor: returns\n"));
 }
 
@@ -1211,4 +1209,5 @@
     if (RT_FAILURE(rc))
         return rc;
+    pGVM->gvmm.s.StatsSched.cHaltCalls++;
 
     Assert(!pGVM->gvmm.s.u64HaltExpire);
@@ -1226,5 +1225,5 @@
     Assert(ASMGetFlags() & X86_EFL_IF);
     const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
-    gvmmR0SchedDoWakeUps(pGVMM, u64Now);
+    pGVM->gvmm.s.StatsSched.cHaltWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
 
     /*
@@ -1235,4 +1234,5 @@
              || (u64ExpireGipTime - u64Now > 750000 /* 0.750 ms */))) /** @todo make this configurable */
     {
+        pGVM->gvmm.s.StatsSched.cHaltBlocking++;
         ASMAtomicXchgU64(&pGVM->gvmm.s.u64HaltExpire, u64ExpireGipTime);
         RTSemFastMutexRelease(pGVMM->UsedLock);
@@ -1242,8 +1242,14 @@
         ASMAtomicXchgU64(&pGVM->gvmm.s.u64HaltExpire, 0);
         if (rc == VERR_TIMEOUT)
+        {
+            pGVM->gvmm.s.StatsSched.cHaltTimeouts++;
             rc = VINF_SUCCESS;
+        }
     }
     else
+    {
+        pGVM->gvmm.s.StatsSched.cHaltNotBlocking++;
         RTSemFastMutexRelease(pGVMM->UsedLock);
+    }
 
     return rc;
@@ -1269,4 +1275,6 @@
     if (RT_SUCCESS(rc))
     {
+        pGVM->gvmm.s.StatsSched.cWakeUpCalls++;
+
         /*
          * Signal the semaphore regardless of whether it's current blocked on it.
@@ -1277,8 +1285,14 @@
          * the it is flagged as halted in the VMM.
          */
-        rc = pGVM->gvmm.s.u64HaltExpire
-           ? VINF_SUCCESS
-           : VINF_GVM_NOT_BLOCKED;
-        ASMAtomicXchgU64(&pGVM->gvmm.s.u64HaltExpire, 0);
+        if (pGVM->gvmm.s.u64HaltExpire)
+        {
+            rc = VINF_SUCCESS;
+            ASMAtomicXchgU64(&pGVM->gvmm.s.u64HaltExpire, 0);
+        }
+        else
+        {
+            rc = VINF_GVM_NOT_BLOCKED;
+            pGVM->gvmm.s.StatsSched.cWakeUpNotHalted++;
+        }
 
         int rc2 = RTSemEventSignal(pGVM->gvmm.s.HaltEvent);
@@ -1290,5 +1304,6 @@
         Assert(ASMGetFlags() & X86_EFL_IF);
         const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
-        gvmmR0SchedDoWakeUps(pGVMM, u64Now);
+        pGVM->gvmm.s.StatsSched.cWakeUpWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
+
 
         rc2 = RTSemFastMutexRelease(pGVMM->UsedLock);
@@ -1327,4 +1342,5 @@
         rc = RTSemFastMutexRequest(pGVMM->UsedLock);
         AssertRC(rc);
+        pGVM->gvmm.s.StatsSched.cPollCalls++;
 
         Assert(ASMGetFlags() & X86_EFL_IF);
@@ -1332,5 +1348,5 @@
 
         if (!fYield)
-            gvmmR0SchedDoWakeUps(pGVMM, u64Now);
+            pGVM->gvmm.s.StatsSched.cPollWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
         else
         {
@@ -1346,2 +1362,104 @@
 }
 
+
+
+/**
+ * Retrieves the GVMM statistics visible to the caller.
+ *
+ * @returns VBox status code.
+ *
+ * @param   pStats      Where to put the statistics.
+ * @param   pSession    The current session.
+ * @param   pVM         The VM to obtain statistics for. Optional.
+ */
+GVMMR0DECL(int) GVMMR0QueryStatistics(PGVMMSTATS pStats, PSUPDRVSESSION pSession, PVM pVM)
+{
+    LogFlow(("GVMMR0QueryStatistics: pStats=%p pSession=%p pVM=%p\n", pStats, pSession, pVM));
+
+    /*
+     * Validate input.
+     */
+    AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+    AssertPtrReturn(pStats, VERR_INVALID_POINTER);
+    pStats->cVMs = 0; /* (crash before taking the sem...) */
+
+    /*
+     * Take the lock and get the VM statistics.
+     */
+    PGVMM pGVMM;
+    if (pVM)
+    {
+        PGVM pGVM;
+        int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, true /*fTakeUsedLock*/);
+        if (RT_FAILURE(rc))
+            return rc;
+        pStats->SchedVM = pGVM->gvmm.s.StatsSched;
+    }
+    else
+    {
+        GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
+        memset(&pStats->SchedVM, 0, sizeof(pStats->SchedVM));
+
+        int rc = RTSemFastMutexRequest(pGVMM->UsedLock);
+        AssertRCReturn(rc, rc);
+    }
+
+    /*
+     * Enumerate the VMs and add the ones visibile to the statistics.
+     */
+    pStats->cVMs = 0;
+    memset(&pStats->SchedSum, 0, sizeof(pStats->SchedSum));
+
+    for (unsigned i = pGVMM->iUsedHead;
+         i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
+         i = pGVMM->aHandles[i].iNext)
+    {
+        PGVM pGVM = pGVMM->aHandles[i].pGVM;
+        void *pvObj = pGVMM->aHandles[i].pvObj;
+        if (    VALID_PTR(pvObj)
+            &&  VALID_PTR(pGVM)
+            &&  pGVM->u32Magic == GVM_MAGIC
+            &&  RT_SUCCESS(SUPR0ObjVerifyAccess(pvObj, pSession, NULL)))
+        {
+            pStats->cVMs++;
+
+            pStats->SchedSum.cHaltCalls        += pGVM->gvmm.s.StatsSched.cHaltCalls;
+            pStats->SchedSum.cHaltBlocking     += pGVM->gvmm.s.StatsSched.cHaltBlocking;
+            pStats->SchedSum.cHaltTimeouts     += pGVM->gvmm.s.StatsSched.cHaltTimeouts;
+            pStats->SchedSum.cHaltNotBlocking  += pGVM->gvmm.s.StatsSched.cHaltNotBlocking;
+            pStats->SchedSum.cHaltWakeUps      += pGVM->gvmm.s.StatsSched.cHaltWakeUps;
+
+            pStats->SchedSum.cWakeUpCalls      += pGVM->gvmm.s.StatsSched.cWakeUpCalls;
+            pStats->SchedSum.cWakeUpNotHalted  += pGVM->gvmm.s.StatsSched.cWakeUpNotHalted;
+            pStats->SchedSum.cWakeUpWakeUps    += pGVM->gvmm.s.StatsSched.cWakeUpWakeUps;
+
+            pStats->SchedSum.cPollCalls        += pGVM->gvmm.s.StatsSched.cPollCalls;
+            pStats->SchedSum.cPollHalts        += pGVM->gvmm.s.StatsSched.cPollHalts;
+            pStats->SchedSum.cPollWakeUps      += pGVM->gvmm.s.StatsSched.cPollWakeUps;
+        }
+    }
+
+    RTSemFastMutexRelease(pGVMM->UsedLock);
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * VMMR0 request wrapper for GVMMR0QueryStatistics.
+ *
+ * @returns see GVMMR0QueryStatistics.
+ * @param   pVM             Pointer to the shared VM structure. Optional.
+ * @param   pReq            The request packet.
+ */
+GVMMR0DECL(int) GVMMR0QueryStatisticsReq(PVM pVM, PGVMMQUERYSTATISTICSSREQ pReq)
+{
+    /*
+     * Validate input and pass it on.
+     */
+    AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+    AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
+
+    return GVMMR0QueryStatistics(&pReq->Stats, pReq->pSession, pVM);
+}
+
Index: /trunk/src/VBox/VMM/VMMR0/GVMMR0Internal.h
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/GVMMR0Internal.h	(revision 5210)
+++ /trunk/src/VBox/VMM/VMMR0/GVMMR0Internal.h	(revision 5211)
@@ -43,4 +43,7 @@
     /** The APIC ID of the CPU that EMT was scheduled on the last time we checked. */
     uint8_t             iCpuEmt;
+
+    /** The scheduler statistics. */
+    GVMMSTATSSCHED      StatsSched;
 } GVMMPERVM;
 /** Pointer to the GVMM per VM data. */
Index: /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 5210)
+++ /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 5211)
@@ -687,5 +687,5 @@
                 return VERR_INVALID_PARAMETER;
             SUPR0Printf("-> GVMMR0CreateVMReq\n");
-            return GVMMR0CreateVMReq(pReqHdr);
+            return GVMMR0CreateVMReq((PGVMMCREATEVMREQ)pReqHdr);
 
         case VMMR0_DO_GVMM_DESTROY_VM:
@@ -708,4 +708,9 @@
                 return VERR_INVALID_PARAMETER;
             return GVMMR0SchedPoll(pVM, (bool)u64Arg);
+
+        case VMMR0_DO_GVMM_QUERY_STATISTICS:
+            if (u64Arg)
+                return VERR_INVALID_PARAMETER;
+            return GVMMR0QueryStatisticsReq(pVM, (PGVMMQUERYSTATISTICSSREQ)pReqHdr);
 
         /*
@@ -759,20 +764,36 @@
          */
         case VMMR0_DO_GMM_INITIAL_RESERVATION:
+            if (u64Arg)
+                return VERR_INVALID_PARAMETER;
             return GMMR0InitialReservationReq(pVM, (PGMMINITIALRESERVATIONREQ)pReqHdr);
         case VMMR0_DO_GMM_UPDATE_RESERVATION:
+            if (u64Arg)
+                return VERR_INVALID_PARAMETER;
             return GMMR0UpdateReservationReq(pVM, (PGMMUPDATERESERVATIONREQ)pReqHdr);
 
         case VMMR0_DO_GMM_ALLOCATE_PAGES:
+            if (u64Arg)
+                return VERR_INVALID_PARAMETER;
             return GMMR0AllocatePagesReq(pVM, (PGMMALLOCATEPAGESREQ)pReqHdr);
         case VMMR0_DO_GMM_FREE_PAGES:
+            if (u64Arg)
+                return VERR_INVALID_PARAMETER;
             return GMMR0FreePagesReq(pVM, (PGMMFREEPAGESREQ)pReqHdr);
         case VMMR0_DO_GMM_BALLOONED_PAGES:
+            if (u64Arg)
+                return VERR_INVALID_PARAMETER;
             return GMMR0BalloonedPagesReq(pVM, (PGMMBALLOONEDPAGESREQ)pReqHdr);
         case VMMR0_DO_GMM_DEFLATED_BALLOON:
+            if (pReqHdr)
+                return VERR_INVALID_PARAMETER;
             return GMMR0DeflatedBalloon(pVM, (uint32_t)u64Arg);
 
         case VMMR0_DO_GMM_MAP_UNMAP_CHUNK:
+            if (u64Arg)
+                return VERR_INVALID_PARAMETER;
             return GMMR0MapUnmapChunkReq(pVM, (PGMMMAPUNMAPCHUNKREQ)pReqHdr);
         case VMMR0_DO_GMM_SEED_CHUNK:
+            if (pReqHdr)
+                return VERR_INVALID_PARAMETER;
             return GMMR0SeedChunk(pVM, (RTR3PTR)u64Arg);
 
