VirtualBox

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

Last change on this file since 60550 was 59415, checked in by vboxsync, 9 years ago

FE/Qt: Qt5 migration (part 70): Debugger: Adjusting code to Qt5 API changes for QAbstractItemModel.

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

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