VirtualBox

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

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

*: spelling fixes, thanks Timeless!

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

© 2023 Oracle
ContactPrivacy policyTerms of Use