VBoxDbgStatsQt4.cpp revision 6328c3cf596acc5e23d882fe7d78751867c528fd
/* $Id$ */
/** @file
* VBox Debugger GUI - Statistics.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DBGG
#include "VBoxDbgStatsQt4.h"
#include <QLocale>
#include <QPushButton>
#include <QSpinBox>
#include <QLabel>
#include <QClipboard>
#include <QApplication>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <stdio.h> //remove me
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The number of column. */
#define DBGGUI_STATS_COLUMNS 9
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* The state of a statistics sample node.
*
* This is used for two pass refresh (1. get data, 2. update the view) and
* for saving the result of a diff.
*/
typedef enum DBGGUISTATSNODESTATE
{
/** The typical invalid zeroth entry. */
/** The node is the root node. */
/** The node is visible. */
/** The node should be refreshed. */
/** diff: The node equals. */
/** diff: The node in set 1 is less than the one in set 2. */
/** diff: The node in set 1 is greater than the one in set 2. */
/** diff: The node is only in set 1. */
/** diff: The node is only in set 2. */
/** The end of the valid state values. */
/**
* A tree node representing a statistic sample.
*
* The nodes carry a reference to the parent and to its position among its
* siblings. Both of these need updating when the grand parent or parent adds a
* new child. This will hopefully not be too expensive but rather pay off when
* we need to create a parent index.
*/
typedef struct DBGGUISTATSNODE
{
/** Pointer to the parent. */
/** Array of pointers to the child nodes. */
/** The number of children. */
/** Our index among the parent's children. */
/** The unit. */
/** The data type.
* For filler nodes not containing data, this will be set to STAMTYPE_INVALID. */
/** The data at last update. */
union
{
/** STAMTYPE_COUNTER. */
/** STAMTYPE_PROFILE. */
/** STAMTYPE_PROFILE_ADV. */
/** STAMTYPE_RATIO_U32. */
/** STAMTYPE_U8 & STAMTYPE_U8_RESET. */
/** STAMTYPE_U16 & STAMTYPE_U16_RESET. */
/** STAMTYPE_U32 & STAMTYPE_U32_RESET. */
/** STAMTYPE_U64 & STAMTYPE_U64_RESET. */
/** STAMTYPE_CALLBACK. */
} Data;
/** The delta. */
/** The name. */
char *pszName;
/** The length of the name. */
/** The description string. */
/** The node state. */
/**
* Recursion stack.
*/
typedef struct DBGGUISTATSSTACK
{
/** The top stack entry. */
/** The stack array. */
struct DBGGUISTATSSTACKENTRY
{
/** The node. */
/** The current child. */
} a[32];
/**
* The item model for the statistics tree view.
*
* This manages the DBGGUISTATSNODE trees.
*/
class VBoxDbgStatsModel : public QAbstractItemModel
{
protected:
/** The root of the sample tree. */
public:
/**
* Constructor.
*
* @param a_pParent The parent object. See QAbstractItemModel in the Qt
* docs for details.
*/
/**
* Destructor.
*
* This will free all the the data the model holds.
*/
virtual ~VBoxDbgStatsModel();
/**
* Updates the data matching the specified pattern.
*
* Nodes not matched by the pattern will become invisible.
*
* @returns true if we reset the model and it's necessary to set the root index.
* @param a_rPatStr The selection pattern.
*/
/**
* Gets the model index of the root node.
*
* @returns root index.
*/
QModelIndex getRootIndex(void) const;
protected:
/**
* Set the root node.
*
* This will free all the current data before taking the ownership of the new
* root node and its children.
*
* @param a_pRoot The new root node.
*/
/** Creates the root node. */
static PDBGGUISTATSNODE createRootNode(void);
/** Creates and insert a node under the given parent. */
static PDBGGUISTATSNODE createAndInsertNode(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition/* = UINT32_MAX*/);
/**
* Resets the node to a pristine state.
*
* @param pNode The node.
*/
/**
* Initializes a pristine node.
*/
static int initNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc);
/**
* Updates (or reinitializes if you like) a node.
*/
static void updateNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc);
/**
* Calculates the full path of a node.
*
* @returns Number of bytes returned, negative value on buffer overflow
*
* @param pNode The node.
* @param psz The output buffer.
* @param cch The size of the buffer.
*/
/**
* Check if the first node is an ancestor to the second one.
*
* @param pAncestor The first node, the alleged ancestor.
* @param pDescendant The second node, the alleged descendant.
*/
/**
* Advance to the next node in the tree.
*
* @returns Pointer to the next node, NULL if we've reached the end or
* was handed a NULL node.
* @param pNode The current node.
*/
/**
* Advance to the next node in the tree that contains data.
*
* @returns Pointer to the next data node, NULL if we've reached the end or
* was handed a NULL node.
* @param pNode The current node.
*/
/**
* Advance to the previous node in the tree.
*
* @returns Pointer to the previous node, NULL if we've reached the end or
* was handed a NULL node.
* @param pNode The current node.
*/
/**
* Advance to the previous node in the tree that contains data.
*
* @returns Pointer to the previous data node, NULL if we've reached the end or
* was handed a NULL node.
* @param pNode The current node.
*/
/**
* Removes a node from the tree and destroys it and all its decentands.
*
* @param pNode The node.
*/
/**
* Destroys a statistics tree.
*
* @param a_pRoot The root of the tree. NULL is fine.
*/
/**
* Stringifies exactly one node, no children.
*
* This is for logging and clipboard.
*
* @param a_pNode The node.
* @param a_rString The string to append the strigified node to.
*/
/**
* Stringifies a node and its children.
*
* This is for logging and clipboard.
*
* @param a_pNode The node.
* @param a_rString The string to append the strigified node to.
*/
public:
/**
* Converts the specified tree to string.
*
* This is for logging and clipboard.
*
* @param a_rRoot Where to start. Use QModelIndex() to start at the root.
* @param a_rString Where to return to return the string dump.
*/
/**
* Dumps the given (sub-)tree as XML.
*
* @param a_rRoot Where to start. Use QModelIndex() to start at the root.
* @param a_rString Where to return to return the XML dump.
*/
/**
* Puts the stringified tree on the clipboard.
*
* @param a_rRoot Where to start. Use QModelIndex() to start at the root.
*/
protected:
/** Worker for logTree. */
public:
/** Logs a (sub-)tree.
*
* @param a_rRoot Where to start. Use QModelIndex() to start at the root.
* @param a_fReleaseLog Whether to use the release log (true) or the debug log (false).
*/
protected:
/** Gets the unit. */
/** Gets the minimum value. */
/** Gets the average value. */
/** Gets the maximum value. */
/** Gets the total value. */
/** Gets the delta value. */
/**
* Destroys a node and all its children.
*
* @param a_pNode The node to destroy.
*/
/**
* Converts an index to a node pointer.
*
* @returns Pointer to the node, NULL if invalid reference.
* @param a_rIndex Reference to the index
*/
{
return NULL;
}
public:
/** @name Overriden QAbstractItemModel methods
* @{ */
///virtual void sort(int a_iColumn, Qt::SortOrder a_eOrder);
/** @} */
};
/**
* Model using the VM / STAM interface as data source.
*/
{
public:
/**
* Constructor.
*
* @param a_pVM The VM handle.
* @param a_rPatStr The selection pattern.
* @param a_pParent The parent object. NULL is fine.
*/
/** Destructor */
virtual ~VBoxDbgStatsModelVM();
/**
* Updates the data matching the specified pattern.
*
* Nodes not matched by the pattern will become invisible.
*
* @returns true if we reset the model and it's necessary to set the root index.
* @param a_rPatStr The selection pattern.
*/
protected:
/**
* Enumeration callback used by updateStats.
*/
static DECLCALLBACK(int) updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
/**
* Enumeration callback used by createNewTree.
*/
static DECLCALLBACK(int) createNewTreeCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
/**
* Constructs a new statistics tree by query data from the VM.
*
* @returns Pointer to the root of the tree we've constructed. This will be NULL
* if the STAM API throws an error or we run out of memory.
* @param a_rPatStr The selection pattern.
*/
protected:
/** Next update child. This is UINT32_MAX when invalid. */
/** Pointer to the node m_szUpdateParent represent and m_iUpdateChild refers to. */
/** The length of the path. */
/** The path to the current update parent, including a trailing slash. */
char m_szUpdateParent[1024];
bool m_fUpdateInsertRemove;
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Formats a number into a 64-byte buffer.
*/
{
static const char s_szDigits[] = "0123456789";
psz += 63;
*psz-- = '\0';
unsigned cDigits = 0;
for (;;)
{
u64 /= 10;
if (!u64)
break;
psz--;
if (!(++cDigits % 3))
*psz-- = ',';
}
return psz;
}
/**
* Formats a number into a 64-byte buffer.
* (18.446.744.073.709.551.615)
*/
{
static const char s_szDigits[] = "0123456789";
psz += 63;
*psz-- = '\0';
unsigned cDigits = 0;
for (;;)
{
u64 /= 10;
if (!u64)
break;
psz--;
if (!(++cDigits % 3))
*psz-- = ',';
}
if (fNegative)
*--psz = '-';
return psz;
}
/**
* Formats a unsigned hexadecimal number into a into a 64-byte buffer.
*/
{
static const char s_szDigits[] = "0123456789abcdef";
psz += 63;
*psz-- = '\0';
unsigned cDigits = 0;
for (;;)
{
u64 /= 16;
++cDigits;
break;
psz--;
if (!(cDigits % 8))
*psz-- = '\'';
}
return psz;
}
#if 0/* unused */
/**
* Formats a sort key number.
*/
{
static const char s_szDigits[] = "0123456789abcdef";
/* signed */
*psz++ = '+';
/* 16 hex digits */
unsigned i = 16;
while (i-- > 0)
{
if (u64)
{
u64 /= 16;
}
else
psz[i] = '0';
}
}
#endif
#if 0/* unused */
/**
* Formats a sort key number.
*/
{
static const char s_szDigits[] = "0123456789abcdef";
/* signed */
if (i64 >= 0)
{
*psz++ = '+';
}
else
{
*psz++ = '-';
}
/* 16 hex digits */
unsigned i = 16;
while (i-- > 0)
{
if (u64)
{
u64 /= 16;
}
else
psz[i] = '0';
}
}
#endif
/*
*
* V B o x D b g S t a t s M o d e l
* V B o x D b g S t a t s M o d e l
* V B o x D b g S t a t s M o d e l
*
*
*/
{
}
{
}
/*static*/ void
{
if (!a_pRoot)
return;
}
/* static*/ void
{
/* destroy all our children */
while (i-- > 0)
{
}
/* free the resources we're using */
{
}
{
}
#ifdef VBOX_STRICT
/* poison it. */
a_pNode->papChildren++;
#endif
/* Finally ourselves */
}
/*static*/ PDBGGUISTATSNODE
VBoxDbgStatsModel::createRootNode(void)
{
if (!pRoot)
return NULL;
return pRoot;
}
/*static*/ PDBGGUISTATSNODE
VBoxDbgStatsModel::createAndInsertNode(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition /*= UINT32_MAX*/)
{
/*
* Create it.
*/
if (!pNode)
return NULL;
/*
* Do we need to expand the array?
*/
{
void *pvNew = RTMemRealloc(pParent->papChildren, sizeof(*pParent->papChildren) * (pParent->cChildren + 32));
if (!pvNew)
{
return NULL;
}
}
/*
* Insert it.
*/
/* Last. */
else
{
/* Shift all the items after ours. */
{
}
}
/* Insert ours */
return pNode;
}
/*static*/ void
{
/*
* Unlink it.
*/
if (pParent)
{
{
}
#ifdef VBOX_STRICT /* poison */
#endif
}
/*
* Delete it.
*/
}
/*static*/ void
{
/* free and reinit the data. */
{
}
/* free the description. */
{
}
}
/*static*/ int
VBoxDbgStatsModel::initNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc)
{
/*
* Copy the data.
*/
if (pszDesc)
pNode->pDescStr = new QString(pszDesc); /* ignore allocation failure (well, at least up to the point we can ignore it) */
switch (enmType)
{
case STAMTYPE_COUNTER:
break;
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
break;
case STAMTYPE_RATIO_U32:
case STAMTYPE_RATIO_U32_RESET:
break;
case STAMTYPE_CALLBACK:
{
break;
}
case STAMTYPE_U8:
case STAMTYPE_U8_RESET:
case STAMTYPE_X8:
case STAMTYPE_X8_RESET:
break;
case STAMTYPE_U16:
case STAMTYPE_U16_RESET:
case STAMTYPE_X16:
case STAMTYPE_X16_RESET:
break;
case STAMTYPE_U32:
case STAMTYPE_U32_RESET:
case STAMTYPE_X32:
case STAMTYPE_X32_RESET:
break;
case STAMTYPE_U64:
case STAMTYPE_U64_RESET:
case STAMTYPE_X64:
case STAMTYPE_X64_RESET:
break;
default:
break;
}
return VINF_SUCCESS;
}
/*static*/ void
VBoxDbgStatsModel::updateNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc)
{
/*
* Reset and init the node if the type changed.
*/
{
}
else
{
/*
* ASSUME that only the sample value will change and that the unit, visibility
* and description remains the same.
*/
switch (enmType)
{
case STAMTYPE_COUNTER:
{
{
}
break;
}
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
{
{
}
break;
}
case STAMTYPE_RATIO_U32:
case STAMTYPE_RATIO_U32_RESET:
{
{
{
}
}
else
{
if (iDeltaA >= 0)
else
}
break;
}
case STAMTYPE_CALLBACK:
{
{
}
{
}
break;
}
case STAMTYPE_U8:
case STAMTYPE_U8_RESET:
case STAMTYPE_X8:
case STAMTYPE_X8_RESET:
{
{
}
break;
}
case STAMTYPE_U16:
case STAMTYPE_U16_RESET:
case STAMTYPE_X16:
case STAMTYPE_X16_RESET:
{
{
}
break;
}
case STAMTYPE_U32:
case STAMTYPE_U32_RESET:
case STAMTYPE_X32:
case STAMTYPE_X32_RESET:
{
{
}
break;
}
case STAMTYPE_U64:
case STAMTYPE_U64_RESET:
case STAMTYPE_X64:
case STAMTYPE_X64_RESET:
{
{
}
break;
}
default:
break;
}
}
}
/*static*/ int32_t
{
{
/* root - don't add it's slash! */
off = 0;
*psz = '\0';
}
else
{
if (off >= 0)
{
}
}
return off;
}
/*static*/ bool
{
while (pDescendant)
{
if (pDescendant == pAncestor)
return true;
}
return false;
}
/*static*/ PDBGGUISTATSNODE
{
if (!pNode)
return NULL;
/* decend to children. */
return pNode->papChildren[0];
if (!pParent)
return NULL;
/* next sibling. */
/* ascend and advanced to a parent's sibiling. */
for (;;)
{
if (!pParent)
return NULL;
}
}
/*static*/ PDBGGUISTATSNODE
{
do
while ( pNode
return pNode;
}
/*static*/ PDBGGUISTATSNODE
{
if (!pNode)
return NULL;
if (!pParent)
return NULL;
/* previous sibling's latest decendant (better expression anyone?). */
{
return pNode;
}
/* ascend to the parent. */
return pParent;
}
/*static*/ PDBGGUISTATSNODE
{
do
while ( pNode
return pNode;
}
#if 0
/*static*/ PDBGGUISTATSNODE
{
/** @todo */
return NULL;
}
#endif
#if 0
/*static*/ PDBGGUISTATSNODE
{
/** @todo */
return NULL;
}
#endif
#if 0
/*static*/ PDBGGUISTATSNODE
{
/** @todo */
return NULL;
}
#endif
VBoxDbgStatsModel::getRootIndex(void) const
{
if (!m_pRoot)
return QModelIndex();
return createIndex(0, 0, m_pRoot);
}
void
{
reset();
}
{
#if 0
if (pNode)
{
}
#endif
return fFlags;
}
int
{
return DBGGUI_STATS_COLUMNS;
}
int
{
}
bool
{
return pParent
}
{
if (!pParent)
{
return QModelIndex(); /* bug? */
}
{
printf("index: iRow=%d >= cChildren=%u (iColumn=%d)\n", iRow, (unsigned)pParent->cChildren, iColumn);
return QModelIndex(); /* bug? */
}
if ((unsigned)iColumn >= DBGGUI_STATS_COLUMNS)
{
return QModelIndex(); /* bug? */
}
//printf("index: iRow=%d iColumn=%d %p %s/%s\n", iRow, iColumn, pChild, pParent->pszName, pChild->pszName);
}
{
if (!pChild)
{
printf("parent: invalid child\n");
return QModelIndex(); /* bug */
}
if (!pParent)
{
// printf("parent: root\n");
return QModelIndex(); /* we're root */
}
//printf("parent: iSelf=%d iColumn=%d %p %s(/%s)\n", pParent->iSelf, a_rChild.column(), pParent, pParent->pszName, pChild->pszName);
}
{
return QVariant();
switch (a_iSection)
{
case 0: return tr("Name");
default:
return QVariant(); /* bug */
}
}
/*static*/ QString
{
return "";
}
/*static*/ QString
{
char sz[128];
{
case STAMTYPE_COUNTER:
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
return "0";
case STAMTYPE_RATIO_U32:
case STAMTYPE_RATIO_U32_RESET:
{
*psz++ = ':';
return psz;
}
case STAMTYPE_CALLBACK:
case STAMTYPE_U8:
case STAMTYPE_U8_RESET:
case STAMTYPE_X8:
case STAMTYPE_X8_RESET:
case STAMTYPE_U16:
case STAMTYPE_U16_RESET:
case STAMTYPE_X16:
case STAMTYPE_X16_RESET:
case STAMTYPE_U32:
case STAMTYPE_U32_RESET:
case STAMTYPE_X32:
case STAMTYPE_X32_RESET:
case STAMTYPE_U64:
case STAMTYPE_U64_RESET:
case STAMTYPE_X64:
case STAMTYPE_X64_RESET:
default:
case STAMTYPE_INVALID:
return "";
}
}
/*static*/ QString
{
char sz[128];
{
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
return "0";
default:
return "";
}
}
/*static*/ QString
{
char sz[128];
{
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
return "0";
default:
return "";
}
}
/*static*/ QString
{
char sz[128];
{
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
return "0";
default:
return "";
}
}
/*static*/ QString
{
char sz[128];
{
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
return "0";
default:
return "";
}
}
/*static*/ QString
{
char sz[128];
{
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
return "0";
default:
return "";
}
}
{
if ( iCol >= DBGGUI_STATS_COLUMNS
/*&& a_eRole != Qt::XxxRole*/) )
return QVariant();
if (!pNode)
return QVariant();
switch (iCol)
{
case 0:
case 1:
case 2:
return strValueTimes(pNode);
case 3:
return strMinValue(pNode);
case 4:
return strAvgValue(pNode);
case 5:
return strMaxValue(pNode);
case 6:
return strTotalValue(pNode);
case 7:
return strDeltaValue(pNode);
case 8:
default:
return QVariant();
}
}
/*static*/ void
{
a_rString = "todo";
}
/*static*/ void
{
/* this node */
a_rString += "\n";
/* the children */
}
void
{
if (pRoot)
}
void
{
if (pClipboard)
}
/*static*/ void
{
/* this node */
if (a_fReleaseLog)
else
/* the children */
}
void
{
if (pRoot)
}
/*
*
* V B o x D b g S t a t s M o d e l V M
* V B o x D b g S t a t s M o d e l V M
* V B o x D b g S t a t s M o d e l V M
*
*
*/
{
/*
* Create a model containing the STAM entries matching the pattern.
* flag that it turned out didn't exist.)
*/
}
{
/* nothing to do here. */
}
/*static*/ DECLCALLBACK(int)
VBoxDbgStatsModelVM::updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
{
/*
* Skip the ones which shouldn't be visible in the GUI.
*/
if (enmVisibility == STAMVISIBILITY_NOT_GUI)
return 0;
/*
* The default assumption is that nothing has changed.
* For now we'll reset the model when ever something changes.
*/
{
/* got it! */;
else
{
/*
* We might be inserting a new node between pPrev and pNode
* or we might be removing one or more nodes. Either case is
* handled in the same rough way.
*/
pThis->m_fUpdateInsertRemove = true;
/** @todo optimize insert since that is a normal occurence. */
/* Start with the current parent node and look for a common ancestor
hoping that this is faster than going from the root (saves lookup). */
{
break;
}
modifying m_szUpdateParent as we go along. */
{
/* Find the end of this component. */
if (!pszEnd)
/* Add the name to the path. */
{
/* first child */
}
else
{
/* binary search. */
for (;;)
{
if (!iDiff)
if (iDiff > 0)
{
iStart = i + 1;
{
break;
}
}
else if (iDiff < 0)
{
iLast = i - 1;
{
break;
}
}
else
{
break;
}
}
}
}
/* Remove all the nodes between pNode and pPrev but keep all
of pNode's ancestors (or it'll get orphaned). */
{
}
/* Removed the data from all ancestors of pNode that it doesn't share them pPrev. */
if (pPrev)
{
{
}
}
/* Finally, adjust the globals (szUpdateParent is one level too deep). */
}
}
else
{
/*
* Insert it at the end of the tree.
*
* Do the same as we're doing down in createNewTreeCallback, walk from the
* root and create whatever we need.
*/
pThis->m_fUpdateInsertRemove = true;
while (*pszCur)
{
/* Find the end of this component. */
if (!pszNext)
/* Create it if it doesn't exist (it will be last if it exists). */
{
if (!pNode)
return VERR_NO_MEMORY;
}
else
/* Advance */
}
}
/*
* Perform the update.
*/
/*
* Advance to the next node with data.
*
* ASSUMES a leaf *must* have data and again we're ASSUMING the sorting
* on slash separated sub-strings.
*/
{
#ifdef VBOX_STRICT
#endif
{
/* decend to the first child. */
}
{
/* next sibling or one if its descendants. */
}
else
{
/* move up and down- / on-wards */
for (;;)
{
/* ascend */
if (!pParent)
{
pThis->m_cchUpdateParent = 0;
break;
}
/* try advance */
{
break;
}
}
}
/* decend to a node containing data and finalize the globals. (ASSUMES leaf has data.) */
{
{
}
}
}
/* else: we're at the end */
return VINF_SUCCESS;
}
bool
{
/*
* Find the first child with data and set it up as the 'next'
* node to be updated.
*/
if (pFirst)
{
}
else
{
m_szUpdateParent[0] = '\0';
m_cchUpdateParent = 0;
}
/*
* Perform the update.
*
* This can be optimized later when the normal updating is working perfectly.
*/
m_fUpdateInsertRemove = false;
/** @todo the way we update this stuff is independent of the source (XML, file, STAM), our only
* ASSUMPTION is that the input is strictly ordered by (fully slashed) name. So, all this stuff
* should really move up into the parent class. */
/* Remove any nodes following the last in the update. */
if ( RT_SUCCESS(rc)
&& m_iUpdateChild != UINT32_MAX)
{
if (!pLast)
else
{
}
m_fUpdateInsertRemove = true;
}
reset();
else
{
/*
* Send dataChanged events.
*/
{
/* get top element */
{
/* push */
}
else
{
/* pop */
/* do the actual work. */
iChild = 0;
{
/* skip to the first needing updating. */
iChild++;
break;
/* collect any subsequent nodes that also needs refreshing. */
{
else
break;
}
QModelIndex BottomRight = createIndex(iChild - 1, DBGGUI_STATS_COLUMNS - 1, pNode->papChildren[iChild - 1]);
/* emit the refresh signal */
}
}
}
}
return m_fUpdateInsertRemove;
}
/*static*/ DECLCALLBACK(int)
VBoxDbgStatsModelVM::createNewTreeCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
{
/*
* Skip the ones which shouldn't be visible in the GUI.
*/
if (enmVisibility == STAMVISIBILITY_NOT_GUI)
return 0;
/*
* Perform a mkdir -p like operation till we've walked / created the entire path down
* to the node specfied node. Remember the last node as that will be the one we will
* stuff the data into.
*/
while (*pszCur)
{
/* find the end of this component. */
if (!pszNext)
/* Create it if it doesn't exist (it will be last if it exists). */
{
if (!pNode)
return VERR_NO_MEMORY;
}
else
/* Advance */
}
/*
* Save the data.
*/
}
{
if (pRoot)
{
if (VBOX_SUCCESS(rc))
return pRoot;
/* failed, cleanup. */
}
return NULL;
}
#if 0 /* save for later */
void VBoxDbgStatsLeafItem::update(STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, STAMVISIBILITY enmVisibility, const char *pszDesc)
{
/*
* Detect changes.
* This path will be taken on the first update and if a item
*/
{
/*
* Unit.
*/
/**
* Update the description.
* Insert two spaces as gap after the last left-aligned field.
* @todo dmik: How to make this better?
*/
/*
* Clear the content.
*/
}
/*
* Update the data.
*/
char sz[64];
switch (enmType)
{
case STAMTYPE_COUNTER:
{
break;
}
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
{
{
}
else
{
}
break;
}
case STAMTYPE_RATIO_U32:
case STAMTYPE_RATIO_U32_RESET:
{
char sz2[64];
char sz3[128];
strcat(strcat(strcpy(sz3, formatNumber(sz, m_Data.RatioU32.u32A)), " : "), formatNumber(sz2, m_Data.RatioU32.u32B));
///@todo ratio: setText(7, formatNumberSigned(sz, m_Data.u64 - u64Prev));
break;
}
case STAMTYPE_CALLBACK:
{
break;
}
case STAMTYPE_U8:
case STAMTYPE_U8_RESET:
{
break;
}
case STAMTYPE_X8:
case STAMTYPE_X8_RESET:
{
break;
}
case STAMTYPE_U16:
case STAMTYPE_U16_RESET:
{
break;
}
case STAMTYPE_X16:
case STAMTYPE_X16_RESET:
{
break;
}
case STAMTYPE_U32:
case STAMTYPE_U32_RESET:
{
break;
}
case STAMTYPE_X32:
case STAMTYPE_X32_RESET:
{
break;
}
case STAMTYPE_U64:
case STAMTYPE_U64_RESET:
{
break;
}
case STAMTYPE_X64:
case STAMTYPE_X64_RESET:
{
break;
}
default:
break;
}
}
{
/* name and description */
/* the number columns */
char sz[128];
switch (m_enmType)
{
case STAMTYPE_COUNTER:
switch (iColumn)
{
default: sz[0] = '\0'; break;
}
break;
case STAMTYPE_PROFILE:
case STAMTYPE_PROFILE_ADV:
{
switch (iColumn)
{
default: sz[0] = '\0'; break;
}
}
else
sz[0] = '\0';
break;
case STAMTYPE_RATIO_U32:
case STAMTYPE_RATIO_U32_RESET:
else
break;
case STAMTYPE_U8:
case STAMTYPE_U8_RESET:
switch (iColumn)
{
default: sz[0] = '\0'; break;
}
break;
case STAMTYPE_U16:
case STAMTYPE_U16_RESET:
switch (iColumn)
{
default: sz[0] = '\0'; break;
}
break;
case STAMTYPE_U32:
case STAMTYPE_U32_RESET:
switch (iColumn)
{
default: sz[0] = '\0'; break;
}
break;
case STAMTYPE_U64:
case STAMTYPE_U64_RESET:
switch (iColumn)
{
default: sz[0] = '\0'; break;
}
break;
case STAMTYPE_CALLBACK:
default:
}
}
#endif /* saved for later reuse */
/*
*
* V B o x D b g S t a t s V i e w
* V B o x D b g S t a t s V i e w
* V B o x D b g S t a t s V i e w
*
*
*/
VBoxDbgStatsView::VBoxDbgStatsView(PVM a_pVM, VBoxDbgStatsModel *a_pModel, VBoxDbgStats *a_pParent/* = NULL*/)
{
/*
* Set the model and view defaults.
*/
//setRootIsDecorated(true);
setItemsExpandable(true);
setSortingEnabled(true);
/*
* We've got three menus to populate and link up.
*/
#ifdef VBOXDBG_USE_QT4
/** @todo */
#else /* QT3 */
m_pLeafMenu = new QPopupMenu(this);
m_pBranchMenu = new QPopupMenu(this);
m_pViewMenu = new QPopupMenu(this);
#endif /* QT3 */
}
{
#if 0 /// @todo check who has to delete the model...
delete m_pModel;
#endif
}
#if 0
/**
* Hides all parent branches which doesn't have any visible leafs.
*/
{
#ifdef VBOXDBG_USE_QT4
/// @todo
#else
{
if (pChild)
return;
pParent->setVisible(false);
}
#endif
}
/**
* Shows all parent branches
*/
{
pParent->setVisible(true);
}
#endif
{
#if 0 /* later */
if (VBOX_SUCCESS(rc))
{
/* hide what's left */
{
pCur->setVisible(false);
}
}
#endif
}
{
}
#if 0 /* later */
{
#ifdef VBOXDBG_USE_QT4
pItem->setExpanded(f);
for (int i = 0; i < cChildren; i++)
#else
setOpenTree(pItem, f);
#endif
}
#ifndef VBOXDBG_USE_QT4
void VBoxDbgStatsView::expandAll()
{
setOpenTree(m_pRoot, true);
}
void VBoxDbgStatsView::collapsAll()
{
setOpenTree(m_pRoot, false);
}
#endif /* QT3 */
/*static*/ DECLCALLBACK(int) VBoxDbgStatsView::updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
{
/*
* Skip the ones which shouldn't be visible in the GUI.
*/
if (enmVisibility == STAMVISIBILITY_NOT_GUI)
return 0;
/** @todo !STAMVISIBILITY_USED */
/*
* Advance to the matching item.
*/
while (pCur)
{
/*
* ASSUMES ascending order of STAM items.
*/
if (!iDiff)
break;
if (iDiff > 0)
{
/*
* Removed / filtered out.
*/
{
pCur->setVisible(false);
}
}
else if (iDiff < 0)
{
/*
* New item, insert before pCur.
*/
else
break;
}
}
/*
* End of items, insert it at the tail.
*/
if (!pCur)
{
else
}
/*
* Update it and move on.
*/
return 0;
}
{
const char * const pszFullName = pszName;
/*
* Start at root.
*/
while (*pszName == '/')
pszName++;
/*
* Walk down the path creating what's missing.
*/
for (;;)
{
/*
* Extract the path component.
*/
if (!pszEnd)
return pParent;
/* advance */
/*
* Try find the name among the children of that parent guy.
*/
#ifdef VBOXDBG_USE_QT4
for (int i = 0; i < cChildren; i++)
{
break;
}
#else
#endif
if (pChild)
else
{
#ifdef VBOXDBG_USE_QT4
#else
#endif
}
pParent->setVisible(true);
}
}
{
if (pItem)
{
if (m_pContextMenuItem->isLeaf())
{
#ifdef VBOXDBG_USE_QT4
#else
#endif
}
else
{
#ifdef VBOXDBG_USE_QT4
#else
#endif
}
}
else
{
#ifdef VBOXDBG_USE_QT4
#else
#endif
}
}
{
AssertReturn(pItem, (void)0);
{
case eReset:
/* fall thru */
case eRefresh:
break;
case eCopy:
break;
case eLog:
break;
case eLogRel:
break;
default: /* keep gcc quite */
break;
}
}
{
AssertReturn(m_pContextMenuItem, (void)0);
/** @todo make enum for iId */
{
case eExpand:
setOpenTree(m_pContextMenuItem, true);
break;
case eCollaps:
setOpenTree(m_pContextMenuItem, false);
break;
case eReset:
{
}
/* fall thru */
case eRefresh:
{
{
while ( m_pCur
{
}
if (!m_pCur)
return;
}
else
{
}
break;
}
case eCopy:
break;
case eLog:
break;
case eLogRel:
break;
}
}
{
{
case eExpand:
setOpenTree(m_pRoot, true);
break;
case eCollaps:
setOpenTree(m_pRoot, false);
break;
case eReset:
/* fall thru */
case eRefresh:
break;
case eCopy:
break;
case eLog:
break;
case eLogRel:
break;
}
}
#endif /* later */
/*
*
* V B o x D b g S t a t s
* V B o x D b g S t a t s
* V B o x D b g S t a t s
*
*
*/
VBoxDbgStats::VBoxDbgStats(PVM pVM, const char *pszPat/* = NULL*/, unsigned uRefreshRate/* = 0*/, QWidget *pParent/* = NULL*/)
{
setWindowTitle("VBoxDbg - Statistics");
/*
* On top, a horizontal box with the pattern field, buttons and refresh interval.
*/
m_pPatCB->setDuplicatesEnabled(false);
m_pPatCB->setEditable(true);
pSB->setMinimum(0);
pSB->setWrapping(false);
/*
* Create the tree view and setup the layout.
*/
#if 0 //fixme
/*
* Perform the first refresh to get a good window size.
* We do this with sorting disabled because it's horribly slow otherwise.
*/
//later: int iColumn = m_pView->sortColumn();
m_pView->setUpdatesEnabled(false);
m_pView->setSortingEnabled(false);
refresh();
//later: m_pView->sortItems(iColumn, Qt::AscendingOrder);
// QTreeView::expandAll
#endif
for (int i = 0; i <= 8; i++)
{
}
/*
* Create a refresh timer and start it.
*/
}
{
//????
}
{
refresh();
}
void VBoxDbgStats::applyAll()
{
apply("");
}
void VBoxDbgStats::refresh()
{
}
{
if ((unsigned)iRefresh != m_uRefreshRate)
{
if (!m_uRefreshRate || iRefresh)
else
}
}