VirtualBox

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

Last change on this file since 43667 was 39917, checked in by vboxsync, 12 years ago

STAM,GMM,VBoxDbg: Adding GMM statistics (at last). Introduces STAMTYPE_BOOL and STAMTYPE_BOOL_RESET. Fixes a beyond end of string access in VBoxDbg (memcmp).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use