summaryrefslogtreecommitdiff
path: root/src/VBox/VMM/VMMR3/STAM.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/STAM.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/STAM.cpp1222
1 files changed, 989 insertions, 233 deletions
diff --git a/src/VBox/VMM/VMMR3/STAM.cpp b/src/VBox/VMM/VMMR3/STAM.cpp
index 3d68f2f7..6b47ef64 100644
--- a/src/VBox/VMM/VMMR3/STAM.cpp
+++ b/src/VBox/VMM/VMMR3/STAM.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -58,12 +58,19 @@
#include <iprt/assert.h>
#include <iprt/asm.h>
-#include <iprt/alloc.h>
+#include <iprt/mem.h>
#include <iprt/stream.h>
#include <iprt/string.h>
/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** The maximum name length excluding the terminator. */
+#define STAM_MAX_NAME_LEN 239
+
+
+/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
@@ -71,8 +78,8 @@
*/
typedef struct STAMR3PRINTONEARGS
{
- PVM pVM;
- void *pvArg;
+ PUVM pUVM;
+ void *pvArg;
DECLCALLBACKMEMBER(void, pfnPrintf)(struct STAMR3PRINTONEARGS *pvArg, const char *pszFormat, ...);
} STAMR3PRINTONEARGS, *PSTAMR3PRINTONEARGS;
@@ -132,6 +139,9 @@ typedef struct STAMR0SAMPLE
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
+#ifdef STAM_WITH_LOOKUP_TREE
+static void stamR3LookupDestroyTree(PSTAMLOOKUP pRoot);
+#endif
static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc);
static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg);
@@ -150,9 +160,9 @@ static void stamR3Ring0StatsUpdateU(PUVM pUVM, const char *pszPa
static void stamR3Ring0StatsUpdateMultiU(PUVM pUVM, const char * const *papszExpressions, unsigned cExpressions);
#ifdef VBOX_WITH_DEBUGGER
-static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
+static FNDBGCCMD stamR3CmdStats;
static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...);
-static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
+static FNDBGCCMD stamR3CmdStatsReset;
#endif
@@ -249,8 +259,8 @@ static const STAMR0SAMPLE g_aGMMStats[] =
{ RT_UOFFSETOF(GMMSTATS, VMStats.enmPolicy), STAMTYPE_U32, STAMUNIT_NONE, "/GMM/VM/enmPolicy", "The current over-commit policy." },
{ RT_UOFFSETOF(GMMSTATS, VMStats.enmPriority), STAMTYPE_U32, STAMUNIT_NONE, "/GMM/VM/enmPriority", "The VM priority for arbitrating VMs in low and out of memory situation." },
{ RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fBallooningEnabled", "Whether ballooning is enabled or not." },
- { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fSharedPagingEnabled", "Whether shared paging is enabled or not." },
- { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fMayAllocate", "Whether the VM is allowed to allocate memory or not." },
+ { RT_UOFFSETOF(GMMSTATS, VMStats.fSharedPagingEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fSharedPagingEnabled", "Whether shared paging is enabled or not." },
+ { RT_UOFFSETOF(GMMSTATS, VMStats.fMayAllocate), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fMayAllocate", "Whether the VM is allowed to allocate memory or not." },
};
@@ -271,11 +281,38 @@ VMMR3DECL(int) STAMR3InitUVM(PUVM pUVM)
AssertRelease(sizeof(pUVM->stam.s) <= sizeof(pUVM->stam.padding));
/*
- * Setup any fixed pointers and offsets.
+ * Initialize the read/write lock and list.
*/
int rc = RTSemRWCreate(&pUVM->stam.s.RWSem);
AssertRCReturn(rc, rc);
+ RTListInit(&pUVM->stam.s.List);
+
+#ifdef STAM_WITH_LOOKUP_TREE
+ /*
+ * Initialize the root node.
+ */
+ PSTAMLOOKUP pRoot = (PSTAMLOOKUP)RTMemAlloc(sizeof(STAMLOOKUP));
+ if (!pRoot)
+ {
+ RTSemRWDestroy(pUVM->stam.s.RWSem);
+ pUVM->stam.s.RWSem = NIL_RTSEMRW;
+ return VERR_NO_MEMORY;
+ }
+ pRoot->pParent = NULL;
+ pRoot->papChildren = NULL;
+ pRoot->pDesc = NULL;
+ pRoot->cDescsInTree = 0;
+ pRoot->cChildren = 0;
+ pRoot->iParent = UINT16_MAX;
+ pRoot->off = 0;
+ pRoot->cch = 0;
+ pRoot->szName[0] = '\0';
+
+ pUVM->stam.s.pRoot = pRoot;
+#endif
+
+
/*
* Register the ring-0 statistics (GVMM/GMM).
*/
@@ -308,14 +345,19 @@ VMMR3DECL(void) STAMR3TermUVM(PUVM pUVM)
/*
* Free used memory and the RWLock.
*/
- PSTAMDESC pCur = pUVM->stam.s.pHead;
- while (pCur)
+ PSTAMDESC pCur, pNext;
+ RTListForEachSafe(&pUVM->stam.s.List, pCur, pNext, STAMDESC, ListEntry)
{
- void *pvFree = pCur;
- pCur = pCur->pNext;
- RTMemFree(pvFree);
+#ifdef STAM_WITH_LOOKUP_TREE
+ pCur->pLookup->pDesc = NULL;
+#endif
+ RTMemFree(pCur);
}
- pUVM->stam.s.pHead = NULL;
+
+#ifdef STAM_WITH_LOOKUP_TREE
+ stamR3LookupDestroyTree(pUVM->stam.s.pRoot);
+ pUVM->stam.s.pRoot = NULL;
+#endif
Assert(pUVM->stam.s.RWSem != NIL_RTSEMRW);
RTSemRWDestroy(pUVM->stam.s.RWSem);
@@ -348,6 +390,7 @@ VMMR3DECL(void) STAMR3TermUVM(PUVM pUVM)
VMMR3DECL(int) STAMR3RegisterU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
{
AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
}
@@ -450,14 +493,11 @@ VMMR3DECL(int) STAMR3RegisterVU(PUVM pUVM, void *pvSample, STAMTYPE enmType, ST
{
AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
- char *pszFormattedName;
- RTStrAPrintfV(&pszFormattedName, pszName, args);
- if (!pszFormattedName)
- return VERR_NO_MEMORY;
+ char szFormattedName[STAM_MAX_NAME_LEN + 8];
+ size_t cch = RTStrPrintfV(szFormattedName, sizeof(szFormattedName), pszName, args);
+ AssertReturn(cch <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
- int rc = STAMR3RegisterU(pUVM, pvSample, enmType, enmVisibility, pszFormattedName, enmUnit, pszDesc);
- RTStrFree(pszFormattedName);
- return rc;
+ return STAMR3RegisterU(pUVM, pvSample, enmType, enmVisibility, szFormattedName, enmUnit, pszDesc);
}
@@ -577,6 +617,624 @@ static int stamR3SlashCompare(const char *psz1, const char *psz2)
#endif /* VBOX_STRICT */
+#ifdef STAM_WITH_LOOKUP_TREE
+
+/**
+ * Compares a lookup node with a name.
+ *
+ * @returns like strcmp and memcmp.
+ * @param pNode The lookup node.
+ * @param pchName The name, not necessarily terminated.
+ * @param cchName The length of the name.
+ */
+DECL_FORCE_INLINE(int) stamR3LookupCmp(PSTAMLOOKUP pNode, const char *pchName, uint32_t cchName)
+{
+ uint32_t cchComp = RT_MIN(pNode->cch, cchName);
+ int iDiff = memcmp(pNode->szName, pchName, cchComp);
+ if (!iDiff && pNode->cch != cchName)
+ iDiff = pNode->cch > cchName ? 2 : -2;
+ return iDiff;
+}
+
+
+/**
+ * Creates a new lookup child node.
+ *
+ * @returns Pointer to the newly created lookup node.
+ * @param pParent The parent node.
+ * @param pchName The name (not necessarily terminated).
+ * @param cchName The length of the name.
+ * @param offName The offset of the node in a path.
+ * @param iChild Child index of a node that's before the one
+ * we're inserting (returned by
+ * stamR3LookupFindChild).
+ */
+static PSTAMLOOKUP stamR3LookupNewChild(PSTAMLOOKUP pParent, const char *pchName, uint32_t cchName, uint32_t offName,
+ uint32_t iChild)
+{
+ Assert(cchName <= UINT8_MAX);
+ Assert(offName <= UINT8_MAX);
+ Assert(iChild < UINT16_MAX);
+
+ /*
+ * Allocate a new entry.
+ */
+ PSTAMLOOKUP pNew = (PSTAMLOOKUP)RTMemAlloc(RT_OFFSETOF(STAMLOOKUP, szName[cchName + 1]));
+ if (!pNew)
+ return NULL;
+ pNew->pParent = pParent;
+ pNew->papChildren = NULL;
+ pNew->pDesc = NULL;
+ pNew->cDescsInTree = 0;
+ pNew->cChildren = 0;
+ pNew->cch = (uint16_t)cchName;
+ pNew->off = (uint16_t)offName;
+ memcpy(pNew->szName, pchName, cchName);
+ pNew->szName[cchName] = '\0';
+
+ /*
+ * Reallocate the array?
+ */
+ if (RT_IS_POWER_OF_TWO(pParent->cChildren))
+ {
+ uint32_t cNew = pParent->cChildren ? (uint32_t)pParent->cChildren * 2 : 8;
+ AssertReturnStmt(cNew <= 0x8000, RTMemFree(pNew), NULL);
+ void *pvNew = RTMemRealloc(pParent->papChildren, cNew * sizeof(pParent->papChildren[0]));
+ if (!pvNew)
+ {
+ RTMemFree(pNew);
+ return NULL;
+ }
+ pParent->papChildren = (PSTAMLOOKUP *)pvNew;
+ }
+
+ /*
+ * Find the exact insertion point using iChild as a very good clue from
+ * the find function.
+ */
+ if (!pParent->cChildren)
+ iChild = 0;
+ else
+ {
+ if (iChild >= pParent->cChildren)
+ iChild = pParent->cChildren - 1;
+ while ( iChild < pParent->cChildren
+ && stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName) < 0)
+ iChild++;
+ }
+
+ /*
+ * Insert it.
+ */
+ if (iChild < pParent->cChildren)
+ {
+ /* Do shift. */
+ uint32_t i = pParent->cChildren;
+ while (i > iChild)
+ {
+ PSTAMLOOKUP pNode = pParent->papChildren[i - 1];
+ pParent->papChildren[i] = pNode;
+ pNode->iParent = i;
+ i--;
+ }
+ }
+
+ pNew->iParent = iChild;
+ pParent->papChildren[iChild] = pNew;
+ pParent->cChildren++;
+
+ return pNew;
+}
+
+
+/**
+ * Looks up a child.
+ *
+ * @returns Pointer to child node if found, NULL if not.
+ * @param pParent The parent node.
+ * @param pchName The name (not necessarily terminated).
+ * @param cchName The length of the name.
+ * @param piChild Where to store a child index suitable for
+ * passing to stamR3LookupNewChild when NULL is
+ * returned.
+ */
+static PSTAMLOOKUP stamR3LookupFindChild(PSTAMLOOKUP pParent, const char *pchName, uint32_t cchName, uint32_t *piChild)
+{
+ uint32_t iChild = pParent->cChildren;
+ if (iChild > 4)
+ {
+ uint32_t iFirst = 0;
+ uint32_t iEnd = iChild;
+ iChild /= 2;
+ for (;;)
+ {
+ int iDiff = stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName);
+ if (!iDiff)
+ {
+ if (piChild)
+ *piChild = iChild;
+ return pParent->papChildren[iChild];
+ }
+
+ /* Split. */
+ if (iDiff < 0)
+ {
+ iFirst = iChild + 1;
+ if (iFirst >= iEnd)
+ {
+ if (piChild)
+ *piChild = iChild;
+ break;
+ }
+ }
+ else
+ {
+ if (iChild == iFirst)
+ {
+ if (piChild)
+ *piChild = iChild ? iChild - 1 : 0;
+ break;
+ }
+ iEnd = iChild;
+ }
+
+ /* Calc next child. */
+ iChild = (iEnd - iFirst) / 2 + iFirst;
+ }
+ return NULL;
+ }
+
+ /*
+ * Linear search.
+ */
+ while (iChild-- > 0)
+ {
+ int iDiff = stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName);
+ if (iDiff <= 0)
+ {
+ if (piChild)
+ *piChild = iChild;
+ return !iDiff ? pParent->papChildren[iChild] : NULL;
+ }
+ }
+ if (piChild)
+ *piChild = 0;
+ return NULL;
+}
+
+
+/**
+ * Find the next sample descriptor node.
+ *
+ * This is for use with insertion in the big list and pattern range lookups.
+ *
+ * @returns Pointer to the next sample descriptor. NULL if not found (i.e.
+ * we're at the end of the list).
+ * @param pLookup The current node.
+ */
+static PSTAMDESC stamR3LookupFindNextWithDesc(PSTAMLOOKUP pLookup)
+{
+ Assert(!pLookup->pDesc);
+ PSTAMLOOKUP pCur = pLookup;
+ uint32_t iCur = 0;
+ for (;;)
+ {
+ /*
+ * Check all children.
+ */
+ uint32_t cChildren = pCur->cChildren;
+ if (iCur < cChildren)
+ {
+ PSTAMLOOKUP *papChildren = pCur->papChildren;
+ do
+ {
+ PSTAMLOOKUP pChild = pCur->papChildren[iCur];
+ if (pChild->pDesc)
+ return pChild->pDesc;
+
+ if (pChild->cChildren > 0)
+ {
+ /* One level down. */
+ iCur = 0;
+ pCur = pChild;
+ break;
+ }
+ } while (++iCur < cChildren);
+ }
+ else
+ {
+ /*
+ * One level up, resuming after the current.
+ */
+ iCur = pCur->iParent + 1;
+ pCur = pCur->pParent;
+ if (!pCur)
+ return NULL;
+ }
+ }
+}
+
+
+/**
+ * Look up a sample descriptor by name.
+ *
+ * @returns Pointer to a sample descriptor.
+ * @param pRoot The root node.
+ * @param pszName The name to lookup.
+ */
+static PSTAMDESC stamR3LookupFindDesc(PSTAMLOOKUP pRoot, const char *pszName)
+{
+ Assert(!pRoot->pParent);
+ while (*pszName++ == '/')
+ {
+ const char *pszEnd = strchr(pszName, '/');
+ uint32_t cch = pszEnd ? pszEnd - pszName : (uint32_t)strlen(pszName);
+ PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pszName, cch, NULL);
+ if (!pChild)
+ break;
+ if (!pszEnd)
+ return pChild->pDesc;
+ pszName = pszEnd;
+ pRoot = pChild;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Finds the first sample descriptor for a given lookup range.
+ *
+ * This is for pattern range lookups.
+ *
+ * @returns Pointer to the first descriptor.
+ * @param pFirst The first node in the range.
+ * @param pLast The last node in the range.
+ */
+static PSTAMDESC stamR3LookupFindFirstDescForRange(PSTAMLOOKUP pFirst, PSTAMLOOKUP pLast)
+{
+ if (pFirst->pDesc)
+ return pFirst->pDesc;
+
+ PSTAMLOOKUP pCur = pFirst;
+ uint32_t iCur = 0;
+ for (;;)
+ {
+ uint32_t cChildren = pCur->cChildren;
+ if (iCur < pCur->cChildren)
+ {
+ /*
+ * Check all children.
+ */
+ PSTAMLOOKUP *papChildren = pCur->papChildren;
+ do
+ {
+ PSTAMLOOKUP pChild = pCur->papChildren[iCur];
+ if (pChild->pDesc)
+ return pChild->pDesc;
+ if (pChild->cChildren > 0)
+ {
+ /* One level down. */
+ iCur = 0;
+ pCur = pChild;
+ break;
+ }
+ if (pChild == pLast)
+ return NULL;
+ } while (++iCur < cChildren);
+ }
+ else
+ {
+ /*
+ * One level up, checking current and its 'older' sibilings.
+ */
+ if (pCur == pLast)
+ return NULL;
+ iCur = pCur->iParent + 1;
+ pCur = pCur->pParent;
+ if (!pCur)
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Finds the first sample descriptor for a given lookup range.
+ *
+ * This is for pattern range lookups.
+ *
+ * @returns Pointer to the first descriptor.
+ * @param pFirst The first node in the range.
+ * @param pLast The last node in the range.
+ */
+static PSTAMDESC stamR3LookupFindLastDescForRange(PSTAMLOOKUP pFirst, PSTAMLOOKUP pLast)
+{
+ PSTAMLOOKUP pCur = pLast;
+ uint32_t iCur = pCur->cChildren - 1;
+ for (;;)
+ {
+ if (iCur < pCur->cChildren)
+ {
+ /*
+ * Check children backwards, depth first.
+ */
+ PSTAMLOOKUP *papChildren = pCur->papChildren;
+ do
+ {
+ PSTAMLOOKUP pChild = pCur->papChildren[iCur];
+ if (pChild->cChildren > 0)
+ {
+ /* One level down. */
+ iCur = pChild->cChildren - 1;
+ pCur = pChild;
+ break;
+ }
+
+ if (pChild->pDesc)
+ return pChild->pDesc;
+ if (pChild == pFirst)
+ return NULL;
+ } while (iCur-- > 0); /* (underflow handled above) */
+ }
+ else
+ {
+ /*
+ * One level up, checking current and its 'older' sibilings.
+ */
+ if (pCur->pDesc)
+ return pCur->pDesc;
+ if (pCur == pFirst)
+ return NULL;
+ iCur = pCur->iParent - 1; /* (underflow handled above) */
+ pCur = pCur->pParent;
+ if (!pCur)
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Look up the first and last descriptors for a (single) pattern expression.
+ *
+ * This is used to optimize pattern enumerations and doesn't have to return 100%
+ * accurate results if that costs too much.
+ *
+ * @returns Pointer to the first descriptor in the range.
+ * @param pRoot The root node.
+ * @param pList The descriptor list anchor.
+ * @param pszPat The name patter to lookup.
+ * @param ppLastDesc Where to store the address of the last
+ * descriptor (approximate).
+ */
+static PSTAMDESC stamR3LookupFindPatternDescRange(PSTAMLOOKUP pRoot, PRTLISTANCHOR pList, const char *pszPat,
+ PSTAMDESC *ppLastDesc)
+{
+ Assert(!pRoot->pParent);
+
+ /*
+ * If there is an early enough wildcard, the whole list needs to be searched.
+ */
+ if ( pszPat[0] == '*' || pszPat[0] == '?'
+ || pszPat[1] == '*' || pszPat[1] == '?')
+ {
+ *ppLastDesc = RTListGetLast(pList, STAMDESC, ListEntry);
+ return RTListGetFirst(pList, STAMDESC, ListEntry);
+ }
+
+ /*
+ * All statistics starts with a slash.
+ */
+ while ( *pszPat++ == '/'
+ && pRoot->cDescsInTree > 0
+ && pRoot->cChildren > 0)
+ {
+ const char *pszEnd = strchr(pszPat, '/');
+ uint32_t cch = pszEnd ? pszEnd - pszPat : (uint32_t)strlen(pszPat);
+ if (!cch)
+ break;
+
+ const char *pszPat1 = (const char *)memchr(pszPat, '*', cch);
+ const char *pszPat2 = (const char *)memchr(pszPat, '?', cch);
+ if (pszPat1 || pszPat2)
+ {
+ /* We've narrowed it down to a sub-tree now. */
+ PSTAMLOOKUP pFirst = pRoot->papChildren[0];
+ PSTAMLOOKUP pLast = pRoot->papChildren[pRoot->cChildren - 1];
+ /** @todo narrow the range further if both pszPat1/2 != pszPat. */
+
+ *ppLastDesc = stamR3LookupFindLastDescForRange(pFirst, pLast);
+ return stamR3LookupFindFirstDescForRange(pFirst, pLast);
+ }
+
+ PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pszPat, cch, NULL);
+ if (!pChild)
+ break;
+
+ /* Advance */
+ if (!pszEnd)
+ return *ppLastDesc = pChild->pDesc;
+ pszPat = pszEnd;
+ pRoot = pChild;
+ }
+
+ /* No match. */
+ *ppLastDesc = NULL;
+ return NULL;
+}
+
+
+/**
+ * Increments the cDescInTree member of the given node an all ancestors.
+ *
+ * @param pLookup The lookup node.
+ */
+static void stamR3LookupIncUsage(PSTAMLOOKUP pLookup)
+{
+ Assert(pLookup->pDesc);
+
+ PSTAMLOOKUP pCur = pLookup;
+ while (pCur != NULL)
+ {
+ pCur->cDescsInTree++;
+ pCur = pCur->pParent;
+ }
+}
+
+
+/**
+ * Descrements the cDescInTree member of the given node an all ancestors.
+ *
+ * @param pLookup The lookup node.
+ */
+static void stamR3LookupDecUsage(PSTAMLOOKUP pLookup)
+{
+ Assert(!pLookup->pDesc);
+
+ PSTAMLOOKUP pCur = pLookup;
+ while (pCur != NULL)
+ {
+ Assert(pCur->cDescsInTree > 0);
+ pCur->cDescsInTree--;
+ pCur = pCur->pParent;
+ }
+}
+
+
+/**
+ * Frees empty lookup nodes if it's worth it.
+ *
+ * @param pLookup The lookup node.
+ */
+static void stamR3LookupMaybeFree(PSTAMLOOKUP pLookup)
+{
+ Assert(!pLookup->pDesc);
+
+ /*
+ * Free between two and three levels of nodes. Freeing too much most
+ * likely wasted effort since we're either going to repopluate the tree
+ * or quit the whole thing.
+ */
+ if (pLookup->cDescsInTree > 0)
+ return;
+
+ PSTAMLOOKUP pCur = pLookup->pParent;
+ if (!pCur)
+ return;
+ if (pCur->cDescsInTree > 0)
+ return;
+ PSTAMLOOKUP pParent = pCur->pParent;
+ if (pParent)
+ return;
+
+ if (pParent->cDescsInTree == 0 && pParent->pParent)
+ {
+ pCur = pParent;
+ pParent = pCur->pParent;
+ }
+
+ /*
+ * Remove pCur from pParent.
+ */
+ PSTAMLOOKUP *papChildren = pParent->papChildren;
+ uint32_t cChildren = --pParent->cChildren;
+ for (uint32_t i = pCur->iParent; i < cChildren; i++)
+ {
+ PSTAMLOOKUP pChild = papChildren[i + 1];
+ pChild->iParent = i;
+ papChildren[i] = pChild;
+ }
+ pCur->pParent = NULL;
+
+ /*
+ * Destroy pCur.
+ */
+ stamR3LookupDestroyTree(pCur);
+}
+
+
+/**
+ * Destroys a lookup tree.
+ *
+ * This is used by STAMR3Term as well as stamR3LookupMaybeFree.
+ *
+ * @param pRoot The root of the tree (must have no parent).
+ */
+static void stamR3LookupDestroyTree(PSTAMLOOKUP pRoot)
+{
+ Assert(pRoot); Assert(!pRoot->pParent);
+ PSTAMLOOKUP pCur = pRoot;
+ for (;;)
+ {
+ uint32_t i = pCur->cChildren;
+ if (i > 0)
+ {
+ /*
+ * Push child (with leaf optimization).
+ */
+ PSTAMLOOKUP pChild = pCur->papChildren[--i];
+ if (pChild->cChildren != 0)
+ pCur = pChild;
+ else
+ {
+ /* free leaves. */
+ for (;;)
+ {
+ if (pChild->papChildren)
+ {
+ RTMemFree(pChild->papChildren);
+ pChild->papChildren = NULL;
+ }
+ RTMemFree(pChild);
+ pCur->papChildren[i] = NULL;
+
+ /* next */
+ if (i == 0)
+ {
+ pCur->cChildren = 0;
+ break;
+ }
+ pChild = pCur->papChildren[--i];
+ if (pChild->cChildren != 0)
+ {
+ pCur->cChildren = i + 1;
+ pCur = pChild;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Pop and free current.
+ */
+ Assert(!pCur->pDesc);
+
+ PSTAMLOOKUP pParent = pCur->pParent;
+ Assert(pCur->iParent == (pParent ? pParent->cChildren - 1 : UINT16_MAX));
+
+ RTMemFree(pCur->papChildren);
+ pCur->papChildren = NULL;
+ RTMemFree(pCur);
+
+ pCur = pParent;
+ if (!pCur)
+ break;
+ pCur->papChildren[--pCur->cChildren] = NULL;
+ }
+ }
+}
+
+#endif /* STAM_WITH_LOOKUP_TREE */
+
+
+
/**
* Internal worker for the different register calls.
*
@@ -596,14 +1254,65 @@ static int stamR3SlashCompare(const char *psz1, const char *psz2)
static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
{
+ AssertReturn(pszName[0] == '/', VERR_INVALID_NAME);
+ AssertReturn(pszName[1] != '/' && pszName[1], VERR_INVALID_NAME);
+ uint32_t const cchName = (uint32_t)strlen(pszName);
+ AssertReturn(cchName <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
+ AssertReturn(pszName[cchName - 1] != '/', VERR_INVALID_NAME);
+ AssertReturn(memchr(pszName, '\\', cchName) == NULL, VERR_INVALID_NAME);
+
STAM_LOCK_WR(pUVM);
/*
- * Check if exists.
+ * Look up the tree location, populating the lookup tree as we walk it.
*/
- PSTAMDESC pPrev = NULL;
- PSTAMDESC pCur = pUVM->stam.s.pHead;
- while (pCur)
+#ifdef STAM_WITH_LOOKUP_TREE
+ PSTAMLOOKUP pLookup = pUVM->stam.s.pRoot; Assert(pLookup);
+ uint32_t offName = 1;
+ for (;;)
+ {
+ /* Get the next part of the path. */
+ const char *pszStart = &pszName[offName];
+ const char *pszEnd = strchr(pszStart, '/');
+ uint32_t cch = pszEnd ? (uint32_t)(pszEnd - pszStart) : cchName - offName;
+ if (cch == 0)
+ {
+ STAM_UNLOCK_WR(pUVM);
+ AssertMsgFailed(("No double or trailing slashes are allowed: '%s'\n", pszName));
+ return VERR_INVALID_NAME;
+ }
+
+ /* Do the looking up. */
+ uint32_t iChild = 0;
+ PSTAMLOOKUP pChild = stamR3LookupFindChild(pLookup, pszStart, cch, &iChild);
+ if (!pChild)
+ {
+ pChild = stamR3LookupNewChild(pLookup, pszStart, cch, offName, iChild);
+ if (!pChild)
+ {
+ STAM_UNLOCK_WR(pUVM);
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ /* Advance. */
+ pLookup = pChild;
+ if (!pszEnd)
+ break;
+ offName += cch + 1;
+ }
+ if (pLookup->pDesc)
+ {
+ STAM_UNLOCK_WR(pUVM);
+ AssertMsgFailed(("Duplicate sample name: %s\n", pszName));
+ return VERR_ALREADY_EXISTS;
+ }
+
+ PSTAMDESC pCur = stamR3LookupFindNextWithDesc(pLookup);
+
+#else
+ PSTAMDESC pCur;
+ RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
{
int iDiff = strcmp(pCur->pszName, pszName);
/* passed it */
@@ -616,24 +1325,24 @@ static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfn
AssertMsgFailed(("Duplicate sample name: %s\n", pszName));
return VERR_ALREADY_EXISTS;
}
-
- /* next */
- pPrev = pCur;
- pCur = pCur->pNext;
}
+#endif
/*
* Check that the name doesn't screw up sorting order when taking
* slashes into account. The QT4 GUI makes some assumptions.
* Problematic chars are: !"#$%&'()*+,-.
*/
+#ifdef VBOX_STRICT
Assert(pszName[0] == '/');
- if (pPrev)
- Assert(stamR3SlashCompare(pPrev->pszName, pszName) < 0);
- if (pCur)
- Assert(stamR3SlashCompare(pCur->pszName, pszName) > 0);
+ PSTAMDESC pPrev = pCur
+ ? RTListGetPrev(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
+ : RTListGetLast(&pUVM->stam.s.List, STAMDESC, ListEntry);
+ Assert(!pPrev || strcmp(pszName, pPrev->pszName) > 0);
+ Assert(!pCur || strcmp(pszName, pCur->pszName) < 0);
+ Assert(!pPrev || stamR3SlashCompare(pPrev->pszName, pszName) < 0);
+ Assert(!pCur || stamR3SlashCompare(pCur->pszName, pszName) > 0);
-#ifdef VBOX_STRICT
/*
* Check alignment requirements.
*/
@@ -688,12 +1397,11 @@ static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfn
* Create a new node and insert it at the current location.
*/
int rc;
- size_t cchName = strlen(pszName) + 1;
- size_t cchDesc = pszDesc ? strlen(pszDesc) + 1 : 0;
- PSTAMDESC pNew = (PSTAMDESC)RTMemAlloc(sizeof(*pNew) + cchName + cchDesc);
+ size_t cbDesc = pszDesc ? strlen(pszDesc) + 1 : 0;
+ PSTAMDESC pNew = (PSTAMDESC)RTMemAlloc(sizeof(*pNew) + cchName + 1 + cbDesc);
if (pNew)
{
- pNew->pszName = (char *)memcpy((char *)(pNew + 1), pszName, cchName);
+ pNew->pszName = (char *)memcpy((char *)(pNew + 1), pszName, cchName + 1);
pNew->enmType = enmType;
pNew->enmVisibility = enmVisibility;
if (enmType != STAMTYPE_CALLBACK)
@@ -707,13 +1415,18 @@ static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfn
pNew->enmUnit = enmUnit;
pNew->pszDesc = NULL;
if (pszDesc)
- pNew->pszDesc = (char *)memcpy((char *)(pNew + 1) + cchName, pszDesc, cchDesc);
+ pNew->pszDesc = (char *)memcpy((char *)(pNew + 1) + cchName + 1, pszDesc, cbDesc);
- pNew->pNext = pCur;
- if (pPrev)
- pPrev->pNext = pNew;
+ if (pCur)
+ RTListNodeInsertBefore(&pCur->ListEntry, &pNew->ListEntry);
else
- pUVM->stam.s.pHead = pNew;
+ RTListAppend(&pUVM->stam.s.List, &pNew->ListEntry);
+
+#ifdef STAM_WITH_LOOKUP_TREE
+ pNew->pLookup = pLookup;
+ pLookup->pDesc = pNew;
+ stamR3LookupIncUsage(pLookup);
+#endif
stamR3ResetOne(pNew, pUVM->pVM);
rc = VINF_SUCCESS;
@@ -727,7 +1440,29 @@ static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfn
/**
- * Deregisters a sample previously registered by STAR3Register().
+ * Destroys the statistics descriptor, unlinking it and freeing all resources.
+ *
+ * @returns VINF_SUCCESS
+ * @param pUVM Pointer to the user mode VM structure.
+ * @param pCur The descriptor to destroy.
+ */
+static int stamR3DestroyDesc(PUVM pUVM, PSTAMDESC pCur)
+{
+ RTListNodeRemove(&pCur->ListEntry);
+#ifdef STAM_WITH_LOOKUP_TREE
+ pCur->pLookup->pDesc = NULL; /** @todo free lookup nodes once it's working. */
+ stamR3LookupDecUsage(pCur->pLookup);
+ stamR3LookupMaybeFree(pCur->pLookup);
+#endif
+ RTMemFree(pCur);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Deregisters a sample previously registered by STAR3Register() given its
+ * address.
*
* This is intended used for devices which can be unplugged and for
* temporary samples.
@@ -736,36 +1471,69 @@ static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfn
* @param pUVM Pointer to the user mode VM structure.
* @param pvSample Pointer to the sample registered with STAMR3Register().
*/
-VMMR3DECL(int) STAMR3DeregisterU(PUVM pUVM, void *pvSample)
+VMMR3DECL(int) STAMR3DeregisterByAddr(PUVM pUVM, void *pvSample)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+
+ /* This is a complete waste of time when shutting down. */
+ VMSTATE enmState = VMR3GetStateU(pUVM);
+ if (enmState >= VMSTATE_DESTROYING)
+ return VINF_SUCCESS;
+
STAM_LOCK_WR(pUVM);
/*
* Search for it.
*/
int rc = VERR_INVALID_HANDLE;
- PSTAMDESC pPrev = NULL;
- PSTAMDESC pCur = pUVM->stam.s.pHead;
- while (pCur)
+ PSTAMDESC pCur, pNext;
+ RTListForEachSafe(&pUVM->stam.s.List, pCur, pNext, STAMDESC, ListEntry)
{
if (pCur->u.pv == pvSample)
+ rc = stamR3DestroyDesc(pUVM, pCur);
+ }
+
+ STAM_UNLOCK_WR(pUVM);
+ return rc;
+}
+
+
+/**
+ * Worker for STAMR3Deregister, STAMR3DeregisterV and STAMR3DeregisterF.
+ *
+ * @returns VBox status code.
+ * @retval VWRN_NOT_FOUND if no matching names found.
+ *
+ * @param pUVM Pointer to the user mode VM structure.
+ * @param pszPat The name pattern.
+ */
+static int stamR3DeregisterByPattern(PUVM pUVM, const char *pszPat)
+{
+ Assert(!strchr(pszPat, '|')); /* single pattern! */
+
+ int rc = VWRN_NOT_FOUND;
+ STAM_LOCK_WR(pUVM);
+
+ PSTAMDESC pLast;
+ PSTAMDESC pCur = stamR3LookupFindPatternDescRange(pUVM->stam.s.pRoot, &pUVM->stam.s.List, pszPat, &pLast);
+ if (pCur)
+ {
+ for (;;)
{
- void *pvFree = pCur;
- pCur = pCur->pNext;
- if (pPrev)
- pPrev->pNext = pCur;
- else
- pUVM->stam.s.pHead = pCur;
+ PSTAMDESC pNext = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
- RTMemFree(pvFree);
- rc = VINF_SUCCESS;
- continue;
- }
+ if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
+ rc = stamR3DestroyDesc(pUVM, pCur);
- /* next */
- pPrev = pCur;
- pCur = pCur->pNext;
+ /* advance. */
+ if (pCur == pLast)
+ break;
+ pCur = pNext;
+ }
+ Assert(pLast);
}
+ else
+ Assert(!pLast);
STAM_UNLOCK_WR(pUVM);
return rc;
@@ -773,18 +1541,71 @@ VMMR3DECL(int) STAMR3DeregisterU(PUVM pUVM, void *pvSample)
/**
- * Deregisters a sample previously registered by STAR3Register().
+ * Deregister zero or more samples given a (single) pattern matching their
+ * names.
*
- * This is intended used for devices which can be unplugged and for
- * temporary samples.
+ * @returns VBox status.
+ * @param pUVM Pointer to the user mode VM structure.
+ * @param pszPat The name pattern.
+ * @sa STAMR3DeregisterF, STAMR3DeregisterV
+ */
+VMMR3DECL(int) STAMR3Deregister(PUVM pUVM, const char *pszPat)
+{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+
+ /* This is a complete waste of time when shutting down. */
+ VMSTATE enmState = VMR3GetStateU(pUVM);
+ if (enmState >= VMSTATE_DESTROYING)
+ return VINF_SUCCESS;
+
+ return stamR3DeregisterByPattern(pUVM, pszPat);
+}
+
+
+/**
+ * Deregister zero or more samples given a (single) pattern matching their
+ * names.
*
* @returns VBox status.
- * @param pVM Pointer to the VM.
- * @param pvSample Pointer to the sample registered with STAMR3Register().
+ * @param pUVM Pointer to the user mode VM structure.
+ * @param pszPatFmt The name pattern format string.
+ * @param ... Format string arguments.
+ * @sa STAMR3Deregister, STAMR3DeregisterV
*/
-VMMR3DECL(int) STAMR3Deregister(PVM pVM, void *pvSample)
+VMMR3DECL(int) STAMR3DeregisterF(PUVM pUVM, const char *pszPatFmt, ...)
{
- return STAMR3DeregisterU(pVM->pUVM, pvSample);
+ va_list va;
+ va_start(va, pszPatFmt);
+ int rc = STAMR3DeregisterV(pUVM, pszPatFmt, va);
+ va_end(va);
+ return rc;
+}
+
+
+/**
+ * Deregister zero or more samples given a (single) pattern matching their
+ * names.
+ *
+ * @returns VBox status.
+ * @param pUVM Pointer to the user mode VM structure.
+ * @param pszPatFmt The name pattern format string.
+ * @param va Format string arguments.
+ * @sa STAMR3Deregister, STAMR3DeregisterF
+ */
+VMMR3DECL(int) STAMR3DeregisterV(PUVM pUVM, const char *pszPatFmt, va_list va)
+{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+
+ /* This is a complete waste of time when shutting down. */
+ VMSTATE enmState = VMR3GetStateU(pUVM);
+ if (enmState >= VMSTATE_DESTROYING)
+ return VINF_SUCCESS;
+
+ char szPat[STAM_MAX_NAME_LEN + 8];
+ size_t cchPat = RTStrPrintfV(szPat, sizeof(szPat), pszPatFmt, va);
+ AssertReturn(cchPat <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
+
+ return stamR3DeregisterByPattern(pUVM, szPat);
}
@@ -793,13 +1614,16 @@ VMMR3DECL(int) STAMR3Deregister(PVM pVM, void *pvSample)
* It's possible to select a subset of the samples.
*
* @returns VBox status. (Basically, it cannot fail.)
- * @param pVM Pointer to the VM.
+ * @param pUVM The user mode VM handle.
* @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
* If NULL all samples are reset.
* @remarks Don't confuse this with the other 'XYZR3Reset' methods, it's not called at VM reset.
*/
-VMMR3DECL(int) STAMR3ResetU(PUVM pUVM, const char *pszPat)
+VMMR3DECL(int) STAMR3Reset(PUVM pUVM, const char *pszPat)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
+
int rc = VINF_SUCCESS;
/* ring-0 */
@@ -875,22 +1699,6 @@ VMMR3DECL(int) STAMR3ResetU(PUVM pUVM, const char *pszPat)
/**
- * Resets statistics for the specified VM.
- * It's possible to select a subset of the samples.
- *
- * @returns VBox status. (Basically, it cannot fail.)
- * @param pVM Pointer to the VM.
- * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
- * If NULL all samples are reset.
- * @remarks Don't confuse this with the other 'XYZR3Reset' methods, it's not called at VM reset.
- */
-VMMR3DECL(int) STAMR3Reset(PVM pVM, const char *pszPat)
-{
- return STAMR3ResetU(pVM->pUVM, pszPat);
-}
-
-
-/**
* Resets one statistics sample.
* Callback for stamR3EnumU().
*
@@ -975,7 +1783,7 @@ static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg)
* It's possible to select a subset of the samples.
*
* @returns VBox status. (Basically, it cannot fail.)
- * @param pUVM Pointer to the user mode VM structure.
+ * @param pUVM The user mode VM handle.
* @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
* If NULL all samples are reset.
* @param fWithDesc Whether to include the descriptions.
@@ -985,8 +1793,11 @@ static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg)
* The returned pointer must be freed by calling STAMR3SnapshotFree().
* @param pcchSnapshot Where to store the size of the snapshot data. (Excluding the trailing '\0')
*/
-VMMR3DECL(int) STAMR3SnapshotU(PUVM pUVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
+VMMR3DECL(int) STAMR3Snapshot(PUVM pUVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
+
STAMR3SNAPSHOTONE State = { NULL, NULL, NULL, pUVM->pVM, 0, VINF_SUCCESS, fWithDesc };
/*
@@ -1022,28 +1833,6 @@ VMMR3DECL(int) STAMR3SnapshotU(PUVM pUVM, const char *pszPat, char **ppszSnapsho
/**
- * Get a snapshot of the statistics.
- * It's possible to select a subset of the samples.
- *
- * @returns VBox status. (Basically, it cannot fail.)
- * @param pVM Pointer to the VM.
- * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
- * If NULL all samples are reset.
- * @param fWithDesc Whether to include the descriptions.
- * @param ppszSnapshot Where to store the pointer to the snapshot data.
- * The format of the snapshot should be XML, but that will have to be discussed
- * when this function is implemented.
- * The returned pointer must be freed by calling STAMR3SnapshotFree().
- * @param pcchSnapshot Where to store the size of the snapshot data.
- * (Excluding the trailing '\\0')
- */
-VMMR3DECL(int) STAMR3Snapshot(PVM pVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
-{
- return STAMR3SnapshotU(pVM->pUVM, pszPat, ppszSnapshot, pcchSnapshot, fWithDesc);
-}
-
-
-/**
* stamR3EnumU callback employed by STAMR3Snapshot.
*
* @returns VBox status code, but it's interpreted as 0 == success / !0 == failure by enmR3Enum.
@@ -1282,11 +2071,11 @@ static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat,
* Releases a statistics snapshot returned by STAMR3Snapshot().
*
* @returns VBox status.
- * @param pUVM Pointer to the user mode VM structure.
+ * @param pUVM The user mode VM handle.
* @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
* NULL is allowed.
*/
-VMMR3DECL(int) STAMR3SnapshotFreeU(PUVM pUVM, char *pszSnapshot)
+VMMR3DECL(int) STAMR3SnapshotFree(PUVM pUVM, char *pszSnapshot)
{
if (!pszSnapshot)
RTMemFree(pszSnapshot);
@@ -1296,20 +2085,6 @@ VMMR3DECL(int) STAMR3SnapshotFreeU(PUVM pUVM, char *pszSnapshot)
/**
- * Releases a statistics snapshot returned by STAMR3Snapshot().
- *
- * @returns VBox status.
- * @param pVM Pointer to the VM.
- * @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
- * NULL is allowed.
- */
-VMMR3DECL(int) STAMR3SnapshotFree(PVM pVM, char *pszSnapshot)
-{
- return STAMR3SnapshotFreeU(pVM->pUVM, pszSnapshot);
-}
-
-
-/**
* Dumps the selected statistics to the log.
*
* @returns VBox status.
@@ -1317,10 +2092,13 @@ VMMR3DECL(int) STAMR3SnapshotFree(PVM pVM, char *pszSnapshot)
* @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
* If NULL all samples are written to the log.
*/
-VMMR3DECL(int) STAMR3DumpU(PUVM pUVM, const char *pszPat)
+VMMR3DECL(int) STAMR3Dump(PUVM pUVM, const char *pszPat)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
+
STAMR3PRINTONEARGS Args;
- Args.pVM = pUVM->pVM;
+ Args.pUVM = pUVM;
Args.pvArg = NULL;
Args.pfnPrintf = stamR3EnumLogPrintf;
@@ -1330,20 +2108,6 @@ VMMR3DECL(int) STAMR3DumpU(PUVM pUVM, const char *pszPat)
/**
- * Dumps the selected statistics to the log.
- *
- * @returns VBox status.
- * @param pVM Pointer to the VM.
- * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
- * If NULL all samples are written to the log.
- */
-VMMR3DECL(int) STAMR3Dump(PVM pVM, const char *pszPat)
-{
- return STAMR3DumpU(pVM->pUVM, pszPat);
-}
-
-
-/**
* Prints to the log.
*
* @param pArgs Pointer to the print one argument structure.
@@ -1368,10 +2132,13 @@ static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pArgs, const c
* @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
* If NULL all samples are written to the log.
*/
-VMMR3DECL(int) STAMR3DumpToReleaseLogU(PUVM pUVM, const char *pszPat)
+VMMR3DECL(int) STAMR3DumpToReleaseLog(PUVM pUVM, const char *pszPat)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
+
STAMR3PRINTONEARGS Args;
- Args.pVM = pUVM->pVM;
+ Args.pUVM = pUVM;
Args.pvArg = NULL;
Args.pfnPrintf = stamR3EnumRelLogPrintf;
@@ -1379,21 +2146,6 @@ VMMR3DECL(int) STAMR3DumpToReleaseLogU(PUVM pUVM, const char *pszPat)
return VINF_SUCCESS;
}
-
-/**
- * Dumps the selected statistics to the release log.
- *
- * @returns VBox status.
- * @param pVM Pointer to the VM.
- * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
- * If NULL all samples are written to the log.
- */
-VMMR3DECL(int) STAMR3DumpToReleaseLog(PVM pVM, const char *pszPat)
-{
- return STAMR3DumpToReleaseLogU(pVM->pUVM, pszPat);
-}
-
-
/**
* Prints to the release log.
*
@@ -1415,14 +2167,17 @@ static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pArgs, cons
* Prints the selected statistics to standard out.
*
* @returns VBox status.
- * @param pVM Pointer to the VM.
+ * @param pUVM The user mode VM handle.
* @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
* If NULL all samples are reset.
*/
-VMMR3DECL(int) STAMR3PrintU(PUVM pUVM, const char *pszPat)
+VMMR3DECL(int) STAMR3Print(PUVM pUVM, const char *pszPat)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
+
STAMR3PRINTONEARGS Args;
- Args.pVM = pUVM->pVM;
+ Args.pUVM = pUVM;
Args.pvArg = NULL;
Args.pfnPrintf = stamR3EnumPrintf;
@@ -1432,20 +2187,6 @@ VMMR3DECL(int) STAMR3PrintU(PUVM pUVM, const char *pszPat)
/**
- * Prints the selected statistics to standard out.
- *
- * @returns VBox status.
- * @param pVM Pointer to the VM.
- * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
- * If NULL all samples are reset.
- */
-VMMR3DECL(int) STAMR3Print(PVM pVM, const char *pszPat)
-{
- return STAMR3PrintU(pVM->pUVM, pszPat);
-}
-
-
-/**
* Prints to stdout.
*
* @param pArgs Pointer to the print one argument structure.
@@ -1507,7 +2248,7 @@ static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg)
case STAMTYPE_CALLBACK:
{
char szBuf[512];
- pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
+ pDesc->u.Callback.pfnPrint(pArgs->pUVM->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, szBuf, STAMR3GetUnit(pDesc->enmUnit));
break;
}
@@ -1589,13 +2330,16 @@ static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg)
*
* @returns Whatever the callback returns.
*
- * @param pUVM Pointer to the user mode VM structure.
+ * @param pUVM The user mode VM handle.
* @param pszPat The pattern to match samples.
* @param pfnEnum The callback function.
* @param pvUser The pvUser argument of the callback function.
*/
-VMMR3DECL(int) STAMR3EnumU(PUVM pUVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
+VMMR3DECL(int) STAMR3Enum(PUVM pUVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
+
STAMR3ENUMONEARGS Args;
Args.pVM = pUVM->pVM;
Args.pfnEnum = pfnEnum;
@@ -1606,22 +2350,6 @@ VMMR3DECL(int) STAMR3EnumU(PUVM pUVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum,
/**
- * Enumerate the statistics by the means of a callback function.
- *
- * @returns Whatever the callback returns.
- *
- * @param pVM Pointer to the VM.
- * @param pszPat The pattern to match samples.
- * @param pfnEnum The callback function.
- * @param pvUser The pvUser argument of the callback function.
- */
-VMMR3DECL(int) STAMR3Enum(PVM pVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
-{
- return STAMR3EnumU(pVM->pUVM, pszPat, pfnEnum, pvUser);
-}
-
-
-/**
* Callback function for STARTR3Enum().
*
* @returns whatever the callback returns.
@@ -1648,6 +2376,19 @@ static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg)
/**
+ * Checks if the string contains a pattern expression or not.
+ *
+ * @returns true / false.
+ * @param pszPat The potential pattern.
+ */
+static bool stamR3IsPattern(const char *pszPat)
+{
+ return strchr(pszPat, '*') != NULL
+ || strchr(pszPat, '?') != NULL;
+}
+
+
+/**
* Match a name against an array of patterns.
*
* @returns true if it matches, false if it doesn't match.
@@ -1745,9 +2486,10 @@ static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
{
int rc = VINF_SUCCESS;
+ PSTAMDESC pCur;
/*
- * All
+ * All.
*/
if (!pszPat || !*pszPat || !strcmp(pszPat, "*"))
{
@@ -1755,15 +2497,11 @@ static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
stamR3Ring0StatsUpdateU(pUVM, "*");
STAM_LOCK_RD(pUVM);
- PSTAMDESC pCur = pUVM->stam.s.pHead;
- while (pCur)
+ RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
{
rc = pfnCallback(pCur, pvArg);
if (rc)
break;
-
- /* next */
- pCur = pCur->pNext;
}
STAM_UNLOCK_RD(pUVM);
}
@@ -1777,16 +2515,48 @@ static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
stamR3Ring0StatsUpdateU(pUVM, pszPat);
STAM_LOCK_RD(pUVM);
- /** @todo This needs to be optimized since the GUI is using this path for the VM info dialog.
- * Note that it's doing exact matching. Organizing the samples in a tree would speed up thing
- * no end (at least for debug and profile builds). */
- for (PSTAMDESC pCur = pUVM->stam.s.pHead; pCur; pCur = pCur->pNext)
+#ifdef STAM_WITH_LOOKUP_TREE
+ if (!stamR3IsPattern(pszPat))
+ {
+ pCur = stamR3LookupFindDesc(pUVM->stam.s.pRoot, pszPat);
+ if (pCur)
+ rc = pfnCallback(pCur, pvArg);
+ }
+ else
+ {
+ PSTAMDESC pLast;
+ pCur = stamR3LookupFindPatternDescRange(pUVM->stam.s.pRoot, &pUVM->stam.s.List, pszPat, &pLast);
+ if (pCur)
+ {
+ for (;;)
+ {
+ if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
+ {
+ rc = pfnCallback(pCur, pvArg);
+ if (rc)
+ break;
+ }
+ if (pCur == pLast)
+ break;
+ pCur = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
+ }
+ Assert(pLast);
+ }
+ else
+ Assert(!pLast);
+
+ }
+#else
+ RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
+ {
if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
{
rc = pfnCallback(pCur, pvArg);
if (rc)
break;
}
+ }
+#endif
STAM_UNLOCK_RD(pUVM);
}
@@ -1812,13 +2582,15 @@ static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
STAM_LOCK_RD(pUVM);
unsigned iExpression = 0;
- for (PSTAMDESC pCur = pUVM->stam.s.pHead; pCur; pCur = pCur->pNext)
+ RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
+ {
if (stamR3MultiMatch(papszExpressions, cExpressions, &iExpression, pCur->pszName))
{
rc = pfnCallback(pCur, pvArg);
if (rc)
break;
}
+ }
STAM_UNLOCK_RD(pUVM);
RTMemTmpFree(papszExpressions);
@@ -2003,30 +2775,22 @@ VMMR3DECL(const char *) STAMR3GetUnit(STAMUNIT enmUnit)
#ifdef VBOX_WITH_DEBUGGER
/**
- * The '.stats' command.
- *
- * @returns VBox status.
- * @param pCmd Pointer to the command descriptor (as registered).
- * @param pCmdHlp Pointer to command helper functions.
- * @param pVM Pointer to the current VM (if any).
- * @param paArgs Pointer to (readonly) array of arguments.
- * @param cArgs Number of arguments in the array.
+ * @callback_method_impl{FNDBGCCMD, The '.stats' command.}
*/
-static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
+static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
{
/*
* Validate input.
*/
- DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
- PUVM pUVM = pVM->pUVM;
- if (!pUVM->stam.s.pHead)
+ DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
+ if (RTListIsEmpty(&pUVM->stam.s.List))
return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
/*
* Do the printing.
*/
STAMR3PRINTONEARGS Args;
- Args.pVM = pVM;
+ Args.pUVM = pUVM;
Args.pvArg = pCmdHlp;
Args.pfnPrintf = stamR3EnumDbgfPrintf;
@@ -2054,29 +2818,21 @@ static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const
/**
- * The '.statsreset' command.
- *
- * @returns VBox status.
- * @param pCmd Pointer to the command descriptor (as registered).
- * @param pCmdHlp Pointer to command helper functions.
- * @param pVM Pointer to the current VM (if any).
- * @param paArgs Pointer to (readonly) array of arguments.
- * @param cArgs Number of arguments in the array.
+ * @callback_method_impl{FNDBGCCMD, The '.statsreset' command.}
*/
-static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
+static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
{
/*
* Validate input.
*/
- DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
- PUVM pUVM = pVM->pUVM;
- if (!pUVM->stam.s.pHead)
+ DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
+ if (RTListIsEmpty(&pUVM->stam.s.List))
return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
/*
* Execute reset.
*/
- int rc = STAMR3ResetU(pUVM, cArgs ? paArgs[0].u.pszString : NULL);
+ int rc = STAMR3Reset(pUVM, cArgs ? paArgs[0].u.pszString : NULL);
if (RT_SUCCESS(rc))
return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "STAMR3ResetU");
return DBGCCmdHlpPrintf(pCmdHlp, "Statistics have been reset.\n");