VirtualBox

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

Last change on this file since 12883 was 12852, checked in by vboxsync, 17 years ago

build fix.

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

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