VirtualBox

source: vbox/trunk/src/VBox/Debugger/VBoxDbgStatsQt4.cpp@ 31510

Last change on this file since 31510 was 31510, checked in by vboxsync, 14 years ago

The debugger is back in the OSE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.9 KB
Line 
1/* $Id: VBoxDbgStatsQt4.cpp 31510 2010-08-10 08:48:11Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Statistics.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * Oracle Corporation confidential
10 * All rights reserved
11 */
12
13
14/*******************************************************************************
15* Header Files *
16*******************************************************************************/
17#define LOG_GROUP LOG_GROUP_DBGG
18#include "VBoxDbgStatsQt4.h"
19
20#include <QLocale>
21#include <QPushButton>
22#include <QSpinBox>
23#include <QLabel>
24#include <QClipboard>
25#include <QApplication>
26#include <QHBoxLayout>
27#include <QVBoxLayout>
28#include <QKeySequence>
29#include <QAction>
30#include <QContextMenuEvent>
31#include <QHeaderView>
32
33#include <VBox/err.h>
34#include <VBox/log.h>
35#include <iprt/string.h>
36#include <iprt/mem.h>
37#include <iprt/assert.h>
38
39
40/*******************************************************************************
41* Defined Constants And Macros *
42*******************************************************************************/
43/** The number of column. */
44#define DBGGUI_STATS_COLUMNS 9
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50/**
51 * The state of a statistics sample node.
52 *
53 * This is used for two pass refresh (1. get data, 2. update the view) and
54 * for saving the result of a diff.
55 */
56typedef enum DBGGUISTATSNODESTATE
57{
58 /** The typical invalid zeroth entry. */
59 kDbgGuiStatsNodeState_kInvalid = 0,
60 /** The node is the root node. */
61 kDbgGuiStatsNodeState_kRoot,
62 /** The node is visible. */
63 kDbgGuiStatsNodeState_kVisible,
64 /** The node should be refreshed. */
65 kDbgGuiStatsNodeState_kRefresh,
66 /** diff: The node equals. */
67 kDbgGuiStatsNodeState_kDiffEqual,
68 /** diff: The node in set 1 is less than the one in set 2. */
69 kDbgGuiStatsNodeState_kDiffSmaller,
70 /** diff: The node in set 1 is greater than the one in set 2. */
71 kDbgGuiStatsNodeState_kDiffGreater,
72 /** diff: The node is only in set 1. */
73 kDbgGuiStatsNodeState_kDiffOnlyIn1,
74 /** diff: The node is only in set 2. */
75 kDbgGuiStatsNodeState_kDiffOnlyIn2,
76 /** The end of the valid state values. */
77 kDbgGuiStatsNodeState_kEnd
78} DBGGUISTATENODESTATE;
79
80
81/**
82 * A tree node representing a statistic sample.
83 *
84 * The nodes carry a reference to the parent and to its position among its
85 * siblings. Both of these need updating when the grand parent or parent adds a
86 * new child. This will hopefully not be too expensive but rather pay off when
87 * we need to create a parent index.
88 */
89typedef struct DBGGUISTATSNODE
90{
91 /** Pointer to the parent. */
92 PDBGGUISTATSNODE pParent;
93 /** Array of pointers to the child nodes. */
94 PDBGGUISTATSNODE *papChildren;
95 /** The number of children. */
96 uint32_t cChildren;
97 /** Our index among the parent's children. */
98 uint32_t iSelf;
99 /** The unit. */
100 STAMUNIT enmUnit;
101 /** The data type.
102 * For filler nodes not containing data, this will be set to STAMTYPE_INVALID. */
103 STAMTYPE enmType;
104 /** The data at last update. */
105 union
106 {
107 /** STAMTYPE_COUNTER. */
108 STAMCOUNTER Counter;
109 /** STAMTYPE_PROFILE. */
110 STAMPROFILE Profile;
111 /** STAMTYPE_PROFILE_ADV. */
112 STAMPROFILEADV ProfileAdv;
113 /** STAMTYPE_RATIO_U32. */
114 STAMRATIOU32 RatioU32;
115 /** STAMTYPE_U8 & STAMTYPE_U8_RESET. */
116 uint8_t u8;
117 /** STAMTYPE_U16 & STAMTYPE_U16_RESET. */
118 uint16_t u16;
119 /** STAMTYPE_U32 & STAMTYPE_U32_RESET. */
120 uint32_t u32;
121 /** STAMTYPE_U64 & STAMTYPE_U64_RESET. */
122 uint64_t u64;
123 /** STAMTYPE_CALLBACK. */
124 QString *pStr;
125 } Data;
126 /** The delta. */
127 int64_t i64Delta;
128 /** The name. */
129 char *pszName;
130 /** The length of the name. */
131 size_t cchName;
132 /** The description string. */
133 QString *pDescStr;
134 /** The node state. */
135 DBGGUISTATENODESTATE enmState;
136} DBGGUISTATSNODE;
137
138
139/**
140 * Recursion stack.
141 */
142typedef struct DBGGUISTATSSTACK
143{
144 /** The top stack entry. */
145 int32_t iTop;
146 /** The stack array. */
147 struct DBGGUISTATSSTACKENTRY
148 {
149 /** The node. */
150 PDBGGUISTATSNODE pNode;
151 /** The current child. */
152 int32_t iChild;
153 } a[32];
154} DBGGUISTATSSTACK;
155
156
157
158
159/**
160 * The item model for the statistics tree view.
161 *
162 * This manages the DBGGUISTATSNODE trees.
163 */
164class VBoxDbgStatsModel : public QAbstractItemModel
165{
166protected:
167 /** The root of the sample tree. */
168 PDBGGUISTATSNODE m_pRoot;
169
170private:
171 /** Next update child. This is UINT32_MAX when invalid. */
172 uint32_t m_iUpdateChild;
173 /** Pointer to the node m_szUpdateParent represent and m_iUpdateChild refers to. */
174 PDBGGUISTATSNODE m_pUpdateParent;
175 /** The length of the path. */
176 size_t m_cchUpdateParent;
177 /** The path to the current update parent, including a trailing slash. */
178 char m_szUpdateParent[1024];
179 /** Inserted or/and removed nodes during the update. */
180 bool m_fUpdateInsertRemove;
181
182
183public:
184 /**
185 * Constructor.
186 *
187 * @param a_pParent The parent object. See QAbstractItemModel in the Qt
188 * docs for details.
189 */
190 VBoxDbgStatsModel(QObject *a_pParent);
191
192 /**
193 * Destructor.
194 *
195 * This will free all the data the model holds.
196 */
197 virtual ~VBoxDbgStatsModel();
198
199 /**
200 * Updates the data matching the specified pattern.
201 *
202 * This will should invoke updatePrep, updateCallback and updateDone.
203 *
204 * It is vitally important that updateCallback is fed the data in the right
205 * order. The code make very definite ASSUMPTIONS about the ordering being
206 * stricly sorted and taking the slash into account when doing so.
207 *
208 * @returns true if we reset the model and it's necessary to set the root index.
209 * @param a_rPatStr The selection pattern.
210 *
211 * @remarks The default implementation is an empty stub.
212 */
213 virtual bool updateStatsByPattern(const QString &a_rPatStr);
214
215 /**
216 * Similar to updateStatsByPattern, except that it only works on a sub-tree and
217 * will not remove anything that's outside that tree.
218 *
219 * @param a_rIndex The sub-tree root. Invalid index means root.
220 *
221 * @todo Create a default implementation using updateStatsByPattern.
222 */
223 virtual void updateStatsByIndex(QModelIndex const &a_rIndex);
224
225 /**
226 * Reset the stats matching the specified pattern.
227 *
228 * @param a_rPatStr The selection pattern.
229 *
230 * @remarks The default implementation is an empty stub.
231 */
232 virtual void resetStatsByPattern(QString const &a_rPatStr);
233
234 /**
235 * Reset the stats of a sub-tree.
236 *
237 * @param a_rIndex The sub-tree root. Invalid index means root.
238 * @param a_fSubTree Whether to reset the sub-tree as well. Default is true.
239 *
240 * @remarks The default implementation makes use of resetStatsByPattern
241 */
242 virtual void resetStatsByIndex(QModelIndex const &a_rIndex, bool a_fSubTree = true);
243
244 /**
245 * Gets the model index of the root node.
246 *
247 * @returns root index.
248 */
249 QModelIndex getRootIndex(void) const;
250
251
252protected:
253 /**
254 * Set the root node.
255 *
256 * This will free all the current data before taking the ownership of the new
257 * root node and its children.
258 *
259 * @param a_pRoot The new root node.
260 */
261 void setRootNode(PDBGGUISTATSNODE a_pRoot);
262
263 /** Creates the root node. */
264 static PDBGGUISTATSNODE createRootNode(void);
265
266 /** Creates and insert a node under the given parent. */
267 static PDBGGUISTATSNODE createAndInsertNode(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition);
268
269 /** Creates and insert a node under the given parent with correct Qt
270 * signalling. */
271 PDBGGUISTATSNODE createAndInsert(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition);
272
273 /**
274 * Resets the node to a pristine state.
275 *
276 * @param pNode The node.
277 */
278 static void resetNode(PDBGGUISTATSNODE pNode);
279
280 /**
281 * Initializes a pristine node.
282 */
283 static int initNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc);
284
285 /**
286 * Updates (or reinitializes if you like) a node.
287 */
288 static void updateNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc);
289
290 /**
291 * Called by updateStatsByPattern(), makes the necessary preparations.
292 *
293 * @returns Success indicator.
294 */
295 bool updatePrepare(void);
296
297 /**
298 * Called by updateStatsByPattern(), finalizes the update.
299 *
300 * @return See updateStatsByPattern().
301 *
302 * @param a_fSuccess Whether the update was successful or not.
303 *
304 */
305 bool updateDone(bool a_fSuccess);
306
307 /**
308 * updateCallback() worker taking care of in-tree inserts and removals.
309 *
310 * @returns The current node.
311 * @param pszName The name of the tree element to update.
312 */
313 PDBGGUISTATSNODE updateCallbackHandleOutOfOrder(const char *pszName);
314
315 /**
316 * updateCallback() worker taking care of tail insertions.
317 *
318 * @returns The current node.
319 * @param pszName The name of the tree element to update.
320 */
321 PDBGGUISTATSNODE updateCallbackHandleTail(const char *pszName);
322
323 /**
324 * updateCallback() worker that advances the update state to the next data node
325 * in anticipation of the next updateCallback call.
326 *
327 * @returns The current node.
328 * @param pNode The current node.
329 */
330 void updateCallbackAdvance(PDBGGUISTATSNODE pNode);
331
332 /** Callback used by updateStatsByPattern() and updateStatsByIndex() to feed
333 * changes.
334 * @copydoc FNSTAMR3ENUM */
335 static DECLCALLBACK(int) updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
336 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser);
337
338 /**
339 * Calculates the full path of a node.
340 *
341 * @returns Number of bytes returned, negative value on buffer overflow
342 *
343 * @param pNode The node.
344 * @param psz The output buffer.
345 * @param cch The size of the buffer.
346 */
347 static ssize_t getNodePath(PCDBGGUISTATSNODE pNode, char *psz, ssize_t cch);
348
349 /**
350 * Check if the first node is an ancestor to the second one.
351 *
352 * @returns true/false.
353 * @param pAncestor The first node, the alleged ancestor.
354 * @param pDescendant The second node, the alleged descendant.
355 */
356 static bool isNodeAncestorOf(PCDBGGUISTATSNODE pAncestor, PCDBGGUISTATSNODE pDescendant);
357
358 /**
359 * Advance to the next node in the tree.
360 *
361 * @returns Pointer to the next node, NULL if we've reached the end or
362 * was handed a NULL node.
363 * @param pNode The current node.
364 */
365 static PDBGGUISTATSNODE nextNode(PDBGGUISTATSNODE pNode);
366
367 /**
368 * Advance to the next node in the tree that contains data.
369 *
370 * @returns Pointer to the next data node, NULL if we've reached the end or
371 * was handed a NULL node.
372 * @param pNode The current node.
373 */
374 static PDBGGUISTATSNODE nextDataNode(PDBGGUISTATSNODE pNode);
375
376 /**
377 * Advance to the previous node in the tree.
378 *
379 * @returns Pointer to the previous node, NULL if we've reached the end or
380 * was handed a NULL node.
381 * @param pNode The current node.
382 */
383 static PDBGGUISTATSNODE prevNode(PDBGGUISTATSNODE pNode);
384
385 /**
386 * Advance to the previous node in the tree that contains data.
387 *
388 * @returns Pointer to the previous data node, NULL if we've reached the end or
389 * was handed a NULL node.
390 * @param pNode The current node.
391 */
392 static PDBGGUISTATSNODE prevDataNode(PDBGGUISTATSNODE pNode);
393
394 /**
395 * Removes a node from the tree.
396 *
397 * @returns pNode.
398 * @param pNode The node.
399 */
400 static PDBGGUISTATSNODE removeNode(PDBGGUISTATSNODE pNode);
401
402 /**
403 * Removes a node from the tree and destroys it and all its decentands.
404 *
405 * @param pNode The node.
406 */
407 static void removeAndDestroyNode(PDBGGUISTATSNODE pNode);
408
409 /** Removes a node from the tree and destroys it and all its decentands
410 * performing the required Qt signalling. */
411 void removeAndDestroy(PDBGGUISTATSNODE pNode);
412
413 /**
414 * Destroys a statistics tree.
415 *
416 * @param a_pRoot The root of the tree. NULL is fine.
417 */
418 static void destroyTree(PDBGGUISTATSNODE a_pRoot);
419
420 /**
421 * Stringifies exactly one node, no children.
422 *
423 * This is for logging and clipboard.
424 *
425 * @param a_pNode The node.
426 * @param a_rString The string to append the strigified node to.
427 */
428 static void stringifyNodeNoRecursion(PDBGGUISTATSNODE a_pNode, QString &a_rString);
429
430 /**
431 * Stringifies a node and its children.
432 *
433 * This is for logging and clipboard.
434 *
435 * @param a_pNode The node.
436 * @param a_rString The string to append the strigified node to.
437 */
438 static void stringifyNode(PDBGGUISTATSNODE a_pNode, QString &a_rString);
439
440public:
441 /**
442 * Converts the specified tree to string.
443 *
444 * This is for logging and clipboard.
445 *
446 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
447 * @param a_rString Where to return to return the string dump.
448 */
449 void stringifyTree(QModelIndex &a_rRoot, QString &a_rString) const;
450
451 /**
452 * Dumps the given (sub-)tree as XML.
453 *
454 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
455 * @param a_rString Where to return to return the XML dump.
456 */
457 void xmlifyTree(QModelIndex &a_rRoot, QString &a_rString) const;
458
459 /**
460 * Puts the stringified tree on the clipboard.
461 *
462 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
463 */
464 void copyTreeToClipboard(QModelIndex &a_rRoot) const;
465
466
467protected:
468 /** Worker for logTree. */
469 static void logNode(PDBGGUISTATSNODE a_pNode, bool a_fReleaseLog);
470
471public:
472 /** Logs a (sub-)tree.
473 *
474 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
475 * @param a_fReleaseLog Whether to use the release log (true) or the debug log (false).
476 */
477 void logTree(QModelIndex &a_rRoot, bool a_fReleaseLog) const;
478
479protected:
480 /** Gets the unit. */
481 static QString strUnit(PCDBGGUISTATSNODE pNode);
482 /** Gets the value/times. */
483 static QString strValueTimes(PCDBGGUISTATSNODE pNode);
484 /** Gets the minimum value. */
485 static QString strMinValue(PCDBGGUISTATSNODE pNode);
486 /** Gets the average value. */
487 static QString strAvgValue(PCDBGGUISTATSNODE pNode);
488 /** Gets the maximum value. */
489 static QString strMaxValue(PCDBGGUISTATSNODE pNode);
490 /** Gets the total value. */
491 static QString strTotalValue(PCDBGGUISTATSNODE pNode);
492 /** Gets the delta value. */
493 static QString strDeltaValue(PCDBGGUISTATSNODE pNode);
494
495 /**
496 * Destroys a node and all its children.
497 *
498 * @param a_pNode The node to destroy.
499 */
500 static void destroyNode(PDBGGUISTATSNODE a_pNode);
501
502 /**
503 * Converts an index to a node pointer.
504 *
505 * @returns Pointer to the node, NULL if invalid reference.
506 * @param a_rIndex Reference to the index
507 */
508 inline PDBGGUISTATSNODE nodeFromIndex(const QModelIndex &a_rIndex) const
509 {
510 if (RT_LIKELY(a_rIndex.isValid()))
511 return (PDBGGUISTATSNODE)a_rIndex.internalPointer();
512 return NULL;
513 }
514
515public:
516
517 /** @name Overriden QAbstractItemModel methods
518 * @{ */
519 virtual int columnCount(const QModelIndex &a_rParent) const;
520 virtual QVariant data(const QModelIndex &a_rIndex, int a_eRole) const;
521 virtual Qt::ItemFlags flags(const QModelIndex &a_rIndex) const;
522 virtual bool hasChildren(const QModelIndex &a_rParent) const;
523 virtual QVariant headerData(int a_iSection, Qt::Orientation a_ePrientation, int a_eRole) const;
524 virtual QModelIndex index(int a_iRow, int a_iColumn, const QModelIndex &a_rParent) const;
525 virtual QModelIndex parent(const QModelIndex &a_rChild) const;
526 virtual int rowCount(const QModelIndex &a_rParent) const;
527 ///virtual void sort(int a_iColumn, Qt::SortOrder a_eOrder);
528 /** @} */
529};
530
531
532/**
533 * Model using the VM / STAM interface as data source.
534 */
535class VBoxDbgStatsModelVM : public VBoxDbgStatsModel, public VBoxDbgBase
536{
537public:
538 /**
539 * Constructor.
540 *
541 * @param a_pDbgGui Pointer to the debugger gui object.
542 * @param a_rPatStr The selection pattern.
543 * @param a_pParent The parent object. NULL is fine.
544 */
545 VBoxDbgStatsModelVM(VBoxDbgGui *a_pDbgGui, QString &a_rPatStr, QObject *a_pParent);
546
547 /** Destructor */
548 virtual ~VBoxDbgStatsModelVM();
549
550 virtual bool updateStatsByPattern(const QString &a_rPatStr);
551 virtual void resetStatsByPattern(const QString &a_rPatStr);
552
553protected:
554 /**
555 * Enumeration callback used by createNewTree.
556 */
557 static DECLCALLBACK(int) createNewTreeCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
558 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser);
559
560 /**
561 * Constructs a new statistics tree by query data from the VM.
562 *
563 * @returns Pointer to the root of the tree we've constructed. This will be NULL
564 * if the STAM API throws an error or we run out of memory.
565 * @param a_rPatStr The selection pattern.
566 */
567 PDBGGUISTATSNODE createNewTree(QString &a_rPatStr);
568};
569
570
571/*******************************************************************************
572* Internal Functions *
573*******************************************************************************/
574
575
576/**
577 * Formats a number into a 64-byte buffer.
578 */
579static char *formatNumber(char *psz, uint64_t u64)
580{
581 static const char s_szDigits[] = "0123456789";
582 psz += 63;
583 *psz-- = '\0';
584 unsigned cDigits = 0;
585 for (;;)
586 {
587 const unsigned iDigit = u64 % 10;
588 u64 /= 10;
589 *psz = s_szDigits[iDigit];
590 if (!u64)
591 break;
592 psz--;
593 if (!(++cDigits % 3))
594 *psz-- = ' ';
595 }
596 return psz;
597}
598
599
600/**
601 * Formats a number into a 64-byte buffer.
602 * (18 446 744 073 709 551 615)
603 */
604static char *formatNumberSigned(char *psz, int64_t i64)
605{
606 static const char s_szDigits[] = "0123456789";
607 psz += 63;
608 *psz-- = '\0';
609 const bool fNegative = i64 < 0;
610 uint64_t u64 = fNegative ? -i64 : i64;
611 unsigned cDigits = 0;
612 for (;;)
613 {
614 const unsigned iDigit = u64 % 10;
615 u64 /= 10;
616 *psz = s_szDigits[iDigit];
617 if (!u64)
618 break;
619 psz--;
620 if (!(++cDigits % 3))
621 *psz-- = ' ';
622 }
623 if (fNegative)
624 *--psz = '-';
625 return psz;
626}
627
628
629/**
630 * Formats a unsigned hexadecimal number into a into a 64-byte buffer.
631 */
632static char *formatHexNumber(char *psz, uint64_t u64, unsigned cZeros)
633{
634 static const char s_szDigits[] = "0123456789abcdef";
635 psz += 63;
636 *psz-- = '\0';
637 unsigned cDigits = 0;
638 for (;;)
639 {
640 const unsigned iDigit = u64 % 16;
641 u64 /= 16;
642 *psz = s_szDigits[iDigit];
643 ++cDigits;
644 if (!u64 && cDigits >= cZeros)
645 break;
646 psz--;
647 if (!(cDigits % 8))
648 *psz-- = '\'';
649 }
650 return psz;
651}
652
653
654#if 0/* unused */
655/**
656 * Formats a sort key number.
657 */
658static void formatSortKey(char *psz, uint64_t u64)
659{
660 static const char s_szDigits[] = "0123456789abcdef";
661 /* signed */
662 *psz++ = '+';
663
664 /* 16 hex digits */
665 psz[16] = '\0';
666 unsigned i = 16;
667 while (i-- > 0)
668 {
669 if (u64)
670 {
671 const unsigned iDigit = u64 % 16;
672 u64 /= 16;
673 psz[i] = s_szDigits[iDigit];
674 }
675 else
676 psz[i] = '0';
677 }
678}
679#endif
680
681
682#if 0/* unused */
683/**
684 * Formats a sort key number.
685 */
686static void formatSortKeySigned(char *psz, int64_t i64)
687{
688 static const char s_szDigits[] = "0123456789abcdef";
689
690 /* signed */
691 uint64_t u64;
692 if (i64 >= 0)
693 {
694 *psz++ = '+';
695 u64 = i64;
696 }
697 else
698 {
699 *psz++ = '-';
700 u64 = -i64;
701 }
702
703 /* 16 hex digits */
704 psz[16] = '\0';
705 unsigned i = 16;
706 while (i-- > 0)
707 {
708 if (u64)
709 {
710 const unsigned iDigit = u64 % 16;
711 u64 /= 16;
712 psz[i] = s_szDigits[iDigit];
713 }
714 else
715 psz[i] = '0';
716 }
717}
718#endif
719
720
721
722/*
723 *
724 * V B o x D b g S t a t s M o d e l
725 * V B o x D b g S t a t s M o d e l
726 * V B o x D b g S t a t s M o d e l
727 *
728 *
729 */
730
731
732VBoxDbgStatsModel::VBoxDbgStatsModel(QObject *a_pParent)
733 : QAbstractItemModel(a_pParent),
734 m_pRoot(NULL), m_iUpdateChild(UINT32_MAX), m_pUpdateParent(NULL), m_cchUpdateParent(0)
735{
736}
737
738
739
740VBoxDbgStatsModel::~VBoxDbgStatsModel()
741{
742 destroyTree(m_pRoot);
743 m_pRoot = NULL;
744}
745
746
747/*static*/ void
748VBoxDbgStatsModel::destroyTree(PDBGGUISTATSNODE a_pRoot)
749{
750 if (!a_pRoot)
751 return;
752 Assert(!a_pRoot->pParent);
753 Assert(!a_pRoot->iSelf);
754
755 destroyNode(a_pRoot);
756}
757
758
759/* static*/ void
760VBoxDbgStatsModel::destroyNode(PDBGGUISTATSNODE a_pNode)
761{
762 /* destroy all our children */
763 uint32_t i = a_pNode->cChildren;
764 while (i-- > 0)
765 {
766 destroyNode(a_pNode->papChildren[i]);
767 a_pNode->papChildren[i] = NULL;
768 }
769
770 /* free the resources we're using */
771 a_pNode->pParent = NULL;
772
773 RTMemFree(a_pNode->papChildren);
774 a_pNode->papChildren = NULL;
775
776 if (a_pNode->enmType == STAMTYPE_CALLBACK)
777 {
778 delete a_pNode->Data.pStr;
779 a_pNode->Data.pStr = NULL;
780 }
781
782 a_pNode->cChildren = 0;
783 a_pNode->iSelf = UINT32_MAX;
784 a_pNode->enmUnit = STAMUNIT_INVALID;
785 a_pNode->enmType = STAMTYPE_INVALID;
786
787 RTMemFree(a_pNode->pszName);
788 a_pNode->pszName = NULL;
789
790 if (a_pNode->pDescStr)
791 {
792 delete a_pNode->pDescStr;
793 a_pNode->pDescStr = NULL;
794 }
795
796#ifdef VBOX_STRICT
797 /* poison it. */
798 a_pNode->pParent++;
799 a_pNode->Data.pStr++;
800 a_pNode->pDescStr++;
801 a_pNode->papChildren++;
802 a_pNode->cChildren = 8442;
803#endif
804
805 /* Finally ourselves */
806 a_pNode->enmState = kDbgGuiStatsNodeState_kInvalid;
807 RTMemFree(a_pNode);
808}
809
810
811/*static*/ PDBGGUISTATSNODE
812VBoxDbgStatsModel::createRootNode(void)
813{
814 PDBGGUISTATSNODE pRoot = (PDBGGUISTATSNODE)RTMemAllocZ(sizeof(DBGGUISTATSNODE));
815 if (!pRoot)
816 return NULL;
817 pRoot->iSelf = 0;
818 pRoot->enmType = STAMTYPE_INVALID;
819 pRoot->enmUnit = STAMUNIT_INVALID;
820 pRoot->pszName = (char *)RTMemDup("/", sizeof("/"));
821 pRoot->cchName = 1;
822 pRoot->enmState = kDbgGuiStatsNodeState_kRoot;
823
824 return pRoot;
825}
826
827
828/*static*/ PDBGGUISTATSNODE
829VBoxDbgStatsModel::createAndInsertNode(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition)
830{
831 /*
832 * Create it.
833 */
834 PDBGGUISTATSNODE pNode = (PDBGGUISTATSNODE)RTMemAllocZ(sizeof(DBGGUISTATSNODE));
835 if (!pNode)
836 return NULL;
837 pNode->iSelf = UINT32_MAX;
838 pNode->enmType = STAMTYPE_INVALID;
839 pNode->enmUnit = STAMUNIT_INVALID;
840 pNode->pszName = (char *)RTMemDupEx(pszName, cchName, 1);
841 pNode->cchName = cchName;
842 pNode->enmState = kDbgGuiStatsNodeState_kVisible;
843
844 /*
845 * Do we need to expand the array?
846 */
847 if (!(pParent->cChildren & 31))
848 {
849 void *pvNew = RTMemRealloc(pParent->papChildren, sizeof(*pParent->papChildren) * (pParent->cChildren + 32));
850 if (!pvNew)
851 {
852 destroyNode(pNode);
853 return NULL;
854 }
855 pParent->papChildren = (PDBGGUISTATSNODE *)pvNew;
856 }
857
858 /*
859 * Insert it.
860 */
861 pNode->pParent = pParent;
862 if (iPosition >= pParent->cChildren)
863 /* Last. */
864 iPosition = pParent->cChildren;
865 else
866 {
867 /* Shift all the items after ours. */
868 uint32_t iShift = pParent->cChildren;
869 while (iShift-- > iPosition)
870 {
871 PDBGGUISTATSNODE pChild = pParent->papChildren[iShift];
872 pParent->papChildren[iShift + 1] = pChild;
873 pChild->iSelf = iShift + 1;
874 }
875 }
876
877 /* Insert ours */
878 pNode->iSelf = iPosition;
879 pParent->papChildren[iPosition] = pNode;
880 pParent->cChildren++;
881
882 return pNode;
883}
884
885
886PDBGGUISTATSNODE
887VBoxDbgStatsModel::createAndInsert(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition)
888{
889 PDBGGUISTATSNODE pNode;
890 if (m_fUpdateInsertRemove)
891 pNode = createAndInsertNode(pParent, pszName, cchName, iPosition);
892 else
893 {
894 beginInsertRows(createIndex(pParent->iSelf, 0, pParent), 0, 0);
895 pNode = createAndInsertNode(pParent, pszName, cchName, iPosition);
896 endInsertRows();
897 }
898 return pNode;
899}
900
901/*static*/ PDBGGUISTATSNODE
902VBoxDbgStatsModel::removeNode(PDBGGUISTATSNODE pNode)
903{
904 PDBGGUISTATSNODE pParent = pNode->pParent;
905 if (pParent)
906 {
907 uint32_t iPosition = pNode->iSelf;
908 Assert(pParent->papChildren[iPosition] == pNode);
909 uint32_t const cChildren = --pParent->cChildren;
910 for (; iPosition < cChildren; iPosition++)
911 {
912 PDBGGUISTATSNODE pChild = pParent->papChildren[iPosition + 1];
913 pParent->papChildren[iPosition] = pChild;
914 pChild->iSelf = iPosition;
915 }
916#ifdef VBOX_STRICT /* poison */
917 pParent->papChildren[iPosition] = (PDBGGUISTATSNODE)0x42;
918#endif
919 }
920 return pNode;
921}
922
923
924/*static*/ void
925VBoxDbgStatsModel::removeAndDestroyNode(PDBGGUISTATSNODE pNode)
926{
927 removeNode(pNode);
928 destroyNode(pNode);
929}
930
931
932void
933VBoxDbgStatsModel::removeAndDestroy(PDBGGUISTATSNODE pNode)
934{
935 if (m_fUpdateInsertRemove)
936 removeAndDestroyNode(pNode);
937 else
938 {
939 /*
940 * Removing is fun since the docs are imprecise as to how persistent
941 * indexes are updated (or aren't). So, let try a few different ideas
942 * and see which works.
943 */
944#if 1
945 /* destroy the children first with the appropriate begin/endRemoveRows signals. */
946 DBGGUISTATSSTACK Stack;
947 Stack.a[0].pNode = pNode;
948 Stack.a[0].iChild = -1;
949 Stack.iTop = 0;
950 while (Stack.iTop >= 0)
951 {
952 /* get top element */
953 PDBGGUISTATSNODE pNode = Stack.a[Stack.iTop].pNode;
954 uint32_t iChild = ++Stack.a[Stack.iTop].iChild;
955 if (iChild < pNode->cChildren)
956 {
957 /* push */
958 Stack.iTop++;
959 Assert(Stack.iTop < (int32_t)RT_ELEMENTS(Stack.a));
960 Stack.a[Stack.iTop].pNode = pNode->papChildren[iChild];
961 Stack.a[Stack.iTop].iChild = 0;
962 }
963 else
964 {
965 /* pop and destroy all the children. */
966 Stack.iTop--;
967 uint32_t i = pNode->cChildren;
968 if (i)
969 {
970 beginRemoveRows(createIndex(pNode->iSelf, 0, pNode), 0, i - 1);
971 while (i-- > 0)
972 destroyNode(pNode->papChildren[i]);
973 pNode->cChildren = 0;
974 endRemoveRows();
975 }
976 }
977 }
978 Assert(!pNode->cChildren);
979
980 /* finally the node it self. */
981 PDBGGUISTATSNODE pParent = pNode->pParent;
982 beginRemoveRows(createIndex(pParent->iSelf, 0, pParent), pNode->iSelf, pNode->iSelf);
983 removeAndDestroyNode(pNode);
984 endRemoveRows();
985
986#elif 0
987 /* This ain't working, leaves invalid indexes behind. */
988 PDBGGUISTATSNODE pParent = pNode->pParent;
989 beginRemoveRows(createIndex(pParent->iSelf, 0, pParent), pNode->iSelf, pNode->iSelf);
990 removeAndDestroyNode(pNode);
991 endRemoveRows();
992#else
993 /* Force reset() of the model after the update. */
994 m_fUpdateInsertRemove = true;
995 removeAndDestroyNode(pNode);
996#endif
997 }
998}
999
1000
1001/*static*/ void
1002VBoxDbgStatsModel::resetNode(PDBGGUISTATSNODE pNode)
1003{
1004 /* free and reinit the data. */
1005 if (pNode->enmType == STAMTYPE_CALLBACK)
1006 {
1007 delete pNode->Data.pStr;
1008 pNode->Data.pStr = NULL;
1009 }
1010 pNode->enmType = STAMTYPE_INVALID;
1011
1012 /* free the description. */
1013 if (pNode->pDescStr)
1014 {
1015 delete pNode->pDescStr;
1016 pNode->pDescStr = NULL;
1017 }
1018}
1019
1020
1021/*static*/ int
1022VBoxDbgStatsModel::initNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc)
1023{
1024 /*
1025 * Copy the data.
1026 */
1027 pNode->enmUnit = enmUnit;
1028 Assert(pNode->enmType == STAMTYPE_INVALID);
1029 pNode->enmType = enmType;
1030 if (pszDesc)
1031 pNode->pDescStr = new QString(pszDesc); /* ignore allocation failure (well, at least up to the point we can ignore it) */
1032
1033 switch (enmType)
1034 {
1035 case STAMTYPE_COUNTER:
1036 pNode->Data.Counter = *(PSTAMCOUNTER)pvSample;
1037 break;
1038
1039 case STAMTYPE_PROFILE:
1040 case STAMTYPE_PROFILE_ADV:
1041 pNode->Data.Profile = *(PSTAMPROFILE)pvSample;
1042 break;
1043
1044 case STAMTYPE_RATIO_U32:
1045 case STAMTYPE_RATIO_U32_RESET:
1046 pNode->Data.RatioU32 = *(PSTAMRATIOU32)pvSample;
1047 break;
1048
1049 case STAMTYPE_CALLBACK:
1050 {
1051 const char *pszString = (const char *)pvSample;
1052 pNode->Data.pStr = new QString(pszString);
1053 break;
1054 }
1055
1056 case STAMTYPE_U8:
1057 case STAMTYPE_U8_RESET:
1058 case STAMTYPE_X8:
1059 case STAMTYPE_X8_RESET:
1060 pNode->Data.u8 = *(uint8_t *)pvSample;
1061 break;
1062
1063 case STAMTYPE_U16:
1064 case STAMTYPE_U16_RESET:
1065 case STAMTYPE_X16:
1066 case STAMTYPE_X16_RESET:
1067 pNode->Data.u16 = *(uint16_t *)pvSample;
1068 break;
1069
1070 case STAMTYPE_U32:
1071 case STAMTYPE_U32_RESET:
1072 case STAMTYPE_X32:
1073 case STAMTYPE_X32_RESET:
1074 pNode->Data.u32 = *(uint32_t *)pvSample;
1075 break;
1076
1077 case STAMTYPE_U64:
1078 case STAMTYPE_U64_RESET:
1079 case STAMTYPE_X64:
1080 case STAMTYPE_X64_RESET:
1081 pNode->Data.u64 = *(uint64_t *)pvSample;
1082 break;
1083
1084 default:
1085 AssertMsgFailed(("%d\n", enmType));
1086 break;
1087 }
1088
1089 return VINF_SUCCESS;
1090}
1091
1092
1093
1094
1095/*static*/ void
1096VBoxDbgStatsModel::updateNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, const char *pszDesc)
1097{
1098
1099 /*
1100 * Reset and init the node if the type changed.
1101 */
1102 if (enmType != pNode->enmType)
1103 {
1104 if (pNode->enmType != STAMTYPE_INVALID)
1105 resetNode(pNode);
1106 initNode(pNode, enmType, pvSample, enmUnit, pszDesc);
1107 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1108 }
1109 else
1110 {
1111 /*
1112 * ASSUME that only the sample value will change and that the unit, visibility
1113 * and description remains the same.
1114 */
1115
1116 int64_t iDelta;
1117 switch (enmType)
1118 {
1119 case STAMTYPE_COUNTER:
1120 {
1121 uint64_t cPrev = pNode->Data.Counter.c;
1122 pNode->Data.Counter = *(PSTAMCOUNTER)pvSample;
1123 iDelta = pNode->Data.Counter.c - cPrev;
1124 if (iDelta || pNode->i64Delta)
1125 {
1126 pNode->i64Delta = iDelta;
1127 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1128 }
1129 break;
1130 }
1131
1132 case STAMTYPE_PROFILE:
1133 case STAMTYPE_PROFILE_ADV:
1134 {
1135 uint64_t cPrevPeriods = pNode->Data.Profile.cPeriods;
1136 pNode->Data.Profile = *(PSTAMPROFILE)pvSample;
1137 iDelta = pNode->Data.Profile.cPeriods - cPrevPeriods;
1138 if (iDelta || pNode->i64Delta)
1139 {
1140 pNode->i64Delta = iDelta;
1141 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1142 }
1143 break;
1144 }
1145
1146 case STAMTYPE_RATIO_U32:
1147 case STAMTYPE_RATIO_U32_RESET:
1148 {
1149 STAMRATIOU32 Prev = pNode->Data.RatioU32;
1150 pNode->Data.RatioU32 = *(PSTAMRATIOU32)pvSample;
1151 int32_t iDeltaA = pNode->Data.RatioU32.u32A - Prev.u32A;
1152 int32_t iDeltaB = pNode->Data.RatioU32.u32B - Prev.u32B;
1153 if (iDeltaA == 0 && iDeltaB == 0)
1154 {
1155 if (pNode->i64Delta)
1156 {
1157 pNode->i64Delta = 0;
1158 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1159 }
1160 }
1161 else
1162 {
1163 if (iDeltaA >= 0)
1164 pNode->i64Delta = iDeltaA + (iDeltaB >= 0 ? iDeltaB : -iDeltaB);
1165 else
1166 pNode->i64Delta = iDeltaA + (iDeltaB < 0 ? iDeltaB : -iDeltaB);
1167 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1168 }
1169 break;
1170 }
1171
1172 case STAMTYPE_CALLBACK:
1173 {
1174 const char *pszString = (const char *)pvSample;
1175 if (!pNode->Data.pStr)
1176 {
1177 pNode->Data.pStr = new QString(pszString);
1178 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1179 }
1180 else if (*pNode->Data.pStr == pszString)
1181 {
1182 delete pNode->Data.pStr;
1183 pNode->Data.pStr = new QString(pszString);
1184 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1185 }
1186 break;
1187 }
1188
1189 case STAMTYPE_U8:
1190 case STAMTYPE_U8_RESET:
1191 case STAMTYPE_X8:
1192 case STAMTYPE_X8_RESET:
1193 {
1194 uint8_t uPrev = pNode->Data.u8;
1195 pNode->Data.u8 = *(uint8_t *)pvSample;
1196 iDelta = (int32_t)pNode->Data.u8 - (int32_t)uPrev;
1197 if (iDelta || pNode->i64Delta)
1198 {
1199 pNode->i64Delta = iDelta;
1200 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1201 }
1202 break;
1203 }
1204
1205 case STAMTYPE_U16:
1206 case STAMTYPE_U16_RESET:
1207 case STAMTYPE_X16:
1208 case STAMTYPE_X16_RESET:
1209 {
1210 uint16_t uPrev = pNode->Data.u16;
1211 pNode->Data.u16 = *(uint16_t *)pvSample;
1212 iDelta = (int32_t)pNode->Data.u16 - (int32_t)uPrev;
1213 if (iDelta || pNode->i64Delta)
1214 {
1215 pNode->i64Delta = iDelta;
1216 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1217 }
1218 break;
1219 }
1220
1221 case STAMTYPE_U32:
1222 case STAMTYPE_U32_RESET:
1223 case STAMTYPE_X32:
1224 case STAMTYPE_X32_RESET:
1225 {
1226 uint32_t uPrev = pNode->Data.u32;
1227 pNode->Data.u32 = *(uint32_t *)pvSample;
1228 iDelta = (int64_t)pNode->Data.u32 - (int64_t)uPrev;
1229 if (iDelta || pNode->i64Delta)
1230 {
1231 pNode->i64Delta = iDelta;
1232 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1233 }
1234 break;
1235 }
1236
1237 case STAMTYPE_U64:
1238 case STAMTYPE_U64_RESET:
1239 case STAMTYPE_X64:
1240 case STAMTYPE_X64_RESET:
1241 {
1242 uint64_t uPrev = pNode->Data.u64;
1243 pNode->Data.u64 = *(uint64_t *)pvSample;
1244 iDelta = pNode->Data.u64 - uPrev;
1245 if (iDelta || pNode->i64Delta)
1246 {
1247 pNode->i64Delta = iDelta;
1248 pNode->enmState = kDbgGuiStatsNodeState_kRefresh;
1249 }
1250 break;
1251 }
1252 default:
1253 AssertMsgFailed(("%d\n", enmType));
1254 break;
1255 }
1256 }
1257}
1258
1259
1260/*static*/ ssize_t
1261VBoxDbgStatsModel::getNodePath(PCDBGGUISTATSNODE pNode, char *psz, ssize_t cch)
1262{
1263 ssize_t off;
1264 if (!pNode->pParent)
1265 {
1266 /* root - don't add it's slash! */
1267 AssertReturn(cch >= 1, -1);
1268 off = 0;
1269 *psz = '\0';
1270 }
1271 else
1272 {
1273 cch -= pNode->cchName + 1;
1274 AssertReturn(cch > 0, -1);
1275 off = getNodePath(pNode->pParent, psz, cch);
1276 if (off >= 0)
1277 {
1278 psz[off++] = '/';
1279 memcpy(&psz[off], pNode->pszName, pNode->cchName + 1);
1280 off += pNode->cchName;
1281 }
1282 }
1283 return off;
1284}
1285
1286
1287/*static*/ bool
1288VBoxDbgStatsModel::isNodeAncestorOf(PCDBGGUISTATSNODE pAncestor, PCDBGGUISTATSNODE pDescendant)
1289{
1290 while (pDescendant)
1291 {
1292 pDescendant = pDescendant->pParent;
1293 if (pDescendant == pAncestor)
1294 return true;
1295 }
1296 return false;
1297}
1298
1299
1300/*static*/ PDBGGUISTATSNODE
1301VBoxDbgStatsModel::nextNode(PDBGGUISTATSNODE pNode)
1302{
1303 if (!pNode)
1304 return NULL;
1305
1306 /* decend to children. */
1307 if (pNode->cChildren)
1308 return pNode->papChildren[0];
1309
1310 PDBGGUISTATSNODE pParent = pNode->pParent;
1311 if (!pParent)
1312 return NULL;
1313
1314 /* next sibling. */
1315 if (pNode->iSelf + 1 < pNode->pParent->cChildren)
1316 return pParent->papChildren[pNode->iSelf + 1];
1317
1318 /* ascend and advanced to a parent's sibiling. */
1319 for (;;)
1320 {
1321 uint32_t iSelf = pParent->iSelf;
1322 pParent = pParent->pParent;
1323 if (!pParent)
1324 return NULL;
1325 if (iSelf + 1 < pParent->cChildren)
1326 return pParent->papChildren[iSelf + 1];
1327 }
1328}
1329
1330
1331/*static*/ PDBGGUISTATSNODE
1332VBoxDbgStatsModel::nextDataNode(PDBGGUISTATSNODE pNode)
1333{
1334 do
1335 pNode = nextNode(pNode);
1336 while ( pNode
1337 && pNode->enmType == STAMTYPE_INVALID);
1338 return pNode;
1339}
1340
1341
1342/*static*/ PDBGGUISTATSNODE
1343VBoxDbgStatsModel::prevNode(PDBGGUISTATSNODE pNode)
1344{
1345 if (!pNode)
1346 return NULL;
1347 PDBGGUISTATSNODE pParent = pNode->pParent;
1348 if (!pParent)
1349 return NULL;
1350
1351 /* previous sibling's latest decendant (better expression anyone?). */
1352 if (pNode->iSelf > 0)
1353 {
1354 pNode = pParent->papChildren[pNode->iSelf - 1];
1355 while (pNode->cChildren)
1356 pNode = pNode->papChildren[pNode->cChildren - 1];
1357 return pNode;
1358 }
1359
1360 /* ascend to the parent. */
1361 return pParent;
1362}
1363
1364
1365/*static*/ PDBGGUISTATSNODE
1366VBoxDbgStatsModel::prevDataNode(PDBGGUISTATSNODE pNode)
1367{
1368 do
1369 pNode = prevNode(pNode);
1370 while ( pNode
1371 && pNode->enmType == STAMTYPE_INVALID);
1372 return pNode;
1373}
1374
1375
1376#if 0
1377/*static*/ PDBGGUISTATSNODE
1378VBoxDbgStatsModel::createNewTree(IMachineDebugger *a_pIMachineDebugger)
1379{
1380 /** @todo */
1381 return NULL;
1382}
1383#endif
1384
1385
1386#if 0
1387/*static*/ PDBGGUISTATSNODE
1388VBoxDbgStatsModel::createNewTree(const char *pszFilename)
1389{
1390 /** @todo */
1391 return NULL;
1392}
1393#endif
1394
1395
1396#if 0
1397/*static*/ PDBGGUISTATSNODE
1398VBoxDbgStatsModel::createDiffTree(PDBGGUISTATSNODE pTree1, PDBGGUISTATSNODE pTree2)
1399{
1400 /** @todo */
1401 return NULL;
1402}
1403#endif
1404
1405
1406PDBGGUISTATSNODE
1407VBoxDbgStatsModel::updateCallbackHandleOutOfOrder(const char *pszName)
1408{
1409 /*
1410 * We might be inserting a new node between pPrev and pNode
1411 * or we might be removing one or more nodes. Either case is
1412 * handled in the same rough way.
1413 *
1414 * Might consider optimizing insertion at some later point since this
1415 * is a normal occurance (dynamic statistics in PATM, IOM, MM, ++).
1416 */
1417 Assert(pszName[0] == '/');
1418 Assert(m_szUpdateParent[m_cchUpdateParent - 1] == '/');
1419
1420 /*
1421 * Start with the current parent node and look for a common ancestor
1422 * hoping that this is faster than going from the root (saves lookup).
1423 */
1424 PDBGGUISTATSNODE pNode = m_pUpdateParent->papChildren[m_iUpdateChild];
1425 PDBGGUISTATSNODE const pPrev = prevDataNode(pNode);
1426 pNode = pNode->pParent;
1427 while (pNode != m_pRoot)
1428 {
1429 if (!strncmp(pszName, m_szUpdateParent, m_cchUpdateParent))
1430 break;
1431 Assert(m_cchUpdateParent > pNode->cchName);
1432 m_cchUpdateParent -= pNode->cchName + 1;
1433 m_szUpdateParent[m_cchUpdateParent] = '\0';
1434 pNode = pNode->pParent;
1435 }
1436 Assert(m_szUpdateParent[m_cchUpdateParent - 1] == '/');
1437
1438 /*
1439 * Descend until we've found/created the node pszName indicates,
1440 * modifying m_szUpdateParent as we go along.
1441 */
1442 while (pszName[m_cchUpdateParent - 1] == '/')
1443 {
1444 /* Find the end of this component. */
1445 const char *const pszSubName = &pszName[m_cchUpdateParent];
1446 const char *pszEnd = strchr(pszSubName, '/');
1447 if (!pszEnd)
1448 pszEnd = strchr(pszSubName, '\0');
1449 size_t cchSubName = pszEnd - pszSubName;
1450
1451 /* Add the name to the path. */
1452 memcpy(&m_szUpdateParent[m_cchUpdateParent], pszSubName, cchSubName);
1453 m_cchUpdateParent += cchSubName;
1454 m_szUpdateParent[m_cchUpdateParent++] = '/';
1455 m_szUpdateParent[m_cchUpdateParent] = '\0';
1456 Assert(m_cchUpdateParent < sizeof(m_szUpdateParent));
1457
1458 if (!pNode->cChildren)
1459 {
1460 /* first child */
1461 pNode = createAndInsert(pNode, pszSubName, cchSubName, 0);
1462 AssertReturn(pNode, NULL);
1463 }
1464 else
1465 {
1466 /* binary search. */
1467 int32_t iStart = 0;
1468 int32_t iLast = pNode->cChildren - 1;
1469 for (;;)
1470 {
1471 int32_t i = iStart + (iLast + 1 - iStart) / 2;
1472 int iDiff = memcmp(pszSubName, pNode->papChildren[i]->pszName, cchSubName);
1473 if (!iDiff)
1474 iDiff = '\0' - pNode->papChildren[i]->pszName[cchSubName];
1475 if (iDiff > 0)
1476 {
1477 iStart = i + 1;
1478 if (iStart > iLast)
1479 {
1480 pNode = createAndInsert(pNode, pszSubName, cchSubName, iStart);
1481 AssertReturn(pNode, NULL);
1482 break;
1483 }
1484 }
1485 else if (iDiff < 0)
1486 {
1487 iLast = i - 1;
1488 if (iLast < iStart)
1489 {
1490 pNode = createAndInsert(pNode, pszSubName, cchSubName, i);
1491 AssertReturn(pNode, NULL);
1492 break;
1493 }
1494 }
1495 else
1496 {
1497 pNode = pNode->papChildren[i];
1498 break;
1499 }
1500 }
1501 }
1502 }
1503 Assert( !memcmp(pszName, m_szUpdateParent, m_cchUpdateParent - 2)
1504 && pszName[m_cchUpdateParent - 1] == '\0');
1505
1506 /*
1507 * Remove all the nodes between pNode and pPrev but keep all
1508 * of pNode's ancestors (or it'll get orphaned).
1509 */
1510 PDBGGUISTATSNODE pCur = prevNode(pNode);
1511 while (pCur != pPrev)
1512 {
1513 PDBGGUISTATSNODE pAdv = prevNode(pCur); Assert(pAdv || !pPrev);
1514 if (!isNodeAncestorOf(pCur, pNode))
1515 {
1516 Assert(pCur != m_pRoot);
1517 removeAndDestroy(pCur);
1518 }
1519 pCur = pAdv;
1520 }
1521
1522 /*
1523 * Remove the data from all ancestors of pNode that it doesn't
1524 * share them pPrev.
1525 */
1526 if (pPrev)
1527 {
1528 pCur = pNode->pParent;
1529 while (!isNodeAncestorOf(pCur, pPrev))
1530 {
1531 resetNode(pNode);
1532 pCur = pCur->pParent;
1533 }
1534 }
1535
1536 /*
1537 * Finally, adjust the globals (szUpdateParent is one level too deep).
1538 */
1539 Assert(m_cchUpdateParent > pNode->cchName + 1);
1540 m_cchUpdateParent -= pNode->cchName + 1;
1541 m_szUpdateParent[m_cchUpdateParent] = '\0';
1542 m_pUpdateParent = pNode->pParent;
1543 m_iUpdateChild = pNode->iSelf;
1544
1545 return pNode;
1546}
1547
1548
1549PDBGGUISTATSNODE
1550VBoxDbgStatsModel::updateCallbackHandleTail(const char *pszName)
1551{
1552 /*
1553 * Insert it at the end of the tree.
1554 *
1555 * Do the same as we're doing down in createNewTreeCallback, walk from the
1556 * root and create whatever we need.
1557 */
1558 AssertReturn(*pszName == '/' && pszName[1] != '/', NULL);
1559 PDBGGUISTATSNODE pNode = m_pRoot;
1560 const char *pszCur = pszName + 1;
1561 while (*pszCur)
1562 {
1563 /* Find the end of this component. */
1564 const char *pszNext = strchr(pszCur, '/');
1565 if (!pszNext)
1566 pszNext = strchr(pszCur, '\0');
1567 size_t cchCur = pszNext - pszCur;
1568
1569 /* Create it if it doesn't exist (it will be last if it exists). */
1570 if ( !pNode->cChildren
1571 || strncmp(pNode->papChildren[pNode->cChildren - 1]->pszName, pszCur, cchCur)
1572 || pNode->papChildren[pNode->cChildren - 1]->pszName[cchCur])
1573 {
1574 pNode = createAndInsert(pNode, pszCur, pszNext - pszCur, pNode->cChildren);
1575 AssertReturn(pNode, NULL);
1576 }
1577 else
1578 pNode = pNode->papChildren[pNode->cChildren - 1];
1579
1580 /* Advance */
1581 pszCur = *pszNext ? pszNext + 1 : pszNext;
1582 }
1583
1584 return pNode;
1585}
1586
1587
1588void
1589VBoxDbgStatsModel::updateCallbackAdvance(PDBGGUISTATSNODE pNode)
1590{
1591 /*
1592 * Advance to the next node with data.
1593 *
1594 * ASSUMES a leaf *must* have data and again we're ASSUMING the sorting
1595 * on slash separated sub-strings.
1596 */
1597 if (m_iUpdateChild != UINT32_MAX)
1598 {
1599#ifdef VBOX_STRICT
1600 PDBGGUISTATSNODE const pCorrectNext = nextDataNode(pNode);
1601#endif
1602 PDBGGUISTATSNODE pParent = pNode->pParent;
1603 if (pNode->cChildren)
1604 {
1605 /* decend to the first child. */
1606 Assert(m_cchUpdateParent + pNode->cchName + 2 < sizeof(m_szUpdateParent));
1607 memcpy(&m_szUpdateParent[m_cchUpdateParent], pNode->pszName, pNode->cchName);
1608 m_cchUpdateParent += pNode->cchName;
1609 m_szUpdateParent[m_cchUpdateParent++] = '/';
1610 m_szUpdateParent[m_cchUpdateParent] = '\0';
1611
1612 pNode = pNode->papChildren[0];
1613 }
1614 else if (pNode->iSelf + 1 < pParent->cChildren)
1615 {
1616 /* next sibling or one if its descendants. */
1617 Assert(m_pUpdateParent == pParent);
1618 pNode = pParent->papChildren[pNode->iSelf + 1];
1619 }
1620 else
1621 {
1622 /* move up and down- / on-wards */
1623 for (;;)
1624 {
1625 /* ascend */
1626 pNode = pParent;
1627 pParent = pParent->pParent;
1628 if (!pParent)
1629 {
1630 Assert(pNode == m_pRoot);
1631 m_iUpdateChild = UINT32_MAX;
1632 m_szUpdateParent[0] = '\0';
1633 m_cchUpdateParent = 0;
1634 m_pUpdateParent = NULL;
1635 break;
1636 }
1637 Assert(m_cchUpdateParent > pNode->cchName + 1);
1638 m_cchUpdateParent -= pNode->cchName + 1;
1639
1640 /* try advance */
1641 if (pNode->iSelf + 1 < pParent->cChildren)
1642 {
1643 pNode = pParent->papChildren[pNode->iSelf + 1];
1644 m_szUpdateParent[m_cchUpdateParent] = '\0';
1645 break;
1646 }
1647 }
1648 }
1649
1650 /* decend to a node containing data and finalize the globals. (ASSUMES leaf has data.) */
1651 if (m_iUpdateChild != UINT32_MAX)
1652 {
1653 while ( pNode->enmType == STAMTYPE_INVALID
1654 && pNode->cChildren > 0)
1655 {
1656 Assert(pNode->enmState == kDbgGuiStatsNodeState_kVisible);
1657
1658 Assert(m_cchUpdateParent + pNode->cchName + 2 < sizeof(m_szUpdateParent));
1659 memcpy(&m_szUpdateParent[m_cchUpdateParent], pNode->pszName, pNode->cchName);
1660 m_cchUpdateParent += pNode->cchName;
1661 m_szUpdateParent[m_cchUpdateParent++] = '/';
1662 m_szUpdateParent[m_cchUpdateParent] = '\0';
1663
1664 pNode = pNode->papChildren[0];
1665 }
1666 Assert(pNode->enmType != STAMTYPE_INVALID);
1667 m_iUpdateChild = pNode->iSelf;
1668 m_pUpdateParent = pNode->pParent;
1669 Assert(pNode == pCorrectNext);
1670 }
1671 }
1672 /* else: we're at the end */
1673}
1674
1675
1676/*static*/ DECLCALLBACK(int)
1677VBoxDbgStatsModel::updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
1678 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
1679{
1680 VBoxDbgStatsModelVM *pThis = (VBoxDbgStatsModelVM *)pvUser;
1681 Log3(("updateCallback: %s\n", pszName));
1682
1683 /*
1684 * Skip the ones which shouldn't be visible in the GUI.
1685 */
1686 if (enmVisibility == STAMVISIBILITY_NOT_GUI)
1687 return 0;
1688
1689 /*
1690 * The default assumption is that nothing has changed.
1691 * For now we'll reset the model when ever something changes.
1692 */
1693 PDBGGUISTATSNODE pNode;
1694 if (pThis->m_iUpdateChild != UINT32_MAX)
1695 {
1696 pNode = pThis->m_pUpdateParent->papChildren[pThis->m_iUpdateChild];
1697 if ( !strncmp(pszName, pThis->m_szUpdateParent, pThis->m_cchUpdateParent)
1698 && !strcmp(pszName + pThis->m_cchUpdateParent, pNode->pszName))
1699 /* got it! */;
1700 else
1701 {
1702 /* insert/remove */
1703 pNode = pThis->updateCallbackHandleOutOfOrder(pszName);
1704 if (!pNode)
1705 return VERR_NO_MEMORY;
1706 }
1707 }
1708 else
1709 {
1710 /* append */
1711 pNode = pThis->updateCallbackHandleTail(pszName);
1712 if (!pNode)
1713 return VERR_NO_MEMORY;
1714 }
1715
1716 /*
1717 * Perform the update and advance to the next one.
1718 */
1719 updateNode(pNode, enmType, pvSample, enmUnit, pszDesc);
1720 pThis->updateCallbackAdvance(pNode);
1721
1722 return VINF_SUCCESS;
1723}
1724
1725
1726bool
1727VBoxDbgStatsModel::updatePrepare(void)
1728{
1729 /*
1730 * Find the first child with data and set it up as the 'next'
1731 * node to be updated.
1732 */
1733 Assert(m_pRoot);
1734 Assert(m_pRoot->enmType == STAMTYPE_INVALID);
1735 PDBGGUISTATSNODE pFirst = nextDataNode(m_pRoot);
1736 if (pFirst)
1737 {
1738 m_iUpdateChild = pFirst->iSelf;
1739 m_pUpdateParent = pFirst->pParent; Assert(m_pUpdateParent);
1740 m_cchUpdateParent = getNodePath(m_pUpdateParent, m_szUpdateParent, sizeof(m_szUpdateParent) - 1);
1741 AssertReturn(m_cchUpdateParent >= 1, false);
1742 m_szUpdateParent[m_cchUpdateParent++] = '/';
1743 m_szUpdateParent[m_cchUpdateParent] = '\0';
1744 }
1745 else
1746 {
1747 m_iUpdateChild = UINT32_MAX;
1748 m_pUpdateParent = NULL;
1749 m_szUpdateParent[0] = '\0';
1750 m_cchUpdateParent = 0;
1751 }
1752
1753 /*
1754 * Set the flag and signal possible layout change.
1755 */
1756 m_fUpdateInsertRemove = false;
1757 /* emit layoutAboutToBeChanged(); - debug this, it gets stuck... */
1758 return true;
1759}
1760
1761
1762bool
1763VBoxDbgStatsModel::updateDone(bool a_fSuccess)
1764{
1765 /*
1766 * Remove any nodes following the last in the update (unless the update failed).
1767 */
1768 if ( a_fSuccess
1769 && m_iUpdateChild != UINT32_MAX)
1770 {
1771 PDBGGUISTATSNODE const pLast = prevDataNode(m_pUpdateParent->papChildren[m_iUpdateChild]);
1772 if (!pLast)
1773 {
1774 /* nuking the whole tree. */
1775 setRootNode(createRootNode());
1776 m_fUpdateInsertRemove = true;
1777 }
1778 else
1779 {
1780 PDBGGUISTATSNODE pNode;
1781 while ((pNode = nextNode(pLast)))
1782 {
1783 Assert(pNode != m_pRoot);
1784 removeAndDestroy(pNode);
1785 }
1786 }
1787 }
1788
1789 /*
1790 * We're done making layout changes (if I understood it correctly), so,
1791 * signal this and then see what to do next. If we did too many removals
1792 * we'll just reset the whole shebang.
1793 */
1794 if (m_fUpdateInsertRemove)
1795 {
1796 /* emit layoutChanged(); - hrmpf, doesn't work reliably... */
1797 reset();
1798 }
1799 else
1800 {
1801 /*
1802 * Send dataChanged events.
1803 *
1804 * We do this here instead of from the updateCallback because it lesses
1805 * the clutter in that method and allow us to emit bulk signals in an
1806 * easier way because we can traverse the tree in a different fashion.
1807 */
1808 DBGGUISTATSSTACK Stack;
1809 Stack.a[0].pNode = m_pRoot;
1810 Stack.a[0].iChild = -1;
1811 Stack.iTop = 0;
1812
1813 while (Stack.iTop >= 0)
1814 {
1815 /* get top element */
1816 PDBGGUISTATSNODE pNode = Stack.a[Stack.iTop].pNode;
1817 uint32_t iChild = ++Stack.a[Stack.iTop].iChild;
1818 if (iChild < pNode->cChildren)
1819 {
1820 /* push */
1821 Stack.iTop++;
1822 Assert(Stack.iTop < (int32_t)RT_ELEMENTS(Stack.a));
1823 Stack.a[Stack.iTop].pNode = pNode->papChildren[iChild];
1824 Stack.a[Stack.iTop].iChild = 0;
1825 }
1826 else
1827 {
1828 /* pop */
1829 Stack.iTop--;
1830
1831 /* do the actual work. */
1832 iChild = 0;
1833 while (iChild < pNode->cChildren)
1834 {
1835 /* skip to the first needing updating. */
1836 while ( iChild < pNode->cChildren
1837 && pNode->papChildren[iChild]->enmState != kDbgGuiStatsNodeState_kRefresh)
1838 iChild++;
1839 if (iChild >= pNode->cChildren)
1840 break;
1841 QModelIndex TopLeft = createIndex(iChild, 0, pNode->papChildren[iChild]);
1842 pNode->papChildren[iChild]->enmState = kDbgGuiStatsNodeState_kVisible;
1843
1844 /* any subsequent nodes that also needs refreshing? */
1845 if ( ++iChild < pNode->cChildren
1846 && pNode->papChildren[iChild]->enmState == kDbgGuiStatsNodeState_kRefresh)
1847 {
1848 do pNode->papChildren[iChild]->enmState = kDbgGuiStatsNodeState_kVisible;
1849 while ( ++iChild < pNode->cChildren
1850 && pNode->papChildren[iChild]->enmState == kDbgGuiStatsNodeState_kRefresh);
1851 QModelIndex BottomRight = createIndex(iChild - 1, DBGGUI_STATS_COLUMNS - 1, pNode->papChildren[iChild - 1]);
1852
1853 /* emit the refresh signal */
1854 emit dataChanged(TopLeft, BottomRight);
1855 }
1856 else
1857 {
1858 /* emit the refresh signal */
1859 emit dataChanged(TopLeft, TopLeft);
1860 }
1861 }
1862 }
1863 }
1864 /* emit layoutChanged(); - hrmpf, doesn't work reliably... */
1865 }
1866
1867 return m_fUpdateInsertRemove;
1868}
1869
1870
1871bool
1872VBoxDbgStatsModel::updateStatsByPattern(const QString &a_rPatStr)
1873{
1874 /* stub */
1875 NOREF(a_rPatStr);
1876 return false;
1877}
1878
1879
1880void
1881VBoxDbgStatsModel::updateStatsByIndex(QModelIndex const &a_rIndex)
1882{
1883 /** @todo implement this based on updateStatsByPattern. */
1884 NOREF(a_rIndex);
1885}
1886
1887
1888void
1889VBoxDbgStatsModel::resetStatsByPattern(QString const &a_rPatStr)
1890{
1891 /* stub */
1892 NOREF(a_rPatStr);
1893}
1894
1895
1896void
1897VBoxDbgStatsModel::resetStatsByIndex(QModelIndex const &a_rIndex, bool fSubTree /*= true*/)
1898{
1899 PCDBGGUISTATSNODE pNode = nodeFromIndex(a_rIndex);
1900 if (pNode == m_pRoot || !a_rIndex.isValid())
1901 {
1902 if (fSubTree)
1903 {
1904 /* everything from the root down. */
1905 resetStatsByPattern(QString());
1906 }
1907 }
1908 else if (pNode)
1909 {
1910 /* the node pattern. */
1911 char szPat[1024+1024+4];
1912 ssize_t cch = getNodePath(pNode, szPat, 1024);
1913 AssertReturnVoid(cch >= 0);
1914
1915 /* the sub-tree pattern. */
1916 if (fSubTree && pNode->cChildren)
1917 {
1918 char *psz = &szPat[cch];
1919 *psz++ = '|';
1920 memcpy(psz, szPat, cch);
1921 psz += cch;
1922 *psz++ = '/';
1923 *psz++ = '*';
1924 *psz++ = '\0';
1925 }
1926
1927 resetStatsByPattern(szPat);
1928 }
1929}
1930
1931
1932QModelIndex
1933VBoxDbgStatsModel::getRootIndex(void) const
1934{
1935 if (!m_pRoot)
1936 return QModelIndex();
1937 return createIndex(0, 0, m_pRoot);
1938}
1939
1940
1941void
1942VBoxDbgStatsModel::setRootNode(PDBGGUISTATSNODE a_pRoot)
1943{
1944 PDBGGUISTATSNODE pOldTree = m_pRoot;
1945 m_pRoot = a_pRoot;
1946 destroyTree(pOldTree);
1947 reset();
1948}
1949
1950
1951Qt::ItemFlags
1952VBoxDbgStatsModel::flags(const QModelIndex &a_rIndex) const
1953{
1954 Qt::ItemFlags fFlags = QAbstractItemModel::flags(a_rIndex);
1955 return fFlags;
1956}
1957
1958
1959int
1960VBoxDbgStatsModel::columnCount(const QModelIndex &a_rParent) const
1961{
1962 NOREF(a_rParent);
1963 return DBGGUI_STATS_COLUMNS;
1964}
1965
1966
1967int
1968VBoxDbgStatsModel::rowCount(const QModelIndex &a_rParent) const
1969{
1970 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
1971 return pParent ? pParent->cChildren : 1 /* root */;
1972}
1973
1974
1975bool
1976VBoxDbgStatsModel::hasChildren(const QModelIndex &a_rParent) const
1977{
1978 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
1979 return pParent ? pParent->cChildren > 0 : true /* root */;
1980}
1981
1982
1983QModelIndex
1984VBoxDbgStatsModel::index(int iRow, int iColumn, const QModelIndex &a_rParent) const
1985{
1986 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
1987 if (!pParent)
1988 {
1989 if ( a_rParent.isValid()
1990 || iRow
1991 || (unsigned)iColumn < DBGGUI_STATS_COLUMNS)
1992 {
1993 Assert(!a_rParent.isValid());
1994 Assert(!iRow);
1995 Assert((unsigned)iColumn < DBGGUI_STATS_COLUMNS);
1996 return QModelIndex();
1997 }
1998
1999 /* root */
2000 return createIndex(0, iColumn, m_pRoot);
2001 }
2002 if ((unsigned)iRow >= pParent->cChildren)
2003 {
2004 Log(("index: iRow=%d >= cChildren=%u (iColumn=%d)\n", iRow, (unsigned)pParent->cChildren, iColumn));
2005 return QModelIndex(); /* bug? */
2006 }
2007 if ((unsigned)iColumn >= DBGGUI_STATS_COLUMNS)
2008 {
2009 Log(("index: iColumn=%d (iRow=%d)\n", iColumn, iRow));
2010 return QModelIndex(); /* bug? */
2011 }
2012 PDBGGUISTATSNODE pChild = pParent->papChildren[iRow];
2013 return createIndex(iRow, iColumn, pChild);
2014}
2015
2016
2017QModelIndex
2018VBoxDbgStatsModel::parent(const QModelIndex &a_rChild) const
2019{
2020 PDBGGUISTATSNODE pChild = nodeFromIndex(a_rChild);
2021 if (!pChild)
2022 {
2023 Log(("parent: invalid child\n"));
2024 return QModelIndex(); /* bug */
2025 }
2026 PDBGGUISTATSNODE pParent = pChild->pParent;
2027 if (!pParent)
2028 return QModelIndex(); /* ultimate root */
2029
2030 return createIndex(pParent->iSelf, 0, pParent);
2031}
2032
2033
2034QVariant
2035VBoxDbgStatsModel::headerData(int a_iSection, Qt::Orientation a_eOrientation, int a_eRole) const
2036{
2037 if ( a_eOrientation == Qt::Horizontal
2038 && a_eRole == Qt::DisplayRole)
2039 switch (a_iSection)
2040 {
2041 case 0: return tr("Name");
2042 case 1: return tr("Unit");
2043 case 2: return tr("Value/Times");
2044 case 3: return tr("Min");
2045 case 4: return tr("Average");
2046 case 5: return tr("Max");
2047 case 6: return tr("Total");
2048 case 7: return tr("dInt");
2049 case 8: return tr("Description");
2050 default:
2051 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2052 return QVariant(); /* bug */
2053 }
2054 else if ( a_eOrientation == Qt::Horizontal
2055 && a_eRole == Qt::TextAlignmentRole)
2056 switch (a_iSection)
2057 {
2058 case 0:
2059 case 1:
2060 return QVariant();
2061 case 2:
2062 case 3:
2063 case 4:
2064 case 5:
2065 case 6:
2066 case 7:
2067 return (int)(Qt::AlignRight | Qt::AlignVCenter);
2068 case 8:
2069 return QVariant();
2070 default:
2071 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2072 return QVariant(); /* bug */
2073 }
2074
2075 return QVariant();
2076}
2077
2078
2079/*static*/ QString
2080VBoxDbgStatsModel::strUnit(PCDBGGUISTATSNODE pNode)
2081{
2082 if (pNode->enmUnit == STAMUNIT_INVALID)
2083 return "";
2084 return STAMR3GetUnit(pNode->enmUnit);
2085}
2086
2087
2088/*static*/ QString
2089VBoxDbgStatsModel::strValueTimes(PCDBGGUISTATSNODE pNode)
2090{
2091 char sz[128];
2092
2093 switch (pNode->enmType)
2094 {
2095 case STAMTYPE_COUNTER:
2096 return formatNumber(sz, pNode->Data.Counter.c);
2097
2098 case STAMTYPE_PROFILE:
2099 case STAMTYPE_PROFILE_ADV:
2100 if (!pNode->Data.Profile.cPeriods)
2101 return "0";
2102 return formatNumber(sz, pNode->Data.Profile.cPeriods);
2103
2104 case STAMTYPE_RATIO_U32:
2105 case STAMTYPE_RATIO_U32_RESET:
2106 {
2107 formatNumber(sz, pNode->Data.RatioU32.u32A);
2108 char *psz = strchr(sz, '\0');
2109 *psz++ = ':';
2110 formatNumber(psz, pNode->Data.RatioU32.u32B);
2111 return psz;
2112 }
2113
2114 case STAMTYPE_CALLBACK:
2115 return *pNode->Data.pStr;
2116
2117 case STAMTYPE_U8:
2118 case STAMTYPE_U8_RESET:
2119 return formatNumber(sz, pNode->Data.u8);
2120
2121 case STAMTYPE_X8:
2122 case STAMTYPE_X8_RESET:
2123 return formatHexNumber(sz, pNode->Data.u8, 2);
2124
2125 case STAMTYPE_U16:
2126 case STAMTYPE_U16_RESET:
2127 return formatNumber(sz, pNode->Data.u16);
2128
2129 case STAMTYPE_X16:
2130 case STAMTYPE_X16_RESET:
2131 return formatHexNumber(sz, pNode->Data.u16, 4);
2132
2133 case STAMTYPE_U32:
2134 case STAMTYPE_U32_RESET:
2135 return formatNumber(sz, pNode->Data.u32);
2136
2137 case STAMTYPE_X32:
2138 case STAMTYPE_X32_RESET:
2139 return formatHexNumber(sz, pNode->Data.u32, 8);
2140
2141 case STAMTYPE_U64:
2142 case STAMTYPE_U64_RESET:
2143 return formatNumber(sz, pNode->Data.u64);
2144
2145 case STAMTYPE_X64:
2146 case STAMTYPE_X64_RESET:
2147 return formatHexNumber(sz, pNode->Data.u64, 16);
2148
2149 default:
2150 AssertMsgFailed(("%d\n", pNode->enmType));
2151 case STAMTYPE_INVALID:
2152 return "";
2153 }
2154}
2155
2156
2157/*static*/ QString
2158VBoxDbgStatsModel::strMinValue(PCDBGGUISTATSNODE pNode)
2159{
2160 char sz[128];
2161
2162 switch (pNode->enmType)
2163 {
2164 case STAMTYPE_PROFILE:
2165 case STAMTYPE_PROFILE_ADV:
2166 if (!pNode->Data.Profile.cPeriods)
2167 return "0";
2168 return formatNumber(sz, pNode->Data.Profile.cTicksMin);
2169 default:
2170 return "";
2171 }
2172}
2173
2174
2175/*static*/ QString
2176VBoxDbgStatsModel::strAvgValue(PCDBGGUISTATSNODE pNode)
2177{
2178 char sz[128];
2179
2180 switch (pNode->enmType)
2181 {
2182 case STAMTYPE_PROFILE:
2183 case STAMTYPE_PROFILE_ADV:
2184 if (!pNode->Data.Profile.cPeriods)
2185 return "0";
2186 return formatNumber(sz, pNode->Data.Profile.cTicks / pNode->Data.Profile.cPeriods);
2187 default:
2188 return "";
2189 }
2190}
2191
2192
2193/*static*/ QString
2194VBoxDbgStatsModel::strMaxValue(PCDBGGUISTATSNODE pNode)
2195{
2196 char sz[128];
2197
2198 switch (pNode->enmType)
2199 {
2200 case STAMTYPE_PROFILE:
2201 case STAMTYPE_PROFILE_ADV:
2202 if (!pNode->Data.Profile.cPeriods)
2203 return "0";
2204 return formatNumber(sz, pNode->Data.Profile.cTicksMax);
2205 default:
2206 return "";
2207 }
2208}
2209
2210
2211/*static*/ QString
2212VBoxDbgStatsModel::strTotalValue(PCDBGGUISTATSNODE pNode)
2213{
2214 char sz[128];
2215
2216 switch (pNode->enmType)
2217 {
2218 case STAMTYPE_PROFILE:
2219 case STAMTYPE_PROFILE_ADV:
2220 if (!pNode->Data.Profile.cPeriods)
2221 return "0";
2222 return formatNumber(sz, pNode->Data.Profile.cTicks);
2223 default:
2224 return "";
2225 }
2226}
2227
2228
2229/*static*/ QString
2230VBoxDbgStatsModel::strDeltaValue(PCDBGGUISTATSNODE pNode)
2231{
2232 char sz[128];
2233
2234 switch (pNode->enmType)
2235 {
2236 case STAMTYPE_PROFILE:
2237 case STAMTYPE_PROFILE_ADV:
2238 if (!pNode->Data.Profile.cPeriods)
2239 return "0";
2240 /* fall thru */
2241 case STAMTYPE_COUNTER:
2242 case STAMTYPE_RATIO_U32:
2243 case STAMTYPE_RATIO_U32_RESET:
2244 case STAMTYPE_U8:
2245 case STAMTYPE_U8_RESET:
2246 case STAMTYPE_X8:
2247 case STAMTYPE_X8_RESET:
2248 case STAMTYPE_U16:
2249 case STAMTYPE_U16_RESET:
2250 case STAMTYPE_X16:
2251 case STAMTYPE_X16_RESET:
2252 case STAMTYPE_U32:
2253 case STAMTYPE_U32_RESET:
2254 case STAMTYPE_X32:
2255 case STAMTYPE_X32_RESET:
2256 case STAMTYPE_U64_RESET:
2257 case STAMTYPE_X64:
2258 case STAMTYPE_X64_RESET:
2259 return formatNumberSigned(sz, pNode->i64Delta);
2260 default:
2261 return "";
2262 }
2263}
2264
2265
2266QVariant
2267VBoxDbgStatsModel::data(const QModelIndex &a_rIndex, int a_eRole) const
2268{
2269 unsigned iCol = a_rIndex.column();
2270 if (iCol >= DBGGUI_STATS_COLUMNS)
2271 return QVariant();
2272
2273 if (a_eRole == Qt::DisplayRole)
2274 {
2275 PDBGGUISTATSNODE pNode = nodeFromIndex(a_rIndex);
2276 if (!pNode)
2277 return QVariant();
2278
2279 switch (iCol)
2280 {
2281 case 0:
2282 return QString(pNode->pszName);
2283 case 1:
2284 return strUnit(pNode);
2285 case 2:
2286 return strValueTimes(pNode);
2287 case 3:
2288 return strMinValue(pNode);
2289 case 4:
2290 return strAvgValue(pNode);
2291 case 5:
2292 return strMaxValue(pNode);
2293 case 6:
2294 return strTotalValue(pNode);
2295 case 7:
2296 return strDeltaValue(pNode);
2297 case 8:
2298 return pNode->pDescStr ? QString(*pNode->pDescStr) : QString("");
2299 default:
2300 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2301 return QVariant();
2302 }
2303 }
2304 else if (a_eRole == Qt::TextAlignmentRole)
2305 switch (iCol)
2306 {
2307 case 0:
2308 case 1:
2309 return QVariant();
2310 case 2:
2311 case 3:
2312 case 4:
2313 case 5:
2314 case 6:
2315 case 7:
2316 return (int)(Qt::AlignRight | Qt::AlignVCenter);
2317 case 8:
2318 return QVariant();
2319 default:
2320 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2321 return QVariant(); /* bug */
2322 }
2323 return QVariant();
2324}
2325
2326
2327/*static*/ void
2328VBoxDbgStatsModel::stringifyNodeNoRecursion(PDBGGUISTATSNODE a_pNode, QString &a_rString)
2329{
2330 /*
2331 * Get the path, padding it to 32-chars and add it to the string.
2332 */
2333 char szBuf[1024];
2334 ssize_t off = getNodePath(a_pNode, szBuf, sizeof(szBuf) - 2);
2335 AssertReturnVoid(off >= 0);
2336 if (off < 32)
2337 {
2338 memset(&szBuf[off], ' ', 32 - off);
2339 szBuf[32] = '\0';
2340 off = 32;
2341 }
2342 szBuf[off++] = ' ';
2343 szBuf[off] = '\0';
2344 a_rString += szBuf;
2345
2346 /*
2347 * The following is derived from stamR3PrintOne, except
2348 * we print to szBuf, do no visibility checks and can skip
2349 * the path bit.
2350 */
2351 switch (a_pNode->enmType)
2352 {
2353 case STAMTYPE_COUNTER:
2354 RTStrPrintf(szBuf, sizeof(szBuf), "%8llu %s", a_pNode->Data.Counter.c, STAMR3GetUnit(a_pNode->enmUnit));
2355 break;
2356
2357 case STAMTYPE_PROFILE:
2358 case STAMTYPE_PROFILE_ADV:
2359 {
2360 uint64_t u64 = a_pNode->Data.Profile.cPeriods ? a_pNode->Data.Profile.cPeriods : 1;
2361 RTStrPrintf(szBuf, sizeof(szBuf),
2362 "%8llu %s (%12llu ticks, %7llu times, max %9llu, min %7lld)",
2363 a_pNode->Data.Profile.cTicks / u64, STAMR3GetUnit(a_pNode->enmUnit),
2364 a_pNode->Data.Profile.cTicks, a_pNode->Data.Profile.cPeriods, a_pNode->Data.Profile.cTicksMax, a_pNode->Data.Profile.cTicksMin);
2365 break;
2366 }
2367
2368 case STAMTYPE_RATIO_U32:
2369 case STAMTYPE_RATIO_U32_RESET:
2370 RTStrPrintf(szBuf, sizeof(szBuf),
2371 "%8u:%-8u %s",
2372 a_pNode->Data.RatioU32.u32A, a_pNode->Data.RatioU32.u32B, STAMR3GetUnit(a_pNode->enmUnit));
2373 break;
2374
2375 case STAMTYPE_CALLBACK:
2376 if (a_pNode->Data.pStr)
2377 a_rString += *a_pNode->Data.pStr;
2378 RTStrPrintf(szBuf, sizeof(szBuf), " %s", STAMR3GetUnit(a_pNode->enmUnit));
2379 break;
2380
2381 case STAMTYPE_U8:
2382 case STAMTYPE_U8_RESET:
2383 RTStrPrintf(szBuf, sizeof(szBuf), "%8u %s", a_pNode->Data.u8, STAMR3GetUnit(a_pNode->enmUnit));
2384 break;
2385
2386 case STAMTYPE_X8:
2387 case STAMTYPE_X8_RESET:
2388 RTStrPrintf(szBuf, sizeof(szBuf), "%8x %s", a_pNode->Data.u8, STAMR3GetUnit(a_pNode->enmUnit));
2389 break;
2390
2391 case STAMTYPE_U16:
2392 case STAMTYPE_U16_RESET:
2393 RTStrPrintf(szBuf, sizeof(szBuf), "%8u %s", a_pNode->Data.u16, STAMR3GetUnit(a_pNode->enmUnit));
2394 break;
2395
2396 case STAMTYPE_X16:
2397 case STAMTYPE_X16_RESET:
2398 RTStrPrintf(szBuf, sizeof(szBuf), "%8x %s", a_pNode->Data.u16, STAMR3GetUnit(a_pNode->enmUnit));
2399 break;
2400
2401 case STAMTYPE_U32:
2402 case STAMTYPE_U32_RESET:
2403 RTStrPrintf(szBuf, sizeof(szBuf), "%8u %s", a_pNode->Data.u32, STAMR3GetUnit(a_pNode->enmUnit));
2404 break;
2405
2406 case STAMTYPE_X32:
2407 case STAMTYPE_X32_RESET:
2408 RTStrPrintf(szBuf, sizeof(szBuf), "%8x %s", a_pNode->Data.u32, STAMR3GetUnit(a_pNode->enmUnit));
2409 break;
2410
2411 case STAMTYPE_U64:
2412 case STAMTYPE_U64_RESET:
2413 RTStrPrintf(szBuf, sizeof(szBuf), "%8llu %s", a_pNode->Data.u64, STAMR3GetUnit(a_pNode->enmUnit));
2414 break;
2415
2416 case STAMTYPE_X64:
2417 case STAMTYPE_X64_RESET:
2418 RTStrPrintf(szBuf, sizeof(szBuf), "%8llx %s", a_pNode->Data.u64, STAMR3GetUnit(a_pNode->enmUnit));
2419 break;
2420
2421 default:
2422 AssertMsgFailed(("enmType=%d\n", a_pNode->enmType));
2423 return;
2424 }
2425
2426 a_rString += szBuf;
2427}
2428
2429
2430/*static*/ void
2431VBoxDbgStatsModel::stringifyNode(PDBGGUISTATSNODE a_pNode, QString &a_rString)
2432{
2433 /* this node (if it has data) */
2434 if (a_pNode->enmType != STAMTYPE_INVALID)
2435 {
2436 if (!a_rString.isEmpty())
2437 a_rString += "\n";
2438 stringifyNodeNoRecursion(a_pNode, a_rString);
2439 }
2440
2441 /* the children */
2442 uint32_t const cChildren = a_pNode->cChildren;
2443 for (uint32_t i = 0; i < cChildren; i++)
2444 stringifyNode(a_pNode->papChildren[i], a_rString);
2445}
2446
2447
2448void
2449VBoxDbgStatsModel::stringifyTree(QModelIndex &a_rRoot, QString &a_rString) const
2450{
2451 PDBGGUISTATSNODE pRoot = a_rRoot.isValid() ? nodeFromIndex(a_rRoot) : m_pRoot;
2452 if (pRoot)
2453 stringifyNode(pRoot, a_rString);
2454}
2455
2456
2457void
2458VBoxDbgStatsModel::copyTreeToClipboard(QModelIndex &a_rRoot) const
2459{
2460 QString String;
2461 stringifyTree(a_rRoot, String);
2462
2463 QClipboard *pClipboard = QApplication::clipboard();
2464 if (pClipboard)
2465 pClipboard->setText(String, QClipboard::Clipboard);
2466}
2467
2468
2469/*static*/ void
2470VBoxDbgStatsModel::logNode(PDBGGUISTATSNODE a_pNode, bool a_fReleaseLog)
2471{
2472 /* this node (if it has data) */
2473 if (a_pNode->enmType != STAMTYPE_INVALID)
2474 {
2475 QString SelfStr;
2476 stringifyNodeNoRecursion(a_pNode, SelfStr);
2477 QByteArray SelfByteArray = SelfStr.toUtf8();
2478 if (a_fReleaseLog)
2479 RTLogRelPrintf("%s\n", SelfByteArray.constData());
2480 else
2481 RTLogPrintf("%s\n", SelfByteArray.constData());
2482 }
2483
2484 /* the children */
2485 uint32_t const cChildren = a_pNode->cChildren;
2486 for (uint32_t i = 0; i < cChildren; i++)
2487 logNode(a_pNode->papChildren[i], a_fReleaseLog);
2488}
2489
2490
2491void
2492VBoxDbgStatsModel::logTree(QModelIndex &a_rRoot, bool a_fReleaseLog) const
2493{
2494 PDBGGUISTATSNODE pRoot = a_rRoot.isValid() ? nodeFromIndex(a_rRoot) : m_pRoot;
2495 if (pRoot)
2496 logNode(pRoot, a_fReleaseLog);
2497}
2498
2499
2500
2501
2502
2503
2504
2505/*
2506 *
2507 * V B o x D b g S t a t s M o d e l V M
2508 * V B o x D b g S t a t s M o d e l V M
2509 * V B o x D b g S t a t s M o d e l V M
2510 *
2511 *
2512 */
2513
2514
2515VBoxDbgStatsModelVM::VBoxDbgStatsModelVM(VBoxDbgGui *a_pDbgGui, QString &a_rPatStr, QObject *a_pParent)
2516 : VBoxDbgStatsModel(a_pParent), VBoxDbgBase(a_pDbgGui)
2517{
2518 /*
2519 * Create a model containing the STAM entries matching the pattern.
2520 * (The original idea was to get everything and rely on some hide/visible
2521 * flag that it turned out didn't exist.)
2522 */
2523 PDBGGUISTATSNODE pTree = createNewTree(a_rPatStr);
2524 setRootNode(pTree);
2525}
2526
2527
2528VBoxDbgStatsModelVM::~VBoxDbgStatsModelVM()
2529{
2530 /* nothing to do here. */
2531}
2532
2533
2534bool
2535VBoxDbgStatsModelVM::updateStatsByPattern(const QString &a_rPatStr)
2536{
2537 /** @todo the way we update this stuff is independent of the source (XML, file, STAM), our only
2538 * ASSUMPTION is that the input is strictly ordered by (fully slashed) name. So, all this stuff
2539 * should really move up into the parent class. */
2540 bool fRc = updatePrepare();
2541 if (fRc)
2542 {
2543 int rc = stamEnum(a_rPatStr, updateCallback, this);
2544 fRc = updateDone(RT_SUCCESS(rc));
2545 }
2546 return fRc;
2547}
2548
2549
2550void
2551VBoxDbgStatsModelVM::resetStatsByPattern(QString const &a_rPatStr)
2552{
2553 stamReset(a_rPatStr);
2554}
2555
2556
2557/*static*/ DECLCALLBACK(int)
2558VBoxDbgStatsModelVM::createNewTreeCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
2559 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
2560{
2561 PDBGGUISTATSNODE pRoot = (PDBGGUISTATSNODE)pvUser;
2562 Log3(("createNewTreeCallback: %s\n", pszName));
2563
2564 /*
2565 * Skip the ones which shouldn't be visible in the GUI.
2566 */
2567 if (enmVisibility == STAMVISIBILITY_NOT_GUI)
2568 return 0;
2569
2570 /*
2571 * Perform a mkdir -p like operation till we've walked / created the entire path down
2572 * to the node specfied node. Remember the last node as that will be the one we will
2573 * stuff the data into.
2574 */
2575 AssertReturn(*pszName == '/' && pszName[1] != '/', VERR_INTERNAL_ERROR);
2576 PDBGGUISTATSNODE pNode = pRoot;
2577 const char *pszCur = pszName + 1;
2578 while (*pszCur)
2579 {
2580 /* find the end of this component. */
2581 const char *pszNext = strchr(pszCur, '/');
2582 if (!pszNext)
2583 pszNext = strchr(pszCur, '\0');
2584 size_t cchCur = pszNext - pszCur;
2585
2586 /* Create it if it doesn't exist (it will be last if it exists). */
2587 if ( !pNode->cChildren
2588 || strncmp(pNode->papChildren[pNode->cChildren - 1]->pszName, pszCur, cchCur)
2589 || pNode->papChildren[pNode->cChildren - 1]->pszName[cchCur])
2590 {
2591 pNode = createAndInsertNode(pNode, pszCur, pszNext - pszCur, UINT32_MAX);
2592 if (!pNode)
2593 return VERR_NO_MEMORY;
2594 }
2595 else
2596 pNode = pNode->papChildren[pNode->cChildren - 1];
2597
2598 /* Advance */
2599 pszCur = *pszNext ? pszNext + 1 : pszNext;
2600 }
2601
2602 /*
2603 * Save the data.
2604 */
2605 return initNode(pNode, enmType, pvSample, enmUnit, pszDesc);
2606}
2607
2608
2609PDBGGUISTATSNODE
2610VBoxDbgStatsModelVM::createNewTree(QString &a_rPatStr)
2611{
2612 PDBGGUISTATSNODE pRoot = createRootNode();
2613 if (pRoot)
2614 {
2615 int rc = stamEnum(a_rPatStr, createNewTreeCallback, pRoot);
2616 if (RT_SUCCESS(rc))
2617 return pRoot;
2618
2619 /* failed, cleanup. */
2620 destroyTree(pRoot);
2621 }
2622
2623 return NULL;
2624}
2625
2626
2627
2628
2629
2630
2631
2632
2633/*
2634 *
2635 * V B o x D b g S t a t s V i e w
2636 * V B o x D b g S t a t s V i e w
2637 * V B o x D b g S t a t s V i e w
2638 *
2639 *
2640 */
2641
2642
2643VBoxDbgStatsView::VBoxDbgStatsView(VBoxDbgGui *a_pDbgGui, VBoxDbgStatsModel *a_pModel, VBoxDbgStats *a_pParent/* = NULL*/)
2644 : QTreeView(a_pParent), VBoxDbgBase(a_pDbgGui), m_pModel(a_pModel), m_PatStr(), m_pParent(a_pParent),
2645 m_pLeafMenu(NULL), m_pBranchMenu(NULL), m_pViewMenu(NULL), m_pCurMenu(NULL), m_CurIndex()
2646
2647{
2648 /*
2649 * Set the model and view defaults.
2650 */
2651 setRootIsDecorated(true);
2652 setModel(m_pModel);
2653 QModelIndex RootIdx = m_pModel->getRootIndex(); /* This should really be QModelIndex(), but Qt on darwin does wrong things then. */
2654 setRootIndex(RootIdx);
2655 setItemsExpandable(true);
2656 setAlternatingRowColors(true);
2657 setSelectionBehavior(SelectRows);
2658 setSelectionMode(SingleSelection);
2659 /// @todo sorting setSortingEnabled(true);
2660
2661 /*
2662 * Create and setup the actions.
2663 */
2664 m_pExpandAct = new QAction("Expand Tree", this);
2665 m_pCollapseAct = new QAction("Collapse Tree", this);
2666 m_pRefreshAct = new QAction("&Refresh", this);
2667 m_pResetAct = new QAction("Rese&t", this);
2668 m_pCopyAct = new QAction("&Copy", this);
2669 m_pToLogAct = new QAction("To &Log", this);
2670 m_pToRelLogAct = new QAction("T&o Release Log", this);
2671
2672 m_pCopyAct->setShortcut(QKeySequence::Copy);
2673 m_pExpandAct->setShortcut(QKeySequence("Ctrl+E"));
2674 m_pCollapseAct->setShortcut(QKeySequence("Ctrl+D"));
2675 m_pRefreshAct->setShortcut(QKeySequence("Ctrl+R"));
2676 m_pToLogAct->setShortcut(QKeySequence("Ctrl+Z"));
2677 m_pToRelLogAct->setShortcut(QKeySequence("Alt+Z"));
2678
2679 addAction(m_pCopyAct);
2680 addAction(m_pExpandAct);
2681 addAction(m_pCollapseAct);
2682 addAction(m_pRefreshAct);
2683 addAction(m_pToLogAct);
2684 addAction(m_pToRelLogAct);
2685
2686 connect(m_pExpandAct, SIGNAL(triggered(bool)), this, SLOT(actExpand()));
2687 connect(m_pCollapseAct, SIGNAL(triggered(bool)), this, SLOT(actCollapse()));
2688 connect(m_pRefreshAct, SIGNAL(triggered(bool)), this, SLOT(actRefresh()));
2689 connect(m_pResetAct, SIGNAL(triggered(bool)), this, SLOT(actReset()));
2690 connect(m_pCopyAct, SIGNAL(triggered(bool)), this, SLOT(actCopy()));
2691 connect(m_pToLogAct, SIGNAL(triggered(bool)), this, SLOT(actToLog()));
2692 connect(m_pToRelLogAct, SIGNAL(triggered(bool)), this, SLOT(actToRelLog()));
2693
2694
2695 /*
2696 * Create the menus and populate them.
2697 */
2698 setContextMenuPolicy(Qt::DefaultContextMenu);
2699
2700 m_pLeafMenu = new QMenu();
2701 m_pLeafMenu->addAction(m_pCopyAct);
2702 m_pLeafMenu->addAction(m_pRefreshAct);
2703 m_pLeafMenu->addAction(m_pResetAct);
2704 m_pLeafMenu->addAction(m_pToLogAct);
2705 m_pLeafMenu->addAction(m_pToRelLogAct);
2706
2707 m_pBranchMenu = new QMenu(this);
2708 m_pBranchMenu->addAction(m_pCopyAct);
2709 m_pBranchMenu->addAction(m_pRefreshAct);
2710 m_pBranchMenu->addAction(m_pResetAct);
2711 m_pBranchMenu->addAction(m_pToLogAct);
2712 m_pBranchMenu->addAction(m_pToRelLogAct);
2713 m_pBranchMenu->addSeparator();
2714 m_pBranchMenu->addAction(m_pExpandAct);
2715 m_pBranchMenu->addAction(m_pCollapseAct);
2716
2717 m_pViewMenu = new QMenu();
2718 m_pViewMenu->addAction(m_pCopyAct);
2719 m_pViewMenu->addAction(m_pRefreshAct);
2720 m_pViewMenu->addAction(m_pResetAct);
2721 m_pViewMenu->addAction(m_pToLogAct);
2722 m_pViewMenu->addAction(m_pToRelLogAct);
2723 m_pViewMenu->addSeparator();
2724 m_pViewMenu->addAction(m_pExpandAct);
2725 m_pViewMenu->addAction(m_pCollapseAct);
2726 m_pViewMenu->addSeparator();
2727
2728 /* the header menu */
2729 QHeaderView *pHdrView = header();
2730 pHdrView->setContextMenuPolicy(Qt::CustomContextMenu);
2731 connect(pHdrView, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(headerContextMenuRequested(const QPoint &)));
2732}
2733
2734
2735VBoxDbgStatsView::~VBoxDbgStatsView()
2736{
2737 m_pParent = NULL;
2738 m_pCurMenu = NULL;
2739 m_CurIndex = QModelIndex();
2740
2741#define DELETE_IT(m) if (m) { delete m; m = NULL; } else do {} while (0)
2742 DELETE_IT(m_pModel);
2743
2744 DELETE_IT(m_pLeafMenu);
2745 DELETE_IT(m_pBranchMenu);
2746 DELETE_IT(m_pViewMenu);
2747
2748 DELETE_IT(m_pExpandAct);
2749 DELETE_IT(m_pCollapseAct);
2750 DELETE_IT(m_pRefreshAct);
2751 DELETE_IT(m_pResetAct);
2752 DELETE_IT(m_pCopyAct);
2753 DELETE_IT(m_pToLogAct);
2754 DELETE_IT(m_pToRelLogAct);
2755#undef DELETE_IT
2756}
2757
2758
2759void
2760VBoxDbgStatsView::updateStats(const QString &rPatStr)
2761{
2762 m_PatStr = rPatStr;
2763 if (m_pModel->updateStatsByPattern(rPatStr))
2764 setRootIndex(m_pModel->getRootIndex()); /* hack */
2765}
2766
2767
2768void
2769VBoxDbgStatsView::setSubTreeExpanded(QModelIndex const &a_rIndex, bool a_fExpanded)
2770{
2771 int cRows = m_pModel->rowCount(a_rIndex);
2772 for (int i = 0; i < cRows; i++)
2773 setSubTreeExpanded(a_rIndex.child(i, 0), a_fExpanded);
2774 setExpanded(a_rIndex, a_fExpanded);
2775}
2776
2777
2778void
2779VBoxDbgStatsView::contextMenuEvent(QContextMenuEvent *a_pEvt)
2780{
2781 /*
2782 * Get the selected item.
2783 * If it's a mouse event select the item under the cursor (if any).
2784 */
2785 QModelIndex Idx;
2786 if (a_pEvt->reason() == QContextMenuEvent::Mouse)
2787 {
2788 Idx = indexAt(a_pEvt->pos());
2789 if (Idx.isValid())
2790 setCurrentIndex(Idx);
2791 }
2792 else
2793 {
2794 QModelIndexList SelIdx = selectedIndexes();
2795 if (!SelIdx.isEmpty())
2796 Idx = SelIdx.at(0);
2797 }
2798
2799 /*
2800 * Popup the corresponding menu.
2801 */
2802 QMenu *pMenu;
2803 if (!Idx.isValid())
2804 pMenu = m_pViewMenu;
2805 else if (m_pModel->hasChildren(Idx))
2806 pMenu = m_pBranchMenu;
2807 else
2808 pMenu = m_pLeafMenu;
2809 if (pMenu)
2810 {
2811 m_pRefreshAct->setEnabled(!Idx.isValid() || Idx == m_pModel->getRootIndex());
2812 m_CurIndex = Idx;
2813 m_pCurMenu = pMenu;
2814
2815 pMenu->exec(a_pEvt->globalPos());
2816
2817 m_pCurMenu = NULL;
2818 m_CurIndex = QModelIndex();
2819 if (m_pRefreshAct)
2820 m_pRefreshAct->setEnabled(true);
2821 }
2822 a_pEvt->accept();
2823}
2824
2825
2826void
2827VBoxDbgStatsView::headerContextMenuRequested(const QPoint &a_rPos)
2828{
2829 /*
2830 * Show the view menu.
2831 */
2832 if (m_pViewMenu)
2833 {
2834 m_pRefreshAct->setEnabled(true);
2835 m_CurIndex = m_pModel->getRootIndex();
2836 m_pCurMenu = m_pViewMenu;
2837
2838 m_pViewMenu->exec(header()->mapToGlobal(a_rPos));
2839
2840 m_pCurMenu = NULL;
2841 m_CurIndex = QModelIndex();
2842 if (m_pRefreshAct)
2843 m_pRefreshAct->setEnabled(true);
2844 }
2845}
2846
2847
2848void
2849VBoxDbgStatsView::actExpand()
2850{
2851 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2852 if (Idx.isValid())
2853 setSubTreeExpanded(Idx, true /* a_fExpanded */);
2854}
2855
2856
2857void
2858VBoxDbgStatsView::actCollapse()
2859{
2860 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2861 if (Idx.isValid())
2862 setSubTreeExpanded(Idx, false /* a_fExpanded */);
2863}
2864
2865
2866void
2867VBoxDbgStatsView::actRefresh()
2868{
2869 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2870 if (!Idx.isValid() || Idx == m_pModel->getRootIndex())
2871 {
2872 if (m_pModel->updateStatsByPattern(m_PatStr))
2873 setRootIndex(m_pModel->getRootIndex()); /* hack */
2874 }
2875 else
2876 m_pModel->updateStatsByIndex(Idx);
2877}
2878
2879
2880void
2881VBoxDbgStatsView::actReset()
2882{
2883 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2884 if (!Idx.isValid() || Idx == m_pModel->getRootIndex())
2885 m_pModel->resetStatsByPattern(m_PatStr);
2886 else
2887 m_pModel->resetStatsByIndex(Idx);
2888}
2889
2890
2891void
2892VBoxDbgStatsView::actCopy()
2893{
2894 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2895 m_pModel->copyTreeToClipboard(Idx);
2896}
2897
2898
2899void
2900VBoxDbgStatsView::actToLog()
2901{
2902 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2903 m_pModel->logTree(Idx, false /* a_fReleaseLog */);
2904}
2905
2906
2907void
2908VBoxDbgStatsView::actToRelLog()
2909{
2910 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2911 m_pModel->logTree(Idx, true /* a_fReleaseLog */);
2912}
2913
2914
2915
2916
2917
2918
2919/*
2920 *
2921 * V B o x D b g S t a t s
2922 * V B o x D b g S t a t s
2923 * V B o x D b g S t a t s
2924 *
2925 *
2926 */
2927
2928
2929VBoxDbgStats::VBoxDbgStats(VBoxDbgGui *a_pDbgGui, const char *pszPat/* = NULL*/, unsigned uRefreshRate/* = 0*/, QWidget *pParent/* = NULL*/)
2930 : VBoxDbgBaseWindow(a_pDbgGui, pParent), m_PatStr(pszPat), m_pPatCB(NULL), m_uRefreshRate(0), m_pTimer(NULL), m_pView(NULL)
2931{
2932 setWindowTitle("VBoxDbg - Statistics");
2933
2934 /*
2935 * On top, a horizontal box with the pattern field, buttons and refresh interval.
2936 */
2937 QHBoxLayout *pHLayout = new QHBoxLayout;
2938
2939 QLabel *pLabel = new QLabel(" Pattern ");
2940 pHLayout->addWidget(pLabel);
2941 pLabel->setMaximumSize(pLabel->sizeHint());
2942 pLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
2943
2944 m_pPatCB = new QComboBox();
2945 m_pPatCB->setAutoCompletion(false);
2946 pHLayout->addWidget(m_pPatCB);
2947 if (!m_PatStr.isEmpty())
2948 m_pPatCB->addItem(m_PatStr);
2949 m_pPatCB->setDuplicatesEnabled(false);
2950 m_pPatCB->setEditable(true);
2951 connect(m_pPatCB, SIGNAL(activated(const QString &)), this, SLOT(apply(const QString &)));
2952
2953 QPushButton *pPB = new QPushButton("&All");
2954 pHLayout->addWidget(pPB);
2955 pPB->setMaximumSize(pPB->sizeHint());
2956 connect(pPB, SIGNAL(clicked()), this, SLOT(applyAll()));
2957
2958 pLabel = new QLabel(" Interval ");
2959 pHLayout->addWidget(pLabel);
2960 pLabel->setMaximumSize(pLabel->sizeHint());
2961 pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2962
2963 QSpinBox *pSB = new QSpinBox();
2964 pHLayout->addWidget(pSB);
2965 pSB->setMinimum(0);
2966 pSB->setMaximum(60);
2967 pSB->setSingleStep(1);
2968 pSB->setValue(uRefreshRate);
2969 pSB->setSuffix(" s");
2970 pSB->setWrapping(false);
2971 pSB->setButtonSymbols(QSpinBox::PlusMinus);
2972 pSB->setMaximumSize(pSB->sizeHint());
2973 connect(pSB, SIGNAL(valueChanged(int)), this, SLOT(setRefresh(int)));
2974
2975 /*
2976 * Create the tree view and setup the layout.
2977 */
2978 VBoxDbgStatsModelVM *pModel = new VBoxDbgStatsModelVM(a_pDbgGui, m_PatStr, NULL);
2979 m_pView = new VBoxDbgStatsView(a_pDbgGui, pModel, this);
2980
2981 QWidget *pHBox = new QWidget;
2982 pHBox->setLayout(pHLayout);
2983
2984 QVBoxLayout *pVLayout = new QVBoxLayout;
2985 pVLayout->addWidget(pHBox);
2986 pVLayout->addWidget(m_pView);
2987 setLayout(pVLayout);
2988
2989 /*
2990 * Resize the columns.
2991 * Seems this has to be done with all nodes expanded.
2992 */
2993 m_pView->expandAll();
2994 for (int i = 0; i <= 8; i++)
2995 m_pView->resizeColumnToContents(i);
2996 m_pView->collapseAll();
2997
2998 /*
2999 * Create a refresh timer and start it.
3000 */
3001 m_pTimer = new QTimer(this);
3002 connect(m_pTimer, SIGNAL(timeout()), this, SLOT(refresh()));
3003 setRefresh(uRefreshRate);
3004
3005 /*
3006 * And some shortcuts.
3007 */
3008 m_pFocusToPat = new QAction("", this);
3009 m_pFocusToPat->setShortcut(QKeySequence("Ctrl+L"));
3010 addAction(m_pFocusToPat);
3011 connect(m_pFocusToPat, SIGNAL(triggered(bool)), this, SLOT(actFocusToPat()));
3012}
3013
3014
3015VBoxDbgStats::~VBoxDbgStats()
3016{
3017 if (m_pTimer)
3018 {
3019 delete m_pTimer;
3020 m_pTimer = NULL;
3021 }
3022
3023 if (m_pPatCB)
3024 {
3025 delete m_pPatCB;
3026 m_pPatCB = NULL;
3027 }
3028
3029 if (m_pView)
3030 {
3031 delete m_pView;
3032 m_pView = NULL;
3033 }
3034}
3035
3036
3037void
3038VBoxDbgStats::closeEvent(QCloseEvent *a_pCloseEvt)
3039{
3040 a_pCloseEvt->accept();
3041 delete this;
3042}
3043
3044
3045void
3046VBoxDbgStats::apply(const QString &Str)
3047{
3048 m_PatStr = Str;
3049 refresh();
3050}
3051
3052
3053void
3054VBoxDbgStats::applyAll()
3055{
3056 apply("");
3057}
3058
3059
3060
3061void
3062VBoxDbgStats::refresh()
3063{
3064 m_pView->updateStats(m_PatStr);
3065}
3066
3067
3068void
3069VBoxDbgStats::setRefresh(int iRefresh)
3070{
3071 if ((unsigned)iRefresh != m_uRefreshRate)
3072 {
3073 if (!m_uRefreshRate || iRefresh)
3074 m_pTimer->start(iRefresh * 1000);
3075 else
3076 m_pTimer->stop();
3077 m_uRefreshRate = iRefresh;
3078 }
3079}
3080
3081
3082void
3083VBoxDbgStats::actFocusToPat()
3084{
3085 if (!m_pPatCB->hasFocus())
3086 m_pPatCB->setFocus(Qt::ShortcutFocusReason);
3087}
3088
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette