VirtualBox

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

Last change on this file since 55863 was 54745, checked in by vboxsync, 10 years ago

VBoxDbgStats: Fixed RATION_U32* formatting.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.6 KB
Line 
1/* $Id: VBoxDbgStatsQt4.cpp 54745 2015-03-13 15:52:33Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Statistics.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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(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 reset();
1860 }
1861 else
1862 {
1863 /*
1864 * Send dataChanged events.
1865 *
1866 * We do this here instead of from the updateCallback because it reduces
1867 * the clutter in that method and allow us to emit bulk signals in an
1868 * easier way because we can traverse the tree in a different fashion.
1869 */
1870 DBGGUISTATSSTACK Stack;
1871 Stack.a[0].pNode = m_pRoot;
1872 Stack.a[0].iChild = -1;
1873 Stack.iTop = 0;
1874
1875 while (Stack.iTop >= 0)
1876 {
1877 /* get top element */
1878 PDBGGUISTATSNODE pNode = Stack.a[Stack.iTop].pNode;
1879 uint32_t iChild = ++Stack.a[Stack.iTop].iChild;
1880 if (iChild < pNode->cChildren)
1881 {
1882 /* push */
1883 Stack.iTop++;
1884 Assert(Stack.iTop < (int32_t)RT_ELEMENTS(Stack.a));
1885 Stack.a[Stack.iTop].pNode = pNode->papChildren[iChild];
1886 Stack.a[Stack.iTop].iChild = 0;
1887 }
1888 else
1889 {
1890 /* pop */
1891 Stack.iTop--;
1892
1893 /* do the actual work. */
1894 iChild = 0;
1895 while (iChild < pNode->cChildren)
1896 {
1897 /* skip to the first needing updating. */
1898 while ( iChild < pNode->cChildren
1899 && pNode->papChildren[iChild]->enmState != kDbgGuiStatsNodeState_kRefresh)
1900 iChild++;
1901 if (iChild >= pNode->cChildren)
1902 break;
1903 QModelIndex TopLeft = createIndex(iChild, 0, pNode->papChildren[iChild]);
1904 pNode->papChildren[iChild]->enmState = kDbgGuiStatsNodeState_kVisible;
1905
1906 /* any subsequent nodes that also needs refreshing? */
1907 if ( ++iChild < pNode->cChildren
1908 && pNode->papChildren[iChild]->enmState == kDbgGuiStatsNodeState_kRefresh)
1909 {
1910 do pNode->papChildren[iChild]->enmState = kDbgGuiStatsNodeState_kVisible;
1911 while ( ++iChild < pNode->cChildren
1912 && pNode->papChildren[iChild]->enmState == kDbgGuiStatsNodeState_kRefresh);
1913 QModelIndex BottomRight = createIndex(iChild - 1, DBGGUI_STATS_COLUMNS - 1, pNode->papChildren[iChild - 1]);
1914
1915 /* emit the refresh signal */
1916 emit dataChanged(TopLeft, BottomRight);
1917 }
1918 else
1919 {
1920 /* emit the refresh signal */
1921 emit dataChanged(TopLeft, TopLeft);
1922 }
1923 }
1924 }
1925 }
1926 /* emit layoutChanged(); - hrmpf, doesn't work reliably... */
1927 }
1928
1929 return m_fUpdateInsertRemove;
1930}
1931
1932
1933bool
1934VBoxDbgStatsModel::updateStatsByPattern(const QString &a_rPatStr)
1935{
1936 /* stub */
1937 NOREF(a_rPatStr);
1938 return false;
1939}
1940
1941
1942void
1943VBoxDbgStatsModel::updateStatsByIndex(QModelIndex const &a_rIndex)
1944{
1945 /** @todo implement this based on updateStatsByPattern. */
1946 NOREF(a_rIndex);
1947}
1948
1949
1950void
1951VBoxDbgStatsModel::resetStatsByPattern(QString const &a_rPatStr)
1952{
1953 /* stub */
1954 NOREF(a_rPatStr);
1955}
1956
1957
1958void
1959VBoxDbgStatsModel::resetStatsByIndex(QModelIndex const &a_rIndex, bool fSubTree /*= true*/)
1960{
1961 PCDBGGUISTATSNODE pNode = nodeFromIndex(a_rIndex);
1962 if (pNode == m_pRoot || !a_rIndex.isValid())
1963 {
1964 if (fSubTree)
1965 {
1966 /* everything from the root down. */
1967 resetStatsByPattern(QString());
1968 }
1969 }
1970 else if (pNode)
1971 {
1972 /* the node pattern. */
1973 char szPat[1024+1024+4];
1974 ssize_t cch = getNodePath(pNode, szPat, 1024);
1975 AssertReturnVoid(cch >= 0);
1976
1977 /* the sub-tree pattern. */
1978 if (fSubTree && pNode->cChildren)
1979 {
1980 char *psz = &szPat[cch];
1981 *psz++ = '|';
1982 memcpy(psz, szPat, cch);
1983 psz += cch;
1984 *psz++ = '/';
1985 *psz++ = '*';
1986 *psz++ = '\0';
1987 }
1988
1989 resetStatsByPattern(szPat);
1990 }
1991}
1992
1993
1994QModelIndex
1995VBoxDbgStatsModel::getRootIndex(void) const
1996{
1997 if (!m_pRoot)
1998 return QModelIndex();
1999 return createIndex(0, 0, m_pRoot);
2000}
2001
2002
2003void
2004VBoxDbgStatsModel::setRootNode(PDBGGUISTATSNODE a_pRoot)
2005{
2006 PDBGGUISTATSNODE pOldTree = m_pRoot;
2007 m_pRoot = a_pRoot;
2008 destroyTree(pOldTree);
2009 reset();
2010}
2011
2012
2013Qt::ItemFlags
2014VBoxDbgStatsModel::flags(const QModelIndex &a_rIndex) const
2015{
2016 Qt::ItemFlags fFlags = QAbstractItemModel::flags(a_rIndex);
2017 return fFlags;
2018}
2019
2020
2021int
2022VBoxDbgStatsModel::columnCount(const QModelIndex &a_rParent) const
2023{
2024 NOREF(a_rParent);
2025 return DBGGUI_STATS_COLUMNS;
2026}
2027
2028
2029int
2030VBoxDbgStatsModel::rowCount(const QModelIndex &a_rParent) const
2031{
2032 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
2033 return pParent ? pParent->cChildren : 1 /* root */;
2034}
2035
2036
2037bool
2038VBoxDbgStatsModel::hasChildren(const QModelIndex &a_rParent) const
2039{
2040 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
2041 return pParent ? pParent->cChildren > 0 : true /* root */;
2042}
2043
2044
2045QModelIndex
2046VBoxDbgStatsModel::index(int iRow, int iColumn, const QModelIndex &a_rParent) const
2047{
2048 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
2049 if (!pParent)
2050 {
2051 if ( a_rParent.isValid()
2052 || iRow
2053 || (unsigned)iColumn < DBGGUI_STATS_COLUMNS)
2054 {
2055 Assert(!a_rParent.isValid());
2056 Assert(!iRow);
2057 Assert((unsigned)iColumn < DBGGUI_STATS_COLUMNS);
2058 return QModelIndex();
2059 }
2060
2061 /* root */
2062 return createIndex(0, iColumn, m_pRoot);
2063 }
2064 if ((unsigned)iRow >= pParent->cChildren)
2065 {
2066 Log(("index: iRow=%d >= cChildren=%u (iColumn=%d)\n", iRow, (unsigned)pParent->cChildren, iColumn));
2067 return QModelIndex(); /* bug? */
2068 }
2069 if ((unsigned)iColumn >= DBGGUI_STATS_COLUMNS)
2070 {
2071 Log(("index: iColumn=%d (iRow=%d)\n", iColumn, iRow));
2072 return QModelIndex(); /* bug? */
2073 }
2074 PDBGGUISTATSNODE pChild = pParent->papChildren[iRow];
2075 return createIndex(iRow, iColumn, pChild);
2076}
2077
2078
2079QModelIndex
2080VBoxDbgStatsModel::parent(const QModelIndex &a_rChild) const
2081{
2082 PDBGGUISTATSNODE pChild = nodeFromIndex(a_rChild);
2083 if (!pChild)
2084 {
2085 Log(("parent: invalid child\n"));
2086 return QModelIndex(); /* bug */
2087 }
2088 PDBGGUISTATSNODE pParent = pChild->pParent;
2089 if (!pParent)
2090 return QModelIndex(); /* ultimate root */
2091
2092 return createIndex(pParent->iSelf, 0, pParent);
2093}
2094
2095
2096QVariant
2097VBoxDbgStatsModel::headerData(int a_iSection, Qt::Orientation a_eOrientation, int a_eRole) const
2098{
2099 if ( a_eOrientation == Qt::Horizontal
2100 && a_eRole == Qt::DisplayRole)
2101 switch (a_iSection)
2102 {
2103 case 0: return tr("Name");
2104 case 1: return tr("Unit");
2105 case 2: return tr("Value/Times");
2106 case 3: return tr("Min");
2107 case 4: return tr("Average");
2108 case 5: return tr("Max");
2109 case 6: return tr("Total");
2110 case 7: return tr("dInt");
2111 case 8: return tr("Description");
2112 default:
2113 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2114 return QVariant(); /* bug */
2115 }
2116 else if ( a_eOrientation == Qt::Horizontal
2117 && a_eRole == Qt::TextAlignmentRole)
2118 switch (a_iSection)
2119 {
2120 case 0:
2121 case 1:
2122 return QVariant();
2123 case 2:
2124 case 3:
2125 case 4:
2126 case 5:
2127 case 6:
2128 case 7:
2129 return (int)(Qt::AlignRight | Qt::AlignVCenter);
2130 case 8:
2131 return QVariant();
2132 default:
2133 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2134 return QVariant(); /* bug */
2135 }
2136
2137 return QVariant();
2138}
2139
2140
2141/*static*/ QString
2142VBoxDbgStatsModel::strUnit(PCDBGGUISTATSNODE pNode)
2143{
2144 if (pNode->enmUnit == STAMUNIT_INVALID)
2145 return "";
2146 return STAMR3GetUnit(pNode->enmUnit);
2147}
2148
2149
2150/*static*/ QString
2151VBoxDbgStatsModel::strValueTimes(PCDBGGUISTATSNODE pNode)
2152{
2153 char sz[128];
2154
2155 switch (pNode->enmType)
2156 {
2157 case STAMTYPE_COUNTER:
2158 return formatNumber(sz, pNode->Data.Counter.c);
2159
2160 case STAMTYPE_PROFILE:
2161 case STAMTYPE_PROFILE_ADV:
2162 if (!pNode->Data.Profile.cPeriods)
2163 return "0";
2164 return formatNumber(sz, pNode->Data.Profile.cPeriods);
2165
2166 case STAMTYPE_RATIO_U32:
2167 case STAMTYPE_RATIO_U32_RESET:
2168 {
2169 char szTmp[64];
2170 char *psz = formatNumber(szTmp, pNode->Data.RatioU32.u32A);
2171 size_t off = strlen(psz);
2172 memcpy(sz, psz, off);
2173 sz[off++] = ':';
2174 strcpy(&sz[off], formatNumber(szTmp, pNode->Data.RatioU32.u32B));
2175 return sz;
2176 }
2177
2178 case STAMTYPE_CALLBACK:
2179 return *pNode->Data.pStr;
2180
2181 case STAMTYPE_U8:
2182 case STAMTYPE_U8_RESET:
2183 return formatNumber(sz, pNode->Data.u8);
2184
2185 case STAMTYPE_X8:
2186 case STAMTYPE_X8_RESET:
2187 return formatHexNumber(sz, pNode->Data.u8, 2);
2188
2189 case STAMTYPE_U16:
2190 case STAMTYPE_U16_RESET:
2191 return formatNumber(sz, pNode->Data.u16);
2192
2193 case STAMTYPE_X16:
2194 case STAMTYPE_X16_RESET:
2195 return formatHexNumber(sz, pNode->Data.u16, 4);
2196
2197 case STAMTYPE_U32:
2198 case STAMTYPE_U32_RESET:
2199 return formatNumber(sz, pNode->Data.u32);
2200
2201 case STAMTYPE_X32:
2202 case STAMTYPE_X32_RESET:
2203 return formatHexNumber(sz, pNode->Data.u32, 8);
2204
2205 case STAMTYPE_U64:
2206 case STAMTYPE_U64_RESET:
2207 return formatNumber(sz, pNode->Data.u64);
2208
2209 case STAMTYPE_X64:
2210 case STAMTYPE_X64_RESET:
2211 return formatHexNumber(sz, pNode->Data.u64, 16);
2212
2213 case STAMTYPE_BOOL:
2214 case STAMTYPE_BOOL_RESET:
2215 return pNode->Data.f ? "true" : "false";
2216
2217 default:
2218 AssertMsgFailed(("%d\n", pNode->enmType));
2219 case STAMTYPE_INVALID:
2220 return "";
2221 }
2222}
2223
2224
2225/*static*/ QString
2226VBoxDbgStatsModel::strMinValue(PCDBGGUISTATSNODE pNode)
2227{
2228 char sz[128];
2229
2230 switch (pNode->enmType)
2231 {
2232 case STAMTYPE_PROFILE:
2233 case STAMTYPE_PROFILE_ADV:
2234 if (!pNode->Data.Profile.cPeriods)
2235 return "0";
2236 return formatNumber(sz, pNode->Data.Profile.cTicksMin);
2237 default:
2238 return "";
2239 }
2240}
2241
2242
2243/*static*/ QString
2244VBoxDbgStatsModel::strAvgValue(PCDBGGUISTATSNODE pNode)
2245{
2246 char sz[128];
2247
2248 switch (pNode->enmType)
2249 {
2250 case STAMTYPE_PROFILE:
2251 case STAMTYPE_PROFILE_ADV:
2252 if (!pNode->Data.Profile.cPeriods)
2253 return "0";
2254 return formatNumber(sz, pNode->Data.Profile.cTicks / pNode->Data.Profile.cPeriods);
2255 default:
2256 return "";
2257 }
2258}
2259
2260
2261/*static*/ QString
2262VBoxDbgStatsModel::strMaxValue(PCDBGGUISTATSNODE pNode)
2263{
2264 char sz[128];
2265
2266 switch (pNode->enmType)
2267 {
2268 case STAMTYPE_PROFILE:
2269 case STAMTYPE_PROFILE_ADV:
2270 if (!pNode->Data.Profile.cPeriods)
2271 return "0";
2272 return formatNumber(sz, pNode->Data.Profile.cTicksMax);
2273 default:
2274 return "";
2275 }
2276}
2277
2278
2279/*static*/ QString
2280VBoxDbgStatsModel::strTotalValue(PCDBGGUISTATSNODE pNode)
2281{
2282 char sz[128];
2283
2284 switch (pNode->enmType)
2285 {
2286 case STAMTYPE_PROFILE:
2287 case STAMTYPE_PROFILE_ADV:
2288 if (!pNode->Data.Profile.cPeriods)
2289 return "0";
2290 return formatNumber(sz, pNode->Data.Profile.cTicks);
2291 default:
2292 return "";
2293 }
2294}
2295
2296
2297/*static*/ QString
2298VBoxDbgStatsModel::strDeltaValue(PCDBGGUISTATSNODE pNode)
2299{
2300 char sz[128];
2301
2302 switch (pNode->enmType)
2303 {
2304 case STAMTYPE_PROFILE:
2305 case STAMTYPE_PROFILE_ADV:
2306 if (!pNode->Data.Profile.cPeriods)
2307 return "0";
2308 /* fall thru */
2309 case STAMTYPE_COUNTER:
2310 case STAMTYPE_RATIO_U32:
2311 case STAMTYPE_RATIO_U32_RESET:
2312 case STAMTYPE_U8:
2313 case STAMTYPE_U8_RESET:
2314 case STAMTYPE_X8:
2315 case STAMTYPE_X8_RESET:
2316 case STAMTYPE_U16:
2317 case STAMTYPE_U16_RESET:
2318 case STAMTYPE_X16:
2319 case STAMTYPE_X16_RESET:
2320 case STAMTYPE_U32:
2321 case STAMTYPE_U32_RESET:
2322 case STAMTYPE_X32:
2323 case STAMTYPE_X32_RESET:
2324 case STAMTYPE_U64:
2325 case STAMTYPE_U64_RESET:
2326 case STAMTYPE_X64:
2327 case STAMTYPE_X64_RESET:
2328 case STAMTYPE_BOOL:
2329 case STAMTYPE_BOOL_RESET:
2330 return formatNumberSigned(sz, pNode->i64Delta);
2331 default:
2332 return "";
2333 }
2334}
2335
2336
2337QVariant
2338VBoxDbgStatsModel::data(const QModelIndex &a_rIndex, int a_eRole) const
2339{
2340 unsigned iCol = a_rIndex.column();
2341 if (iCol >= DBGGUI_STATS_COLUMNS)
2342 return QVariant();
2343
2344 if (a_eRole == Qt::DisplayRole)
2345 {
2346 PDBGGUISTATSNODE pNode = nodeFromIndex(a_rIndex);
2347 if (!pNode)
2348 return QVariant();
2349
2350 switch (iCol)
2351 {
2352 case 0:
2353 return QString(pNode->pszName);
2354 case 1:
2355 return strUnit(pNode);
2356 case 2:
2357 return strValueTimes(pNode);
2358 case 3:
2359 return strMinValue(pNode);
2360 case 4:
2361 return strAvgValue(pNode);
2362 case 5:
2363 return strMaxValue(pNode);
2364 case 6:
2365 return strTotalValue(pNode);
2366 case 7:
2367 return strDeltaValue(pNode);
2368 case 8:
2369 return pNode->pDescStr ? QString(*pNode->pDescStr) : QString("");
2370 default:
2371 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2372 return QVariant();
2373 }
2374 }
2375 else if (a_eRole == Qt::TextAlignmentRole)
2376 switch (iCol)
2377 {
2378 case 0:
2379 case 1:
2380 return QVariant();
2381 case 2:
2382 case 3:
2383 case 4:
2384 case 5:
2385 case 6:
2386 case 7:
2387 return (int)(Qt::AlignRight | Qt::AlignVCenter);
2388 case 8:
2389 return QVariant();
2390 default:
2391 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
2392 return QVariant(); /* bug */
2393 }
2394 return QVariant();
2395}
2396
2397
2398/*static*/ void
2399VBoxDbgStatsModel::stringifyNodeNoRecursion(PDBGGUISTATSNODE a_pNode, QString &a_rString)
2400{
2401 /*
2402 * Get the path, padding it to 32-chars and add it to the string.
2403 */
2404 char szBuf[1024];
2405 ssize_t off = getNodePath(a_pNode, szBuf, sizeof(szBuf) - 2);
2406 AssertReturnVoid(off >= 0);
2407 if (off < 32)
2408 {
2409 memset(&szBuf[off], ' ', 32 - off);
2410 szBuf[32] = '\0';
2411 off = 32;
2412 }
2413 szBuf[off++] = ' ';
2414 szBuf[off] = '\0';
2415 a_rString += szBuf;
2416
2417 /*
2418 * The following is derived from stamR3PrintOne, except
2419 * we print to szBuf, do no visibility checks and can skip
2420 * the path bit.
2421 */
2422 switch (a_pNode->enmType)
2423 {
2424 case STAMTYPE_COUNTER:
2425 RTStrPrintf(szBuf, sizeof(szBuf), "%8llu %s", a_pNode->Data.Counter.c, STAMR3GetUnit(a_pNode->enmUnit));
2426 break;
2427
2428 case STAMTYPE_PROFILE:
2429 case STAMTYPE_PROFILE_ADV:
2430 {
2431 uint64_t u64 = a_pNode->Data.Profile.cPeriods ? a_pNode->Data.Profile.cPeriods : 1;
2432 RTStrPrintf(szBuf, sizeof(szBuf),
2433 "%8llu %s (%12llu ticks, %7llu times, max %9llu, min %7lld)",
2434 a_pNode->Data.Profile.cTicks / u64, STAMR3GetUnit(a_pNode->enmUnit),
2435 a_pNode->Data.Profile.cTicks, a_pNode->Data.Profile.cPeriods, a_pNode->Data.Profile.cTicksMax, a_pNode->Data.Profile.cTicksMin);
2436 break;
2437 }
2438
2439 case STAMTYPE_RATIO_U32:
2440 case STAMTYPE_RATIO_U32_RESET:
2441 RTStrPrintf(szBuf, sizeof(szBuf),
2442 "%8u:%-8u %s",
2443 a_pNode->Data.RatioU32.u32A, a_pNode->Data.RatioU32.u32B, STAMR3GetUnit(a_pNode->enmUnit));
2444 break;
2445
2446 case STAMTYPE_CALLBACK:
2447 if (a_pNode->Data.pStr)
2448 a_rString += *a_pNode->Data.pStr;
2449 RTStrPrintf(szBuf, sizeof(szBuf), " %s", STAMR3GetUnit(a_pNode->enmUnit));
2450 break;
2451
2452 case STAMTYPE_U8:
2453 case STAMTYPE_U8_RESET:
2454 RTStrPrintf(szBuf, sizeof(szBuf), "%8u %s", a_pNode->Data.u8, STAMR3GetUnit(a_pNode->enmUnit));
2455 break;
2456
2457 case STAMTYPE_X8:
2458 case STAMTYPE_X8_RESET:
2459 RTStrPrintf(szBuf, sizeof(szBuf), "%8x %s", a_pNode->Data.u8, STAMR3GetUnit(a_pNode->enmUnit));
2460 break;
2461
2462 case STAMTYPE_U16:
2463 case STAMTYPE_U16_RESET:
2464 RTStrPrintf(szBuf, sizeof(szBuf), "%8u %s", a_pNode->Data.u16, STAMR3GetUnit(a_pNode->enmUnit));
2465 break;
2466
2467 case STAMTYPE_X16:
2468 case STAMTYPE_X16_RESET:
2469 RTStrPrintf(szBuf, sizeof(szBuf), "%8x %s", a_pNode->Data.u16, STAMR3GetUnit(a_pNode->enmUnit));
2470 break;
2471
2472 case STAMTYPE_U32:
2473 case STAMTYPE_U32_RESET:
2474 RTStrPrintf(szBuf, sizeof(szBuf), "%8u %s", a_pNode->Data.u32, STAMR3GetUnit(a_pNode->enmUnit));
2475 break;
2476
2477 case STAMTYPE_X32:
2478 case STAMTYPE_X32_RESET:
2479 RTStrPrintf(szBuf, sizeof(szBuf), "%8x %s", a_pNode->Data.u32, STAMR3GetUnit(a_pNode->enmUnit));
2480 break;
2481
2482 case STAMTYPE_U64:
2483 case STAMTYPE_U64_RESET:
2484 RTStrPrintf(szBuf, sizeof(szBuf), "%8llu %s", a_pNode->Data.u64, STAMR3GetUnit(a_pNode->enmUnit));
2485 break;
2486
2487 case STAMTYPE_X64:
2488 case STAMTYPE_X64_RESET:
2489 RTStrPrintf(szBuf, sizeof(szBuf), "%8llx %s", a_pNode->Data.u64, STAMR3GetUnit(a_pNode->enmUnit));
2490 break;
2491
2492 case STAMTYPE_BOOL:
2493 case STAMTYPE_BOOL_RESET:
2494 RTStrPrintf(szBuf, sizeof(szBuf), "%s %s", a_pNode->Data.f ? "true " : "false ", STAMR3GetUnit(a_pNode->enmUnit));
2495 break;
2496
2497 default:
2498 AssertMsgFailed(("enmType=%d\n", a_pNode->enmType));
2499 return;
2500 }
2501
2502 a_rString += szBuf;
2503}
2504
2505
2506/*static*/ void
2507VBoxDbgStatsModel::stringifyNode(PDBGGUISTATSNODE a_pNode, QString &a_rString)
2508{
2509 /* this node (if it has data) */
2510 if (a_pNode->enmType != STAMTYPE_INVALID)
2511 {
2512 if (!a_rString.isEmpty())
2513 a_rString += "\n";
2514 stringifyNodeNoRecursion(a_pNode, a_rString);
2515 }
2516
2517 /* the children */
2518 uint32_t const cChildren = a_pNode->cChildren;
2519 for (uint32_t i = 0; i < cChildren; i++)
2520 stringifyNode(a_pNode->papChildren[i], a_rString);
2521}
2522
2523
2524void
2525VBoxDbgStatsModel::stringifyTree(QModelIndex &a_rRoot, QString &a_rString) const
2526{
2527 PDBGGUISTATSNODE pRoot = a_rRoot.isValid() ? nodeFromIndex(a_rRoot) : m_pRoot;
2528 if (pRoot)
2529 stringifyNode(pRoot, a_rString);
2530}
2531
2532
2533void
2534VBoxDbgStatsModel::copyTreeToClipboard(QModelIndex &a_rRoot) const
2535{
2536 QString String;
2537 stringifyTree(a_rRoot, String);
2538
2539 QClipboard *pClipboard = QApplication::clipboard();
2540 if (pClipboard)
2541 pClipboard->setText(String, QClipboard::Clipboard);
2542}
2543
2544
2545/*static*/ void
2546VBoxDbgStatsModel::logNode(PDBGGUISTATSNODE a_pNode, bool a_fReleaseLog)
2547{
2548 /* this node (if it has data) */
2549 if (a_pNode->enmType != STAMTYPE_INVALID)
2550 {
2551 QString SelfStr;
2552 stringifyNodeNoRecursion(a_pNode, SelfStr);
2553 QByteArray SelfByteArray = SelfStr.toUtf8();
2554 if (a_fReleaseLog)
2555 RTLogRelPrintf("%s\n", SelfByteArray.constData());
2556 else
2557 RTLogPrintf("%s\n", SelfByteArray.constData());
2558 }
2559
2560 /* the children */
2561 uint32_t const cChildren = a_pNode->cChildren;
2562 for (uint32_t i = 0; i < cChildren; i++)
2563 logNode(a_pNode->papChildren[i], a_fReleaseLog);
2564}
2565
2566
2567void
2568VBoxDbgStatsModel::logTree(QModelIndex &a_rRoot, bool a_fReleaseLog) const
2569{
2570 PDBGGUISTATSNODE pRoot = a_rRoot.isValid() ? nodeFromIndex(a_rRoot) : m_pRoot;
2571 if (pRoot)
2572 logNode(pRoot, a_fReleaseLog);
2573}
2574
2575
2576
2577
2578
2579
2580
2581/*
2582 *
2583 * V B o x D b g S t a t s M o d e l V M
2584 * V B o x D b g S t a t s M o d e l V M
2585 * V B o x D b g S t a t s M o d e l V M
2586 *
2587 *
2588 */
2589
2590
2591VBoxDbgStatsModelVM::VBoxDbgStatsModelVM(VBoxDbgGui *a_pDbgGui, QString &a_rPatStr, QObject *a_pParent)
2592 : VBoxDbgStatsModel(a_pParent), VBoxDbgBase(a_pDbgGui)
2593{
2594 /*
2595 * Create a model containing the STAM entries matching the pattern.
2596 * (The original idea was to get everything and rely on some hide/visible
2597 * flag that it turned out didn't exist.)
2598 */
2599 PDBGGUISTATSNODE pTree = createNewTree(a_rPatStr);
2600 setRootNode(pTree);
2601}
2602
2603
2604VBoxDbgStatsModelVM::~VBoxDbgStatsModelVM()
2605{
2606 /* nothing to do here. */
2607}
2608
2609
2610bool
2611VBoxDbgStatsModelVM::updateStatsByPattern(const QString &a_rPatStr)
2612{
2613 /** @todo the way we update this stuff is independent of the source (XML, file, STAM), our only
2614 * ASSUMPTION is that the input is strictly ordered by (fully slashed) name. So, all this stuff
2615 * should really move up into the parent class. */
2616 bool fRc = updatePrepare();
2617 if (fRc)
2618 {
2619 int rc = stamEnum(a_rPatStr, updateCallback, this);
2620 fRc = updateDone(RT_SUCCESS(rc));
2621 }
2622 return fRc;
2623}
2624
2625
2626void
2627VBoxDbgStatsModelVM::resetStatsByPattern(QString const &a_rPatStr)
2628{
2629 stamReset(a_rPatStr);
2630}
2631
2632
2633/*static*/ DECLCALLBACK(int)
2634VBoxDbgStatsModelVM::createNewTreeCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
2635 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
2636{
2637 PDBGGUISTATSNODE pRoot = (PDBGGUISTATSNODE)pvUser;
2638 Log3(("createNewTreeCallback: %s\n", pszName));
2639
2640 /*
2641 * Skip the ones which shouldn't be visible in the GUI.
2642 */
2643 if (enmVisibility == STAMVISIBILITY_NOT_GUI)
2644 return 0;
2645
2646 /*
2647 * Perform a mkdir -p like operation till we've walked / created the entire path down
2648 * to the node specfied node. Remember the last node as that will be the one we will
2649 * stuff the data into.
2650 */
2651 AssertReturn(*pszName == '/' && pszName[1] != '/', VERR_INTERNAL_ERROR);
2652 PDBGGUISTATSNODE pNode = pRoot;
2653 const char *pszCur = pszName + 1;
2654 while (*pszCur)
2655 {
2656 /* find the end of this component. */
2657 const char *pszNext = strchr(pszCur, '/');
2658 if (!pszNext)
2659 pszNext = strchr(pszCur, '\0');
2660 size_t cchCur = pszNext - pszCur;
2661
2662 /* Create it if it doesn't exist (it will be last if it exists). */
2663 if ( !pNode->cChildren
2664 || strncmp(pNode->papChildren[pNode->cChildren - 1]->pszName, pszCur, cchCur)
2665 || pNode->papChildren[pNode->cChildren - 1]->pszName[cchCur])
2666 {
2667 pNode = createAndInsertNode(pNode, pszCur, pszNext - pszCur, UINT32_MAX);
2668 if (!pNode)
2669 return VERR_NO_MEMORY;
2670 }
2671 else
2672 pNode = pNode->papChildren[pNode->cChildren - 1];
2673
2674 /* Advance */
2675 pszCur = *pszNext ? pszNext + 1 : pszNext;
2676 }
2677
2678 /*
2679 * Save the data.
2680 */
2681 return initNode(pNode, enmType, pvSample, enmUnit, pszDesc);
2682}
2683
2684
2685PDBGGUISTATSNODE
2686VBoxDbgStatsModelVM::createNewTree(QString &a_rPatStr)
2687{
2688 PDBGGUISTATSNODE pRoot = createRootNode();
2689 if (pRoot)
2690 {
2691 int rc = stamEnum(a_rPatStr, createNewTreeCallback, pRoot);
2692 if (RT_SUCCESS(rc))
2693 return pRoot;
2694
2695 /* failed, cleanup. */
2696 destroyTree(pRoot);
2697 }
2698
2699 return NULL;
2700}
2701
2702
2703
2704
2705
2706
2707
2708
2709/*
2710 *
2711 * V B o x D b g S t a t s V i e w
2712 * V B o x D b g S t a t s V i e w
2713 * V B o x D b g S t a t s V i e w
2714 *
2715 *
2716 */
2717
2718
2719VBoxDbgStatsView::VBoxDbgStatsView(VBoxDbgGui *a_pDbgGui, VBoxDbgStatsModel *a_pModel, VBoxDbgStats *a_pParent/* = NULL*/)
2720 : QTreeView(a_pParent), VBoxDbgBase(a_pDbgGui), m_pModel(a_pModel), m_PatStr(), m_pParent(a_pParent),
2721 m_pLeafMenu(NULL), m_pBranchMenu(NULL), m_pViewMenu(NULL), m_pCurMenu(NULL), m_CurIndex()
2722
2723{
2724 /*
2725 * Set the model and view defaults.
2726 */
2727 setRootIsDecorated(true);
2728 setModel(m_pModel);
2729 QModelIndex RootIdx = m_pModel->getRootIndex(); /* This should really be QModelIndex(), but Qt on darwin does wrong things then. */
2730 setRootIndex(RootIdx);
2731 setItemsExpandable(true);
2732 setAlternatingRowColors(true);
2733 setSelectionBehavior(SelectRows);
2734 setSelectionMode(SingleSelection);
2735 /// @todo sorting setSortingEnabled(true);
2736
2737 /*
2738 * Create and setup the actions.
2739 */
2740 m_pExpandAct = new QAction("Expand Tree", this);
2741 m_pCollapseAct = new QAction("Collapse Tree", this);
2742 m_pRefreshAct = new QAction("&Refresh", this);
2743 m_pResetAct = new QAction("Rese&t", this);
2744 m_pCopyAct = new QAction("&Copy", this);
2745 m_pToLogAct = new QAction("To &Log", this);
2746 m_pToRelLogAct = new QAction("T&o Release Log", this);
2747 m_pAdjColumns = new QAction("&Adjust Columns", this);
2748
2749 m_pCopyAct->setShortcut(QKeySequence::Copy);
2750 m_pExpandAct->setShortcut(QKeySequence("Ctrl+E"));
2751 m_pCollapseAct->setShortcut(QKeySequence("Ctrl+D"));
2752 m_pRefreshAct->setShortcut(QKeySequence("Ctrl+R"));
2753 m_pResetAct->setShortcut(QKeySequence("Alt+R"));
2754 m_pToLogAct->setShortcut(QKeySequence("Ctrl+Z"));
2755 m_pToRelLogAct->setShortcut(QKeySequence("Alt+Z"));
2756 m_pAdjColumns->setShortcut(QKeySequence("Ctrl+A"));
2757
2758 addAction(m_pCopyAct);
2759 addAction(m_pExpandAct);
2760 addAction(m_pCollapseAct);
2761 addAction(m_pRefreshAct);
2762 addAction(m_pResetAct);
2763 addAction(m_pToLogAct);
2764 addAction(m_pToRelLogAct);
2765 addAction(m_pAdjColumns);
2766
2767 connect(m_pExpandAct, SIGNAL(triggered(bool)), this, SLOT(actExpand()));
2768 connect(m_pCollapseAct, SIGNAL(triggered(bool)), this, SLOT(actCollapse()));
2769 connect(m_pRefreshAct, SIGNAL(triggered(bool)), this, SLOT(actRefresh()));
2770 connect(m_pResetAct, SIGNAL(triggered(bool)), this, SLOT(actReset()));
2771 connect(m_pCopyAct, SIGNAL(triggered(bool)), this, SLOT(actCopy()));
2772 connect(m_pToLogAct, SIGNAL(triggered(bool)), this, SLOT(actToLog()));
2773 connect(m_pToRelLogAct, SIGNAL(triggered(bool)), this, SLOT(actToRelLog()));
2774 connect(m_pAdjColumns, SIGNAL(triggered(bool)), this, SLOT(actAdjColumns()));
2775
2776
2777 /*
2778 * Create the menus and populate them.
2779 */
2780 setContextMenuPolicy(Qt::DefaultContextMenu);
2781
2782 m_pLeafMenu = new QMenu();
2783 m_pLeafMenu->addAction(m_pCopyAct);
2784 m_pLeafMenu->addAction(m_pRefreshAct);
2785 m_pLeafMenu->addAction(m_pResetAct);
2786 m_pLeafMenu->addAction(m_pToLogAct);
2787 m_pLeafMenu->addAction(m_pToRelLogAct);
2788
2789 m_pBranchMenu = new QMenu(this);
2790 m_pBranchMenu->addAction(m_pCopyAct);
2791 m_pBranchMenu->addAction(m_pRefreshAct);
2792 m_pBranchMenu->addAction(m_pResetAct);
2793 m_pBranchMenu->addAction(m_pToLogAct);
2794 m_pBranchMenu->addAction(m_pToRelLogAct);
2795 m_pBranchMenu->addSeparator();
2796 m_pBranchMenu->addAction(m_pExpandAct);
2797 m_pBranchMenu->addAction(m_pCollapseAct);
2798
2799 m_pViewMenu = new QMenu();
2800 m_pViewMenu->addAction(m_pCopyAct);
2801 m_pViewMenu->addAction(m_pRefreshAct);
2802 m_pViewMenu->addAction(m_pResetAct);
2803 m_pViewMenu->addAction(m_pToLogAct);
2804 m_pViewMenu->addAction(m_pToRelLogAct);
2805 m_pViewMenu->addSeparator();
2806 m_pViewMenu->addAction(m_pExpandAct);
2807 m_pViewMenu->addAction(m_pCollapseAct);
2808 m_pViewMenu->addSeparator();
2809 m_pViewMenu->addAction(m_pAdjColumns);
2810
2811 /* the header menu */
2812 QHeaderView *pHdrView = header();
2813 pHdrView->setContextMenuPolicy(Qt::CustomContextMenu);
2814 connect(pHdrView, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(headerContextMenuRequested(const QPoint &)));
2815}
2816
2817
2818VBoxDbgStatsView::~VBoxDbgStatsView()
2819{
2820 m_pParent = NULL;
2821 m_pCurMenu = NULL;
2822 m_CurIndex = QModelIndex();
2823
2824#define DELETE_IT(m) if (m) { delete m; m = NULL; } else do {} while (0)
2825 DELETE_IT(m_pModel);
2826
2827 DELETE_IT(m_pLeafMenu);
2828 DELETE_IT(m_pBranchMenu);
2829 DELETE_IT(m_pViewMenu);
2830
2831 DELETE_IT(m_pExpandAct);
2832 DELETE_IT(m_pCollapseAct);
2833 DELETE_IT(m_pRefreshAct);
2834 DELETE_IT(m_pResetAct);
2835 DELETE_IT(m_pCopyAct);
2836 DELETE_IT(m_pToLogAct);
2837 DELETE_IT(m_pToRelLogAct);
2838 DELETE_IT(m_pAdjColumns);
2839#undef DELETE_IT
2840}
2841
2842
2843void
2844VBoxDbgStatsView::updateStats(const QString &rPatStr)
2845{
2846 m_PatStr = rPatStr;
2847 if (m_pModel->updateStatsByPattern(rPatStr))
2848 setRootIndex(m_pModel->getRootIndex()); /* hack */
2849}
2850
2851
2852void
2853VBoxDbgStatsView::resizeColumnsToContent()
2854{
2855 for (int i = 0; i <= 8; i++)
2856 {
2857 resizeColumnToContents(i);
2858 /* Some extra room for distinguishing numbers better in Value, Min, Avg, Max, Total, dInt columns. */
2859 if (i >= 2 && i <= 7)
2860 setColumnWidth(i, columnWidth(i) + 10);
2861 }
2862}
2863
2864
2865void
2866VBoxDbgStatsView::setSubTreeExpanded(QModelIndex const &a_rIndex, bool a_fExpanded)
2867{
2868 int cRows = m_pModel->rowCount(a_rIndex);
2869 for (int i = 0; i < cRows; i++)
2870 setSubTreeExpanded(a_rIndex.child(i, 0), a_fExpanded);
2871 setExpanded(a_rIndex, a_fExpanded);
2872}
2873
2874
2875void
2876VBoxDbgStatsView::contextMenuEvent(QContextMenuEvent *a_pEvt)
2877{
2878 /*
2879 * Get the selected item.
2880 * If it's a mouse event select the item under the cursor (if any).
2881 */
2882 QModelIndex Idx;
2883 if (a_pEvt->reason() == QContextMenuEvent::Mouse)
2884 {
2885 Idx = indexAt(a_pEvt->pos());
2886 if (Idx.isValid())
2887 setCurrentIndex(Idx);
2888 }
2889 else
2890 {
2891 QModelIndexList SelIdx = selectedIndexes();
2892 if (!SelIdx.isEmpty())
2893 Idx = SelIdx.at(0);
2894 }
2895
2896 /*
2897 * Popup the corresponding menu.
2898 */
2899 QMenu *pMenu;
2900 if (!Idx.isValid())
2901 pMenu = m_pViewMenu;
2902 else if (m_pModel->hasChildren(Idx))
2903 pMenu = m_pBranchMenu;
2904 else
2905 pMenu = m_pLeafMenu;
2906 if (pMenu)
2907 {
2908 m_pRefreshAct->setEnabled(!Idx.isValid() || Idx == m_pModel->getRootIndex());
2909 m_CurIndex = Idx;
2910 m_pCurMenu = pMenu;
2911
2912 pMenu->exec(a_pEvt->globalPos());
2913
2914 m_pCurMenu = NULL;
2915 m_CurIndex = QModelIndex();
2916 if (m_pRefreshAct)
2917 m_pRefreshAct->setEnabled(true);
2918 }
2919 a_pEvt->accept();
2920}
2921
2922
2923void
2924VBoxDbgStatsView::headerContextMenuRequested(const QPoint &a_rPos)
2925{
2926 /*
2927 * Show the view menu.
2928 */
2929 if (m_pViewMenu)
2930 {
2931 m_pRefreshAct->setEnabled(true);
2932 m_CurIndex = m_pModel->getRootIndex();
2933 m_pCurMenu = m_pViewMenu;
2934
2935 m_pViewMenu->exec(header()->mapToGlobal(a_rPos));
2936
2937 m_pCurMenu = NULL;
2938 m_CurIndex = QModelIndex();
2939 if (m_pRefreshAct)
2940 m_pRefreshAct->setEnabled(true);
2941 }
2942}
2943
2944
2945void
2946VBoxDbgStatsView::actExpand()
2947{
2948 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2949 if (Idx.isValid())
2950 setSubTreeExpanded(Idx, true /* a_fExpanded */);
2951}
2952
2953
2954void
2955VBoxDbgStatsView::actCollapse()
2956{
2957 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2958 if (Idx.isValid())
2959 setSubTreeExpanded(Idx, false /* a_fExpanded */);
2960}
2961
2962
2963void
2964VBoxDbgStatsView::actRefresh()
2965{
2966 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2967 if (!Idx.isValid() || Idx == m_pModel->getRootIndex())
2968 {
2969 if (m_pModel->updateStatsByPattern(m_PatStr))
2970 setRootIndex(m_pModel->getRootIndex()); /* hack */
2971 }
2972 else
2973 m_pModel->updateStatsByIndex(Idx);
2974}
2975
2976
2977void
2978VBoxDbgStatsView::actReset()
2979{
2980 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2981 if (!Idx.isValid() || Idx == m_pModel->getRootIndex())
2982 m_pModel->resetStatsByPattern(m_PatStr);
2983 else
2984 m_pModel->resetStatsByIndex(Idx);
2985}
2986
2987
2988void
2989VBoxDbgStatsView::actCopy()
2990{
2991 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
2992 m_pModel->copyTreeToClipboard(Idx);
2993}
2994
2995
2996void
2997VBoxDbgStatsView::actToLog()
2998{
2999 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
3000 m_pModel->logTree(Idx, false /* a_fReleaseLog */);
3001}
3002
3003
3004void
3005VBoxDbgStatsView::actToRelLog()
3006{
3007 QModelIndex Idx = m_pCurMenu ? m_CurIndex : currentIndex();
3008 m_pModel->logTree(Idx, true /* a_fReleaseLog */);
3009}
3010
3011
3012void
3013VBoxDbgStatsView::actAdjColumns()
3014{
3015 resizeColumnsToContent();
3016}
3017
3018
3019
3020
3021
3022
3023/*
3024 *
3025 * V B o x D b g S t a t s
3026 * V B o x D b g S t a t s
3027 * V B o x D b g S t a t s
3028 *
3029 *
3030 */
3031
3032
3033VBoxDbgStats::VBoxDbgStats(VBoxDbgGui *a_pDbgGui, const char *pszPat/* = NULL*/, unsigned uRefreshRate/* = 0*/, QWidget *pParent/* = NULL*/)
3034 : VBoxDbgBaseWindow(a_pDbgGui, pParent), m_PatStr(pszPat), m_pPatCB(NULL), m_uRefreshRate(0), m_pTimer(NULL), m_pView(NULL)
3035{
3036 /* Assign window-title: */
3037 if (parent())
3038 {
3039 setWindowTitle(QString("%1 - Statistics").arg(parentWidget()->windowTitle()));
3040 parent()->installEventFilter(this);
3041 }
3042 else
3043 setWindowTitle("VBoxDbg - Statistics");
3044
3045 /*
3046 * On top, a horizontal box with the pattern field, buttons and refresh interval.
3047 */
3048 QHBoxLayout *pHLayout = new QHBoxLayout;
3049
3050 QLabel *pLabel = new QLabel(" Pattern ");
3051 pHLayout->addWidget(pLabel);
3052 pLabel->setMaximumSize(pLabel->sizeHint());
3053 pLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
3054
3055 m_pPatCB = new QComboBox();
3056 m_pPatCB->setAutoCompletion(false);
3057 pHLayout->addWidget(m_pPatCB);
3058 if (!m_PatStr.isEmpty())
3059 m_pPatCB->addItem(m_PatStr);
3060 m_pPatCB->setDuplicatesEnabled(false);
3061 m_pPatCB->setEditable(true);
3062 connect(m_pPatCB, SIGNAL(activated(const QString &)), this, SLOT(apply(const QString &)));
3063
3064 QPushButton *pPB = new QPushButton("&All");
3065 pHLayout->addWidget(pPB);
3066 pPB->setMaximumSize(pPB->sizeHint());
3067 connect(pPB, SIGNAL(clicked()), this, SLOT(applyAll()));
3068
3069 pLabel = new QLabel(" Interval ");
3070 pHLayout->addWidget(pLabel);
3071 pLabel->setMaximumSize(pLabel->sizeHint());
3072 pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
3073
3074 QSpinBox *pSB = new QSpinBox();
3075 pHLayout->addWidget(pSB);
3076 pSB->setMinimum(0);
3077 pSB->setMaximum(60);
3078 pSB->setSingleStep(1);
3079 pSB->setValue(uRefreshRate);
3080 pSB->setSuffix(" s");
3081 pSB->setWrapping(false);
3082 pSB->setButtonSymbols(QSpinBox::PlusMinus);
3083 pSB->setMaximumSize(pSB->sizeHint());
3084 connect(pSB, SIGNAL(valueChanged(int)), this, SLOT(setRefresh(int)));
3085
3086 /*
3087 * Create the tree view and setup the layout.
3088 */
3089 VBoxDbgStatsModelVM *pModel = new VBoxDbgStatsModelVM(a_pDbgGui, m_PatStr, NULL);
3090 m_pView = new VBoxDbgStatsView(a_pDbgGui, pModel, this);
3091
3092 QWidget *pHBox = new QWidget;
3093 pHBox->setLayout(pHLayout);
3094
3095 QVBoxLayout *pVLayout = new QVBoxLayout;
3096 pVLayout->addWidget(pHBox);
3097 pVLayout->addWidget(m_pView);
3098 setLayout(pVLayout);
3099
3100 /*
3101 * Resize the columns.
3102 * Seems this has to be done with all nodes expanded.
3103 */
3104 m_pView->expandAll();
3105 m_pView->resizeColumnsToContent();
3106 m_pView->collapseAll();
3107
3108 /*
3109 * Create a refresh timer and start it.
3110 */
3111 m_pTimer = new QTimer(this);
3112 connect(m_pTimer, SIGNAL(timeout()), this, SLOT(refresh()));
3113 setRefresh(uRefreshRate);
3114
3115 /*
3116 * And some shortcuts.
3117 */
3118 m_pFocusToPat = new QAction("", this);
3119 m_pFocusToPat->setShortcut(QKeySequence("Ctrl+L"));
3120 addAction(m_pFocusToPat);
3121 connect(m_pFocusToPat, SIGNAL(triggered(bool)), this, SLOT(actFocusToPat()));
3122}
3123
3124
3125VBoxDbgStats::~VBoxDbgStats()
3126{
3127 if (m_pTimer)
3128 {
3129 delete m_pTimer;
3130 m_pTimer = NULL;
3131 }
3132
3133 if (m_pPatCB)
3134 {
3135 delete m_pPatCB;
3136 m_pPatCB = NULL;
3137 }
3138
3139 if (m_pView)
3140 {
3141 delete m_pView;
3142 m_pView = NULL;
3143 }
3144}
3145
3146
3147void
3148VBoxDbgStats::closeEvent(QCloseEvent *a_pCloseEvt)
3149{
3150 a_pCloseEvt->accept();
3151 delete this;
3152}
3153
3154
3155bool VBoxDbgStats::eventFilter(QObject *pWatched, QEvent *pEvent)
3156{
3157 /* Skip events which are not related to our parent: */
3158 if (pWatched != parent())
3159 return VBoxDbgBaseWindow::eventFilter(pWatched, pEvent);
3160
3161 /* Depending on event-type: */
3162 switch (pEvent->type())
3163 {
3164 case QEvent::WindowTitleChange: setWindowTitle(QString("%1 - Statistics").arg(parentWidget()->windowTitle())); break;
3165 default: break;
3166 }
3167
3168 /* Call to base-class: */
3169 return VBoxDbgBaseWindow::eventFilter(pWatched, pEvent);
3170}
3171
3172
3173void
3174VBoxDbgStats::apply(const QString &Str)
3175{
3176 m_PatStr = Str;
3177 refresh();
3178}
3179
3180
3181void
3182VBoxDbgStats::applyAll()
3183{
3184 apply("");
3185}
3186
3187
3188
3189void
3190VBoxDbgStats::refresh()
3191{
3192 m_pView->updateStats(m_PatStr);
3193}
3194
3195
3196void
3197VBoxDbgStats::setRefresh(int iRefresh)
3198{
3199 if ((unsigned)iRefresh != m_uRefreshRate)
3200 {
3201 if (!m_uRefreshRate || iRefresh)
3202 m_pTimer->start(iRefresh * 1000);
3203 else
3204 m_pTimer->stop();
3205 m_uRefreshRate = iRefresh;
3206 }
3207}
3208
3209
3210void
3211VBoxDbgStats::actFocusToPat()
3212{
3213 if (!m_pPatCB->hasFocus())
3214 m_pPatCB->setFocus(Qt::ShortcutFocusReason);
3215}
3216
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