VirtualBox

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

Last change on this file since 12660 was 12660, checked in by vboxsync, 16 years ago

Debugger: coding.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.5 KB
Line 
1/* $Id: VBoxDbgStatsQt4.cpp 12660 2008-09-22 23:57:52Z 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.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 * 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
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#include <stdio.h> //remove me
46
47
48/*******************************************************************************
49* Defined Constants And Macros *
50*******************************************************************************/
51/** The number of column. */
52#define DBGGUI_STATS_COLUMNS 9
53
54
55
56/*******************************************************************************
57* Structures and Typedefs *
58*******************************************************************************/
59/**
60 * The state of a statistics sample node.
61 *
62 * This is used for two pass refresh (1. get data, 2. update the view) and
63 * for saving the result of a diff.
64 */
65typedef enum DBGGUISTATSNODESTATE
66{
67 /** The typical invalid zeroth entry. */
68 kDbgGuiStatsNodeState_kInvalid = 0,
69 /** The node is the root node. */
70 kDbgGuiStatsNodeState_kRoot,
71 /** The node is visible. */
72 kDbgGuiStatsNodeState_kVisible,
73 /** The node is invisible. */
74 kDbgGuiStatsNodeState_kInvisible,
75 /** The node should be made visble. */
76 kDbgGuiStatsNodeState_kMakeVisible,
77 /** The node should be made invisble. */
78 kDbgGuiStatsNodeState_kMakeInvisible,
79 /** The node should be refreshed. */
80 kDbgGuiStatsNodeState_kRefresh,
81 /** diff: The node equals. */
82 kDbgGuiStatsNodeState_kDiffEqual,
83 /** diff: The node in set 1 is less than the one in set 2. */
84 kDbgGuiStatsNodeState_kDiffSmaller,
85 /** diff: The node in set 1 is greater than the one in set 2. */
86 kDbgGuiStatsNodeState_kDiffGreater,
87 /** diff: The node is only in set 1. */
88 kDbgGuiStatsNodeState_kDiffOnlyIn1,
89 /** diff: The node is only in set 2. */
90 kDbgGuiStatsNodeState_kDiffOnlyIn2,
91 /** The end of the valid state values. */
92 kDbgGuiStatsNodeState_kEnd
93} DBGGUISTATENODESTATE;
94
95
96/**
97 * A tree node representing a statistic sample.
98 *
99 * The nodes carry a reference to the parent and to its position among its
100 * siblings. Both of these need updating when the grand parent or parent adds a
101 * new child. This will hopefully not be too expensive but rather pay off when
102 * we need to create a parent index.
103 */
104typedef struct DBGGUISTATSNODE
105{
106 /** Pointer to the parent. */
107 PDBGGUISTATSNODE pParent;
108 /** Array of pointers to the child nodes. */
109 PDBGGUISTATSNODE *papChildren;
110 /** The number of children. */
111 uint32_t cChildren;
112 /** Our index among the parent's children. */
113 uint32_t iSelf;
114 /** The unit. */
115 STAMUNIT enmUnit;
116 /** The data type. */
117 STAMTYPE enmType;
118 /** The data at last update. */
119 union
120 {
121 /** STAMTYPE_COUNTER. */
122 STAMCOUNTER Counter;
123 /** STAMTYPE_PROFILE. */
124 STAMPROFILE Profile;
125 /** STAMTYPE_PROFILE_ADV. */
126 STAMPROFILEADV ProfileAdv;
127 /** STAMTYPE_RATIO_U32. */
128 STAMRATIOU32 RatioU32;
129 /** STAMTYPE_U8 & STAMTYPE_U8_RESET. */
130 uint8_t u8;
131 /** STAMTYPE_U16 & STAMTYPE_U16_RESET. */
132 uint16_t u16;
133 /** STAMTYPE_U32 & STAMTYPE_U32_RESET. */
134 uint32_t u32;
135 /** STAMTYPE_U64 & STAMTYPE_U64_RESET. */
136 uint64_t u64;
137 /** STAMTYPE_CALLBACK - buffer of 256 bytes. */
138 char *psz;
139 } Data;
140 /** The name. */
141 char *pszName;
142 /** The length of the name. */
143 size_t cchName;
144 /** The description string. */
145 QString *pDescStr;
146 /** The node state. */
147 DBGGUISTATENODESTATE enmState;
148 /** The delta. */
149 char szDelta[32];
150} DBGGUISTATSNODE;
151
152
153/**
154 * Recursion stack.
155 */
156typedef struct DBGGUISTATSSTACK
157{
158 /** The top stack entry. */
159 int32_t iTop;
160 /** The stack array. */
161 struct DBGGUISTATSSTACKENTRY
162 {
163 /** The node. */
164 PDBGGUISTATSNODE pNode;
165 /** The current child. */
166 int32_t iChild;
167 } a[32];
168} DBGGUISTATSSTACK;
169
170
171
172
173/**
174 * The item model for the statistics tree view.
175 *
176 * This manages the DBGGUISTATSNODE trees.
177 */
178class VBoxDbgStatsModel : public QAbstractItemModel
179{
180protected:
181 /** The root of the sample tree. */
182 PDBGGUISTATSNODE m_pRoot;
183
184public:
185 /**
186 * Constructor.
187 *
188 * @param a_pParent The parent object. See QAbstractItemModel in the Qt
189 * docs for details.
190 */
191 VBoxDbgStatsModel(QObject *a_pParent);
192
193 /**
194 * Destructor.
195 *
196 * This will free all the the data the model holds.
197 */
198 virtual ~VBoxDbgStatsModel();
199
200 /**
201 * Updates the data matching the specified pattern.
202 *
203 * Nodes not matched by the pattern will become invisible.
204 *
205 * @param a_rPatStr The selection pattern.
206 */
207 virtual void update(const QString &a_rPatStr) = 0;
208
209 /**
210 * Gets the model index of the root node.
211 *
212 * @returns root index.
213 */
214 QModelIndex getRootIndex(void) const;
215
216protected:
217 /**
218 * Set the root node.
219 *
220 * This will free all the current data before taking the ownership of the new
221 * root node and its children.
222 *
223 * @param a_pRoot The new root node.
224 */
225 void setRootNode(PDBGGUISTATSNODE a_pRoot);
226
227 /** Creates the root node. */
228 static PDBGGUISTATSNODE createRootNode(void);
229
230 /** Creates and insert a node under the given parent. */
231 static PDBGGUISTATSNODE createAndInsertNode(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition = UINT32_MAX);
232
233 /**
234 * Destroys a statistics tree.
235 *
236 * @param a_pRoot The root of the tree. NULL is fine.
237 */
238 static void destroyTree(PDBGGUISTATSNODE a_pRoot);
239
240 /**
241 * Stringifies exactly one node, no children.
242 *
243 * This is for logging and clipboard.
244 *
245 * @param a_pNode The node.
246 * @param a_rString The string to append the strigified node to.
247 */
248 static void stringifyNodeNoRecursion(PDBGGUISTATSNODE a_pNode, QString &a_rString);
249
250 /**
251 * Stringifies a node and its children.
252 *
253 * This is for logging and clipboard.
254 *
255 * @param a_pNode The node.
256 * @param a_rString The string to append the strigified node to.
257 */
258 static void stringifyNode(PDBGGUISTATSNODE a_pNode, QString &a_rString);
259
260public:
261 /**
262 * Converts the specified tree to string.
263 *
264 * This is for logging and clipboard.
265 *
266 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
267 * @param a_rString Where to return to return the string dump.
268 */
269 void stringifyTree(QModelIndex &a_rRoot, QString &a_rString) const;
270
271 /**
272 * Dumps the given (sub-)tree as XML.
273 *
274 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
275 * @param a_rString Where to return to return the XML dump.
276 */
277 void xmlifyTree(QModelIndex &a_rRoot, QString &a_rString) const;
278
279 /**
280 * Puts the stringified tree on the clipboard.
281 *
282 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
283 */
284 void copyTreeToClipboard(QModelIndex &a_rRoot) const;
285
286
287protected:
288 /** Worker for logTree. */
289 static void logNode(PDBGGUISTATSNODE a_pNode, bool a_fReleaseLog);
290
291public:
292 /** Logs a (sub-)tree.
293 *
294 * @param a_rRoot Where to start. Use QModelIndex() to start at the root.
295 * @param a_fReleaseLog Whether to use the release log (true) or the debug log (false).
296 */
297 void logTree(QModelIndex &a_rRoot, bool a_fReleaseLog) const;
298
299protected:
300 /** Gets the unit. */
301 static QString strUnit(PCDBGGUISTATSNODE pNode);
302 /** Gets the value. */
303 static QString strValue(PCDBGGUISTATSNODE pNode);
304
305 /**
306 * Destroys a node and all its children.
307 *
308 * @param a_pNode The node to destroy.
309 */
310 static void destroyNode(PDBGGUISTATSNODE a_pNode);
311
312 /**
313 * Converts an index to a node pointer.
314 *
315 * @returns Pointer to the node, NULL if invalid reference.
316 * @param a_rIndex Reference to the index
317 */
318 inline PDBGGUISTATSNODE nodeFromIndex(const QModelIndex &a_rIndex) const
319 {
320 if (RT_LIKELY(a_rIndex.isValid()))
321 return (PDBGGUISTATSNODE)a_rIndex.internalPointer();
322 return NULL;
323 }
324
325public:
326
327 /** @name Overriden QAbstractItemModel methods
328 * @{ */
329 virtual int columnCount(const QModelIndex &a_rParent) const;
330 virtual QVariant data(const QModelIndex &a_rIndex, int a_eRole) const;
331 virtual Qt::ItemFlags flags(const QModelIndex &a_rIndex) const;
332 virtual bool hasChildren(const QModelIndex &a_rParent) const;
333 virtual QVariant headerData(int a_iSection, Qt::Orientation a_ePrientation, int a_eRole) const;
334 virtual QModelIndex index(int a_iRow, int a_iColumn, const QModelIndex &a_rParent) const;
335 virtual QModelIndex parent(const QModelIndex &a_rChild) const;
336 virtual int rowCount(const QModelIndex &a_rParent) const;
337 ///virtual void sort(int a_iColumn, Qt::SortOrder a_eOrder);
338 /** @} */
339};
340
341
342/**
343 * Model using the VM / STAM interface as data source.
344 */
345class VBoxDbgStatsModelVM : public VBoxDbgStatsModel, public VBoxDbgBase
346{
347public:
348 /**
349 * Constructor.
350 *
351 * @param a_pVM The VM handle.
352 * @param a_rPatStr The selection pattern.
353 * @param a_pParent The parent object. NULL is fine.
354 */
355 VBoxDbgStatsModelVM(PVM a_pVM, QString &a_rPatStr, QObject *a_pParent);
356
357 /** Destructor */
358 virtual ~VBoxDbgStatsModelVM();
359
360 /**
361 * Updates the data matching the specified pattern.
362 *
363 * Nodes not matched by the pattern will become invisible.
364 *
365 * @param a_rPatStr The selection pattern.
366 */
367 virtual void update(const QString &a_rPatStr);
368
369protected:
370 /**
371 * Enumeration callback used by update.
372 */
373 static DECLCALLBACK(int) updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
374 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser);
375
376 /**
377 * Initializes a new node.
378 */
379 static int initNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, STAMVISIBILITY enmVisibility, const char *pszDesc);
380
381 /**
382 * Enumeration callback used by createNewTree.
383 */
384 static DECLCALLBACK(int) createNewTreeCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
385 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser);
386
387 /**
388 * Constructs a new statistics tree by query data from the VM.
389 *
390 * @returns Pointer to the root of the tree we've constructed. This will be NULL
391 * if the STAM API throws an error or we run out of memory.
392 * @param a_rPatStr The selection pattern.
393 */
394 PDBGGUISTATSNODE createNewTree(QString &a_rPatStr);
395
396protected:
397 /** Next update child. This is UINT32_MAX when invalid. */
398 uint32_t m_iUpdateChild;
399 /** Pointer to the node m_szUpdateParent represent and m_iUpdateChild refers to. */
400 PDBGGUISTATSNODE m_pUpdateParent;
401 /** The length of the path. */
402 size_t m_cchUpdateParent;
403 /** The path to the current update parent, including a trailing slash. */
404 char m_szUpdateParent[1024];
405 /** Inserted or/and removed nodes during the update. */
406 bool m_fUpdateInsertRemove;
407};
408
409
410/*******************************************************************************
411* Internal Functions *
412*******************************************************************************/
413
414
415/**
416 * Formats a number into a 64-byte buffer.
417 */
418static char *formatNumber(char *psz, uint64_t u64)
419{
420 static const char s_szDigits[] = "0123456789";
421 psz += 63;
422 *psz-- = '\0';
423 unsigned cDigits = 0;
424 for (;;)
425 {
426 const unsigned iDigit = u64 % 10;
427 u64 /= 10;
428 *psz = s_szDigits[iDigit];
429 if (!u64)
430 break;
431 psz--;
432 if (!(++cDigits % 3))
433 *psz-- = ',';
434 }
435 return psz;
436}
437
438
439#if 0
440/**
441 * Formats a number into a 64-byte buffer.
442 * (18.446.744.073.709.551.615)
443 */
444static char *formatNumberSigned(char *psz, int64_t i64)
445{
446 static const char s_szDigits[] = "0123456789";
447 psz += 63;
448 *psz-- = '\0';
449 const bool fNegative = i64 < 0;
450 uint64_t u64 = fNegative ? -i64 : i64;
451 unsigned cDigits = 0;
452 for (;;)
453 {
454 const unsigned iDigit = u64 % 10;
455 u64 /= 10;
456 *psz = s_szDigits[iDigit];
457 if (!u64)
458 break;
459 psz--;
460 if (!(++cDigits % 3))
461 *psz-- = ',';
462 }
463 if (fNegative)
464 *--psz = '-';
465 return psz;
466}
467#endif
468
469
470/**
471 * Formats a unsigned hexadecimal number into a into a 64-byte buffer.
472 */
473static char *formatHexNumber(char *psz, uint64_t u64, unsigned cZeros)
474{
475 static const char s_szDigits[] = "0123456789abcdef";
476 psz += 63;
477 *psz-- = '\0';
478 unsigned cDigits = 0;
479 for (;;)
480 {
481 const unsigned iDigit = u64 % 16;
482 u64 /= 16;
483 *psz = s_szDigits[iDigit];
484 ++cDigits;
485 if (!u64 && cDigits >= cZeros)
486 break;
487 psz--;
488 if (!(cDigits % 8))
489 *psz-- = '\'';
490 }
491 return psz;
492}
493
494
495/**
496 * Formats a sort key number.
497 */
498static void formatSortKey(char *psz, uint64_t u64)
499{
500 static const char s_szDigits[] = "0123456789abcdef";
501 /* signed */
502 *psz++ = '+';
503
504 /* 16 hex digits */
505 psz[16] = '\0';
506 unsigned i = 16;
507 while (i-- > 0)
508 {
509 if (u64)
510 {
511 const unsigned iDigit = u64 % 16;
512 u64 /= 16;
513 psz[i] = s_szDigits[iDigit];
514 }
515 else
516 psz[i] = '0';
517 }
518}
519
520
521#if 0/* unused */
522/**
523 * Formats a sort key number.
524 */
525static void formatSortKeySigned(char *psz, int64_t i64)
526{
527 static const char s_szDigits[] = "0123456789abcdef";
528
529 /* signed */
530 uint64_t u64;
531 if (i64 >= 0)
532 {
533 *psz++ = '+';
534 u64 = i64;
535 }
536 else
537 {
538 *psz++ = '-';
539 u64 = -i64;
540 }
541
542 /* 16 hex digits */
543 psz[16] = '\0';
544 unsigned i = 16;
545 while (i-- > 0)
546 {
547 if (u64)
548 {
549 const unsigned iDigit = u64 % 16;
550 u64 /= 16;
551 psz[i] = s_szDigits[iDigit];
552 }
553 else
554 psz[i] = '0';
555 }
556}
557#endif
558
559
560
561/*
562 *
563 * V B o x D b g S t a t s M o d e l
564 * V B o x D b g S t a t s M o d e l
565 * V B o x D b g S t a t s M o d e l
566 *
567 *
568 */
569
570
571VBoxDbgStatsModel::VBoxDbgStatsModel(QObject *a_pParent)
572 : QAbstractItemModel(a_pParent), m_pRoot(NULL)
573{
574}
575
576
577
578VBoxDbgStatsModel::~VBoxDbgStatsModel()
579{
580 destroyTree(m_pRoot);
581 m_pRoot = NULL;
582}
583
584
585/*static*/ void
586VBoxDbgStatsModel::destroyTree(PDBGGUISTATSNODE a_pRoot)
587{
588 if (!a_pRoot)
589 return;
590 Assert(!a_pRoot->pParent);
591 Assert(!a_pRoot->iSelf);
592
593 destroyNode(a_pRoot);
594}
595
596
597/* static*/ void
598VBoxDbgStatsModel::destroyNode(PDBGGUISTATSNODE a_pNode)
599{
600 /* destroy all our children */
601 uint32_t i = a_pNode->cChildren;
602 while (i-- > 0)
603 {
604 destroyNode(a_pNode->papChildren[i]);
605 a_pNode->papChildren[i] = NULL;
606 }
607 a_pNode->cChildren = 0;
608
609 /* free the resources we're using */
610 a_pNode->pParent = NULL;
611
612 RTMemFree(a_pNode->papChildren);
613 a_pNode->papChildren = NULL;
614
615 a_pNode->cChildren = 0;
616 a_pNode->iSelf = UINT32_MAX;
617 a_pNode->enmUnit = STAMUNIT_INVALID;
618 a_pNode->enmType = STAMTYPE_INVALID;
619
620 RTMemFree(a_pNode->pszName);
621 a_pNode->pszName = NULL;
622
623 if (a_pNode->pDescStr)
624 {
625 delete a_pNode->pDescStr;
626 a_pNode->pDescStr = NULL;
627 }
628
629 /* Finally ourselves */
630 a_pNode->enmState = kDbgGuiStatsNodeState_kInvalid;
631 RTMemFree(a_pNode);
632}
633
634
635/*static*/ PDBGGUISTATSNODE
636VBoxDbgStatsModel::createRootNode(void)
637{
638 PDBGGUISTATSNODE pRoot = (PDBGGUISTATSNODE)RTMemAllocZ(sizeof(DBGGUISTATSNODE));
639 if (!pRoot)
640 return NULL;
641 pRoot->iSelf = 0;
642 pRoot->enmType = STAMTYPE_INVALID;
643 pRoot->enmUnit = STAMUNIT_INVALID;
644 pRoot->pszName = (char *)RTMemDup("/", sizeof("/"));
645 pRoot->cchName = 1;
646 pRoot->enmState = kDbgGuiStatsNodeState_kRoot;
647
648 return pRoot;
649}
650
651
652/*static*/ PDBGGUISTATSNODE
653VBoxDbgStatsModel::createAndInsertNode(PDBGGUISTATSNODE pParent, const char *pszName, size_t cchName, uint32_t iPosition /*= UINT32_MAX*/)
654{
655 /*
656 * Create it.
657 */
658 PDBGGUISTATSNODE pNode = (PDBGGUISTATSNODE)RTMemAllocZ(sizeof(DBGGUISTATSNODE));
659 if (!pNode)
660 return NULL;
661 pNode->iSelf = UINT32_MAX;
662 pNode->enmType = STAMTYPE_INVALID;
663 pNode->enmUnit = STAMUNIT_INVALID;
664 pNode->pszName = (char *)RTMemDupEx(pszName, cchName, 1);
665 pNode->cchName = cchName;
666 pNode->enmState = kDbgGuiStatsNodeState_kVisible;
667
668 /*
669 * Do we need to expand the array?
670 */
671 if (!(pParent->cChildren & 31))
672 {
673 void *pvNew = RTMemRealloc(pParent->papChildren, sizeof(*pParent->papChildren) * (pParent->cChildren + 32));
674 if (!pvNew)
675 {
676 destroyNode(pNode);
677 return NULL;
678 }
679 pParent->papChildren = (PDBGGUISTATSNODE *)pvNew;
680 }
681
682 /*
683 * Insert it.
684 */
685 pNode->pParent = pParent;
686 if (iPosition >= pParent->cChildren)
687 /* Last. */
688 iPosition = pParent->cChildren;
689 else
690 {
691 /* Shift all the items after ours. */
692 uint32_t iShift = pParent->cChildren;
693 while (iShift-- > iPosition)
694 {
695 PDBGGUISTATSNODE pChild = pParent->papChildren[iShift];
696 pParent->papChildren[iShift + 1] = pChild;
697 pChild->iSelf = iShift + 1;
698 }
699 }
700
701 /* Insert ours */
702 pNode->iSelf = iPosition;
703 pParent->papChildren[iPosition] = pNode;
704 pParent->cChildren++;
705
706 return pNode;
707}
708
709
710#if 0
711/*static*/ PDBGGUISTATSNODE
712VBoxDbgStatsModel::createNewTree(IMachineDebugger *a_pIMachineDebugger)
713{
714 /** @todo */
715 return NULL;
716}
717#endif
718
719
720#if 0
721/*static*/ PDBGGUISTATSNODE
722VBoxDbgStatsModel::createNewTree(const char *pszFilename)
723{
724 /** @todo */
725 return NULL;
726}
727#endif
728
729
730#if 0
731/*static*/ PDBGGUISTATSNODE
732VBoxDbgStatsModel::createDiffTree(PDBGGUISTATSNODE pTree1, PDBGGUISTATSNODE pTree2)
733{
734 /** @todo */
735 return NULL;
736}
737#endif
738
739
740QModelIndex
741VBoxDbgStatsModel::getRootIndex(void) const
742{
743 if (!m_pRoot)
744 return QModelIndex();
745 return createIndex(0, 0, m_pRoot);
746}
747
748
749void
750VBoxDbgStatsModel::setRootNode(PDBGGUISTATSNODE a_pRoot)
751{
752 PDBGGUISTATSNODE pOldTree = m_pRoot;
753 m_pRoot = a_pRoot;
754 destroyTree(pOldTree);
755 reset();
756}
757
758
759Qt::ItemFlags
760VBoxDbgStatsModel::flags(const QModelIndex &a_rIndex) const
761{
762 Qt::ItemFlags fFlags = QAbstractItemModel::flags(a_rIndex);
763
764 PDBGGUISTATSNODE pNode = nodeFromIndex(a_rIndex);
765 if (pNode)
766 {
767 if (pNode->enmState == kDbgGuiStatsNodeState_kInvisible)
768 fFlags &= ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
769 }
770 return fFlags;
771}
772
773
774int
775VBoxDbgStatsModel::columnCount(const QModelIndex &a_rParent) const
776{
777 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
778 if ( pParent
779 && pParent->enmState == kDbgGuiStatsNodeState_kInvisible)
780 return 0;
781 return DBGGUI_STATS_COLUMNS;
782}
783
784
785int
786VBoxDbgStatsModel::rowCount(const QModelIndex &a_rParent) const
787{
788 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
789 if ( !pParent
790 || pParent->enmState == kDbgGuiStatsNodeState_kInvisible)
791 return 0;
792 return pParent->cChildren;
793}
794
795
796bool
797VBoxDbgStatsModel::hasChildren(const QModelIndex &a_rParent) const
798{
799 PDBGGUISTATSNODE pParent = nodeFromIndex(a_rParent);
800 return pParent
801 && pParent->cChildren > 0
802 && pParent->enmState != kDbgGuiStatsNodeState_kInvisible;
803}
804
805
806QModelIndex
807VBoxDbgStatsModel::index(int iRow, int iColumn, const QModelIndex &r_pParent) const
808{
809 PDBGGUISTATSNODE pParent = nodeFromIndex(r_pParent);
810 if (!pParent)
811 {
812 printf("index: iRow=%d iColumn=%d invalid parent\n", iRow, iColumn);
813 return QModelIndex(); /* bug? */
814 }
815 if ((unsigned)iRow >= pParent->cChildren)
816 {
817 printf("index: iRow=%d >= cChildren=%u (iColumn=%d)\n", iRow, (unsigned)pParent->cChildren, iColumn);
818 return QModelIndex(); /* bug? */
819 }
820 if ((unsigned)iColumn >= DBGGUI_STATS_COLUMNS)
821 {
822 printf("index: iColumn=%d (iRow=%d)\n", iColumn, iRow);
823 return QModelIndex(); /* bug? */
824 }
825 PDBGGUISTATSNODE pChild = pParent->papChildren[iRow];
826 if (pChild->enmState == kDbgGuiStatsNodeState_kInvisible)
827 {
828 printf("index: invisible (iColumn=%d iRow=%d)\n", iColumn, iRow);
829 return QModelIndex();
830 }
831
832 //printf("index: iRow=%d iColumn=%d %p %s/%s\n", iRow, iColumn, pParent->papChildren[iRow], pParent->pszName, pParent->papChildren[iRow]->pszName);
833 return createIndex(iRow, iColumn, pParent->papChildren[iRow]);
834}
835
836
837QModelIndex
838VBoxDbgStatsModel::parent(const QModelIndex &a_rChild) const
839{
840 PDBGGUISTATSNODE pChild = nodeFromIndex(a_rChild);
841 if (!pChild)
842 {
843 printf("parent: invalid child\n");
844 return QModelIndex(); /* bug */
845 }
846 PDBGGUISTATSNODE pParent = pChild->pParent;
847 if (!pParent)
848 {
849 printf("parent: root\n");
850 return QModelIndex(); /* we're root */
851 }
852
853 //printf("parent: iSelf=%d iColumn=%d %p %s(/%s)\n", pParent->iSelf, a_rChild.column(), pParent, pParent->pszName, pChild->pszName);
854 return createIndex(pParent->iSelf, a_rChild.column(), pParent);
855}
856
857
858QVariant
859VBoxDbgStatsModel::headerData(int a_iSection, Qt::Orientation a_eOrientation, int a_eRole) const
860{
861 if ( a_eOrientation != Qt::Horizontal
862 || a_eRole != Qt::DisplayRole)
863 return QVariant();
864 switch (a_iSection)
865 {
866 case 0: return tr("Name");
867 case 1: return tr("Unit");
868 case 2: return tr("Value/Times");
869 case 3: return tr("Min");
870 case 4: return tr("Average");
871 case 5: return tr("Max");
872 case 6: return tr("Total");
873 case 7: return tr("dInt");
874 case 8: return tr("Description");
875 default:
876 AssertCompile(DBGGUI_STATS_COLUMNS == 9);
877 return QVariant(); /* bug */
878 }
879}
880
881
882/*static*/ QString
883VBoxDbgStatsModel::strUnit(PCDBGGUISTATSNODE pNode)
884{
885 if (pNode->enmUnit == STAMUNIT_INVALID)
886 return "";
887 return STAMR3GetUnit(pNode->enmUnit);
888}
889
890
891/*static*/ QString
892VBoxDbgStatsModel::strValue(PCDBGGUISTATSNODE pNode)
893{
894 char sz[128];
895
896 switch (pNode->enmType)
897 {
898 case STAMTYPE_COUNTER:
899 return formatNumber(sz, pNode->Data.Counter.c);
900
901 case STAMTYPE_PROFILE:
902 case STAMTYPE_PROFILE_ADV:
903 if (!pNode->Data.Profile.cPeriods)
904 return "0";
905 return formatNumber(sz, pNode->Data.Profile.cPeriods);
906
907 case STAMTYPE_RATIO_U32:
908 case STAMTYPE_RATIO_U32_RESET:
909 {
910 formatNumber(sz, pNode->Data.RatioU32.u32A);
911 char *psz = strchr(sz, '\0');
912 *psz++ = ':';
913 formatNumber(psz, pNode->Data.RatioU32.u32B);
914 return psz;
915 }
916
917 case STAMTYPE_CALLBACK:
918 return pNode->Data.psz;
919
920 case STAMTYPE_U8:
921 case STAMTYPE_U8_RESET:
922 return formatNumber(sz, pNode->Data.u8);
923
924 case STAMTYPE_X8:
925 case STAMTYPE_X8_RESET:
926 return formatHexNumber(sz, pNode->Data.u8, 2);
927
928 case STAMTYPE_U16:
929 case STAMTYPE_U16_RESET:
930 return formatNumber(sz, pNode->Data.u16);
931
932 case STAMTYPE_X16:
933 case STAMTYPE_X16_RESET:
934 return formatHexNumber(sz, pNode->Data.u16, 4);
935
936 case STAMTYPE_U32:
937 case STAMTYPE_U32_RESET:
938 return formatNumber(sz, pNode->Data.u32);
939
940 case STAMTYPE_X32:
941 case STAMTYPE_X32_RESET:
942 return formatHexNumber(sz, pNode->Data.u32, 8);
943
944 case STAMTYPE_U64:
945 case STAMTYPE_U64_RESET:
946 return formatNumber(sz, pNode->Data.u64);
947
948 case STAMTYPE_X64:
949 case STAMTYPE_X64_RESET:
950 return formatHexNumber(sz, pNode->Data.u64, 16);
951
952 default:
953 AssertMsgFailed(("%d\n", pNode->enmType));
954 case STAMTYPE_INVALID:
955 return "";
956 }
957}
958
959
960QVariant
961VBoxDbgStatsModel::data(const QModelIndex &a_rIndex, int a_eRole) const
962{
963 unsigned iCol = a_rIndex.column();
964 if ( iCol >= DBGGUI_STATS_COLUMNS
965 || ( a_eRole != Qt::DisplayRole
966 /*&& a_eRole != Qt::XxxRole*/) )
967 return QVariant();
968
969 PDBGGUISTATSNODE pNode = nodeFromIndex(a_rIndex);
970 if ( !pNode
971 || pNode->enmState == kDbgGuiStatsNodeState_kInvisible)
972 return QVariant();
973
974 switch (iCol)
975 {
976 case 0:
977 return QString(pNode->pszName);
978
979 case 1:
980 return strUnit(pNode);
981 case 2:
982 return strValue(pNode);
983 case 3:
984 case 4:
985 case 5:
986 case 6:
987 return QString("todo"); /** @todo the rest of the data formatting... */
988 case 7:
989 return pNode->szDelta;
990 case 8:
991 return pNode->pDescStr ? QString(*pNode->pDescStr) : QString("");
992
993 default:
994 return QVariant();
995 }
996}
997
998
999/*static*/ void
1000VBoxDbgStatsModel::stringifyNodeNoRecursion(PDBGGUISTATSNODE a_pNode, QString &a_rString)
1001{
1002 NOREF(a_pNode);
1003 a_rString = "todo";
1004}
1005
1006
1007/*static*/ void
1008VBoxDbgStatsModel::stringifyNode(PDBGGUISTATSNODE a_pNode, QString &a_rString)
1009{
1010 /* this node */
1011 if (!a_rString.isEmpty())
1012 a_rString += "\n";
1013 stringifyNodeNoRecursion(a_pNode, a_rString);
1014
1015 /* the children */
1016 uint32_t const cChildren = a_pNode->cChildren;
1017 for (uint32_t i = 0; i < cChildren; i++)
1018 stringifyNode(a_pNode->papChildren[i], a_rString);
1019}
1020
1021
1022void
1023VBoxDbgStatsModel::stringifyTree(QModelIndex &a_rRoot, QString &a_rString) const
1024{
1025 PDBGGUISTATSNODE pRoot = a_rRoot.isValid() ? nodeFromIndex(a_rRoot) : m_pRoot;
1026 if (pRoot)
1027 stringifyNode(pRoot, a_rString);
1028}
1029
1030
1031void
1032VBoxDbgStatsModel::copyTreeToClipboard(QModelIndex &a_rRoot) const
1033{
1034 QString String;
1035 stringifyTree(a_rRoot, String);
1036
1037 QClipboard *pClipboard = QApplication::clipboard();
1038 if (pClipboard)
1039 pClipboard->setText(String, QClipboard::Clipboard);
1040}
1041
1042
1043/*static*/ void
1044VBoxDbgStatsModel::logNode(PDBGGUISTATSNODE a_pNode, bool a_fReleaseLog)
1045{
1046 /* this node */
1047 QString SelfStr;
1048 stringifyNodeNoRecursion(a_pNode, SelfStr);
1049 QByteArray SelfByteArray = SelfStr.toUtf8();
1050 if (a_fReleaseLog)
1051 RTLogRelPrintf("%s\n", SelfByteArray.constData());
1052 else
1053 RTLogPrintf("%s\n", SelfByteArray.constData());
1054
1055 /* the children */
1056 uint32_t const cChildren = a_pNode->cChildren;
1057 for (uint32_t i = 0; i < cChildren; i++)
1058 logNode(a_pNode->papChildren[i], a_fReleaseLog);
1059}
1060
1061
1062void
1063VBoxDbgStatsModel::logTree(QModelIndex &a_rRoot, bool a_fReleaseLog) const
1064{
1065 PDBGGUISTATSNODE pRoot = a_rRoot.isValid() ? nodeFromIndex(a_rRoot) : m_pRoot;
1066 if (pRoot)
1067 logNode(pRoot, a_fReleaseLog);
1068}
1069
1070
1071
1072
1073/*
1074 *
1075 * V B o x D b g S t a t s M o d e l V M
1076 * V B o x D b g S t a t s M o d e l V M
1077 * V B o x D b g S t a t s M o d e l V M
1078 *
1079 *
1080 */
1081
1082
1083VBoxDbgStatsModelVM::VBoxDbgStatsModelVM(PVM a_pVM, QString &a_rPatStr, QObject *a_pParent)
1084 : VBoxDbgStatsModel(a_pParent), VBoxDbgBase(a_pVM),
1085 m_iUpdateChild(UINT32_MAX), m_pUpdateParent(NULL), m_cchUpdateParent(0)
1086{
1087 /*
1088 * Create a model containing the STAM entries matching the pattern.
1089 * (The original idea was to get everything and rely on some hide/visible
1090 * flag that it turned out didn't exist.)
1091 */
1092 PDBGGUISTATSNODE pTree = createNewTree(a_rPatStr);
1093 setRootNode(pTree);
1094}
1095
1096
1097VBoxDbgStatsModelVM::~VBoxDbgStatsModelVM()
1098{
1099 /* nothing to do here. */
1100}
1101
1102
1103static void resetNode(PDBGGUISTATSNODE pNode)
1104{
1105 /** @todo */
1106}
1107
1108static void updateNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
1109 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
1110{
1111 /** @todo */
1112}
1113
1114static int32_t getNodePath(PCDBGGUISTATSNODE pNode, char *psz, ssize_t cch)
1115{
1116 int32_t off;
1117 if (!pNode->pParent)
1118 {
1119 /* root - don't add it's slash! */
1120 AssertReturn(cch >= 1, -1);
1121 off = 0;
1122 *psz = '\0';
1123 }
1124 else
1125 {
1126 cch -= pNode->cchName - 1;
1127 AssertReturn(cch > 0, -1);
1128 off = getNodePath(pNode->pParent, psz, cch);
1129 if (off >= 0)
1130 {
1131 psz[off++] = '/';
1132 memcpy(&psz[off], pNode->pszName, pNode->cchName + 1);
1133 off += pNode->cchName;
1134 }
1135 }
1136 return off;
1137}
1138
1139static bool isNodeAncestorOf(PCDBGGUISTATSNODE pNode, PCDBGGUISTATSNODE pAncestor)
1140{
1141 while (pNode)
1142 {
1143 pNode = pNode->pParent;
1144 if (pNode == pAncestor)
1145 return true;
1146 }
1147 return false;
1148}
1149
1150static PDBGGUISTATSNODE nextNode(PDBGGUISTATSNODE pNode)
1151{
1152 /** @todo */
1153 return pNode;
1154}
1155
1156static PDBGGUISTATSNODE prevNode(PDBGGUISTATSNODE pNode)
1157{
1158 /** @todo */
1159 return pNode;
1160}
1161
1162static void removeAndDeleteNode(PDBGGUISTATSNODE pNode)
1163{
1164 /** @todo */
1165
1166}
1167
1168
1169/*static*/ DECLCALLBACK(int)
1170VBoxDbgStatsModelVM::updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
1171 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
1172{
1173 VBoxDbgStatsModelVM *pThis = (VBoxDbgStatsModelVM *)pvUser;
1174 Log3(("updateCallback: %s\n", pszName));
1175
1176 /*
1177 * Skip the ones which shouldn't be visible in the GUI.
1178 */
1179 if (enmVisibility == STAMVISIBILITY_NOT_GUI)
1180 return 0;
1181
1182 /*
1183 * The default assumption is that nothing has changed.
1184 * For now we'll reset the model when ever something changes.
1185 */
1186 PDBGGUISTATSNODE pNode;
1187 if (pThis->m_iUpdateChild != UINT32_MAX)
1188 {
1189 pNode = pThis->m_pUpdateParent->papChildren[pThis->m_iUpdateChild];
1190 if ( !strncmp(pszName, pThis->m_szUpdateParent, pThis->m_cchUpdateParent)
1191 && !strcmp(pszName + pThis->m_cchUpdateParent, pNode->pszName))
1192 /* got it! */;
1193 else
1194 {
1195 /*
1196 * We might be inserting a new node between pPrev and pNode
1197 * or we might be removing one or more nodes. Either case is
1198 * handled in the same rough way.
1199 */
1200 pThis->m_fUpdateInsertRemove = true;
1201
1202 /* Start with the current parent node and look for a common ancestor
1203 hoping that this is faster than going from the root. */
1204 PDBGGUISTATSNODE const pPrev = prevNode(pNode);
1205 while (pNode != pThis->m_pRoot)
1206 {
1207 if (!strncmp(pszName, pThis->m_szUpdateParent, pThis->m_cchUpdateParent))
1208 break;
1209 Assert(pThis->m_cchUpdateParent > pNode->cchName);
1210 pThis->m_cchUpdateParent -= pNode->cchName + 1;
1211 pThis->m_szUpdateParent[pThis->m_cchUpdateParent] = '\0';
1212 pNode = pNode->pParent;
1213 }
1214
1215 /* Decent until we've found/created the node pszName indicates,
1216 modifying m_szUpdateParent as we go along. */
1217 while (pszName[pThis->m_cchUpdateParent - 1] == '/')
1218 {
1219 /* Find the end of this component. */
1220 const char *pszStart = &pszName[pThis->m_cchUpdateParent];
1221 const char *pszEnd = strchr(pszStart, '/');
1222 if (!pszEnd)
1223 pszEnd = strchr(pszStart, '/');
1224 char *pszSubName = &pThis->m_szUpdateParent[pThis->m_cchUpdateParent];
1225 size_t cchSubName = pszEnd - pszStart;
1226
1227 /* Add the name to the path. */
1228 memcpy(pszSubName, pszStart, cchSubName);
1229 pThis->m_cchUpdateParent += cchSubName;
1230 pThis->m_szUpdateParent[pThis->m_cchUpdateParent] = '\0';
1231
1232 if (pNode->cChildren)
1233 {
1234 /* first child */
1235 pNode = createAndInsertNode(pNode, pszSubName, UINT32_MAX);
1236 AssertReturn(pNode, VERR_NO_MEMORY);
1237 }
1238 else
1239 {
1240 /* binary search. */
1241 int32_t iStart = 0;
1242 int32_t iLast = pNode->cChildren - 1;
1243 for (;;)
1244 {
1245 int32_t i = iStart + (iLast + 1 - iStart) / 2;
1246 int iDiff = strcmp(pszSubName, pNode->papChildren[i]->pszName);
1247 if (iDiff > 0)
1248 {
1249 iStart = i + 1;
1250 if (iStart > iLast)
1251 {
1252 pNode = createAndInsertNode(pNode, pszSubName, iStart);
1253 AssertReturn(pNode, VERR_NO_MEMORY);
1254 break;
1255 }
1256 }
1257 else if (iDiff < 0)
1258 {
1259 iLast = i - 1;
1260 if (iLast < iStart)
1261 {
1262 pNode = createAndInsertNode(pNode, pszSubName, i);
1263 AssertReturn(pNode, VERR_NO_MEMORY);
1264 break;
1265 }
1266 }
1267 else
1268 {
1269 pNode = pNode->papChildren[i];
1270 break;
1271 }
1272 }
1273 }
1274
1275 pThis->m_szUpdateParent[pThis->m_cchUpdateParent++] = '/';
1276 pThis->m_szUpdateParent[pThis->m_cchUpdateParent] = '\0';
1277 Assert(pThis->m_cchUpdateParent < sizeof(pThis->m_szUpdateParent));
1278 }
1279 Assert(pszName[pThis->m_cchUpdateParent - 1] == '\0');
1280
1281 /* Remove all the nodes between pNode and pPrev but keep all
1282 of pNode's ancestors (or it'll get orphaned). */
1283 PDBGGUISTATSNODE pCur = prevNode(pNode);
1284 Assert(pCur != pPrev);
1285 while (pCur != pPrev)
1286 {
1287 PDBGGUISTATSNODE pAdv = prevNode(pCur);
1288 if (!isNodeAncestorOf(pCur, pNode))
1289 removeAndDeleteNode(pCur);
1290 pCur = pAdv;
1291 }
1292
1293 /* Removed the data from all ancestors of pNode that it doesn't share them pPrev. */
1294 pCur = pNode->pParent;
1295 while (!isNodeAncestorOf(pCur, pPrev))
1296 {
1297 resetNode(pNode);
1298 pCur = pCur->pParent;
1299 }
1300
1301 /* Finally, adjust the globals (szUpdateParent is one level too deep). */
1302 Assert(pThis->m_cchUpdateParent > pNode->cchName - 1);
1303 pThis->m_cchUpdateParent -= pNode->cchName - 1;
1304 pThis->m_szUpdateParent[pThis->m_cchUpdateParent] = '\0';
1305 pThis->m_pUpdateParent = pNode->pParent;
1306 pThis->m_iUpdateChild = pNode->iSelf;
1307 }
1308 }
1309 else
1310 {
1311 /*
1312 * Insert it at the end of the tree .
1313 * .
1314 * Do the same as we're doing down in createNewTreeCallback, walk from the .
1315 * root and create whatever we need.
1316 */
1317 pThis->m_fUpdateInsertRemove = true;
1318
1319 AssertReturn(*pszName == '/' && pszName[1] != '/', VERR_INTERNAL_ERROR);
1320 pNode = pThis->m_pRoot;
1321 const char *pszCur = pszName + 1;
1322 while (*pszCur)
1323 {
1324 /* Find the end of this component. */
1325 const char *pszNext = strchr(pszCur, '/');
1326 if (!pszNext)
1327 pszNext = strchr(pszCur, '\0');
1328 size_t cchCur = pszNext - pszCur;
1329
1330 /* Create it if it doesn't exist (it will be last if it exists). */
1331 if ( !pNode->cChildren
1332 || strncmp(pNode->papChildren[pNode->cChildren - 1]->pszName, pszCur, cchCur)
1333 || pNode->papChildren[pNode->cChildren - 1]->pszName[cchCur])
1334 {
1335 pNode = createAndInsertNode(pNode, pszCur, pszNext - pszCur, UINT32_MAX);
1336 if (!pNode)
1337 return VERR_NO_MEMORY;
1338 }
1339 else
1340 pNode = pNode->papChildren[pNode->cChildren - 1];
1341
1342 /* Advance */
1343 pszCur = *pszNext ? pszNext + 1 : pszNext;
1344 }
1345
1346 }
1347
1348 /*
1349 * Perform the update.
1350 */
1351 updateNode(pNode, enmType, pvSample, enmUnit, enmVisibility, pszDesc, pvUser);
1352
1353 /*
1354 * Advance to the next node.
1355 * (Again, we're ASSUMING that the input is sorted on (full) path name.)
1356 */
1357 PDBGGUISTATSNODE pParent = pNode->pParent;
1358 if (pNode->cChildren)
1359 {
1360 /* decend to the children. */
1361 pThis->m_iUpdateChild = 0;
1362 Assert(pThis->m_cchUpdateParent + pNode->cchName + 2 < sizeof(pThis->m_szUpdateParent));
1363 memcpy(&pThis->m_szUpdateParent[pThis->m_cchUpdateParent], pNode->pszName, pNode->cchName);
1364 pThis->m_cchUpdateParent += pNode->cchName;
1365 pThis->m_szUpdateParent[pThis->m_cchUpdateParent++] = '/';
1366 pThis->m_pUpdateParent = pNode;
1367 }
1368 else if (pNode->iSelf + 1 < pParent->cChildren)
1369 {
1370 /* next sibling */
1371 pThis->m_iUpdateChild = pNode->iSelf + 1;
1372 Assert(pThis->m_pUpdateParent == pNode->pParent);
1373 }
1374 else
1375 {
1376 /* move up and down- / on-wards */
1377 for (;;)
1378 {
1379 /* ascend */
1380 pNode = pParent;
1381 pParent = pParent->pParent;
1382 if (!pParent)
1383 {
1384 Assert(pNode == pThis->m_pRoot);
1385 pThis->m_iUpdateChild = UINT32_MAX;
1386 pThis->m_szUpdateParent[0] = '\0';
1387 pThis->m_cchUpdateParent = 0;
1388 pThis->m_pUpdateParent = NULL;
1389 break;
1390 }
1391 Assert(pThis->m_cchUpdateParent > pNode->cchName);
1392 pThis->m_cchUpdateParent -= pNode->cchName - 1;
1393 pThis->m_szUpdateParent[pThis->m_cchUpdateParent] = '\0';
1394
1395 /* try advance */
1396 if (pNode->iSelf + 1 < pParent->cChildren)
1397 {
1398 pNode = pParent->papChildren[pNode->iSelf + 1];
1399
1400 /* decend to a node containing data. */
1401 while ( pNode->enmType == STAMTYPE_INVALID
1402 && pNode->cChildren > 0)
1403 {
1404 Assert(pNode->enmState == kDbgGuiStatsNodeState_kVisible);
1405
1406 Assert(pThis->m_cchUpdateParent + pNode->cchName + 2 < sizeof(pThis->m_szUpdateParent));
1407 memcpy(&pThis->m_szUpdateParent[pThis->m_cchUpdateParent], pNode->pszName, pNode->cchName);
1408 pThis->m_cchUpdateParent += pNode->cchName;
1409 pThis->m_szUpdateParent[pThis->m_cchUpdateParent++] = '/';
1410
1411 pNode = pNode->papChildren[0];
1412 }
1413 Assert(pNode->enmType != STAMTYPE_INVALID);
1414 pThis->m_iUpdateChild = pNode->iSelf;
1415 pThis->m_pUpdateParent = pNode->pParent;
1416 break;
1417 }
1418 }
1419
1420 }
1421 return VINF_SUCCESS;
1422}
1423
1424
1425void
1426VBoxDbgStatsModelVM::update(const QString &a_rPatStr)
1427{
1428 /*
1429 * Find the first child with data and set it up as the 'next'
1430 * node to be updated.
1431 */
1432 PDBGGUISTATSNODE pFirst = m_pRoot;
1433 while (pFirst && pFirst->enmType == STAMTYPE_INVALID)
1434 pFirst = nextNode(pFirst);
1435 if (pFirst)
1436 {
1437 m_iUpdateChild = pFirst->iSelf;
1438 m_pUpdateParent = pFirst;
1439 m_cchUpdateParent = getNodePath(m_pUpdateParent, m_szUpdateParent, sizeof(m_szUpdateParent) - 1);
1440 AssertReturnVoid(m_cchUpdateParent >= 1);
1441 m_szUpdateParent[m_cchUpdateParent++] = '/';
1442 m_szUpdateParent[m_cchUpdateParent] = '\0';
1443 }
1444 else
1445 {
1446 m_iUpdateChild = UINT32_MAX;
1447 m_pUpdateParent = NULL;
1448 m_szUpdateParent[0] = '\0';
1449 m_cchUpdateParent = 0;
1450 }
1451
1452 /*
1453 * Perform the update.
1454 *
1455 * If we're inserting and/or removing anything we'll simply reset the model.
1456 * This can be optimized later when the normal updating is working perfectly.
1457 */
1458 m_fUpdateInsertRemove = false;
1459
1460 /** @todo the way we update this stuff is independent of the source (XML, file, STAM), our only
1461 * ASSUMPTION is that the input is strictly ordered by (full) name. So, all this stuff should
1462 * really move up into the parent class. */
1463 stamEnum(a_rPatStr, updateCallback, this);
1464
1465 if (m_fUpdateInsertRemove)
1466 reset();
1467 else
1468 {
1469 /*
1470 * Send dataChanged events.
1471 */
1472 DBGGUISTATSSTACK Stack;
1473 Stack.a[0].pNode = m_pRoot;
1474 Stack.a[0].iChild = -1;
1475 Stack.iTop = 0;
1476
1477 while (Stack.iTop >= 0)
1478 {
1479 /* get top element */
1480 PDBGGUISTATSNODE pNode = Stack.a[Stack.iTop].pNode;
1481 uint32_t iChild = ++Stack.a[Stack.iTop].iChild;
1482 if (iChild < pNode->cChildren)
1483 {
1484 /* push */
1485 Stack.iTop++;
1486 Assert(Stack.iTop < (int32_t)RT_ELEMENTS(Stack.a));
1487 Stack.a[Stack.iTop].pNode = pNode->papChildren[iChild];
1488 Stack.a[Stack.iTop].iChild = 0;
1489 }
1490 else
1491 {
1492 /* pop */
1493 Stack.iTop--;
1494
1495 /* do the actual work. */
1496 iChild = 0;
1497 while (iChild < pNode->cChildren)
1498 {
1499 /* skip to the first needing updating. */
1500 while ( iChild < pNode->cChildren
1501 && ( pNode->papChildren[iChild]->enmState != kDbgGuiStatsNodeState_kMakeVisible
1502 && pNode->papChildren[iChild]->enmState != kDbgGuiStatsNodeState_kMakeInvisible
1503 && pNode->papChildren[iChild]->enmState != kDbgGuiStatsNodeState_kRefresh))
1504 iChild++;
1505 if (iChild >= pNode->cChildren)
1506 break;
1507 QModelIndex TopLeft = createIndex(iChild, 0, pNode->papChildren[iChild]);
1508
1509 /* collect any subsequent nodes that also needs refreshing. */
1510 while (++iChild < pNode->cChildren)
1511 {
1512 DBGGUISTATENODESTATE enmState = pNode->papChildren[iChild]->enmState;
1513 if ( enmState == kDbgGuiStatsNodeState_kRefresh
1514 || enmState == kDbgGuiStatsNodeState_kMakeVisible)
1515 enmState = kDbgGuiStatsNodeState_kVisible;
1516 else if (enmState == kDbgGuiStatsNodeState_kMakeInvisible)
1517 enmState = kDbgGuiStatsNodeState_kInvisible;
1518 else
1519 break;
1520 pNode->papChildren[iChild]->enmState = enmState;
1521 }
1522 QModelIndex BottomRight = createIndex(iChild - 1, DBGGUI_STATS_COLUMNS - 1, pNode->papChildren[iChild - 1]);
1523
1524 /* emit the refresh signal */
1525 emit dataChanged(TopLeft, BottomRight);
1526 }
1527 }
1528 }
1529 }
1530}
1531
1532
1533/*static*/ int
1534VBoxDbgStatsModelVM::initNode(PDBGGUISTATSNODE pNode, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, STAMVISIBILITY enmVisibility, const char *pszDesc)
1535{
1536 /*
1537 * Copy the data.
1538 */
1539 bool fVisible = true;
1540 pNode->enmUnit = enmUnit;
1541 Assert(pNode->enmType == STAMTYPE_INVALID);
1542 pNode->enmType = enmType;
1543 if (pszDesc)
1544 pNode->pDescStr = new QString(pszDesc); /* ignore allocation failure (well, at least up to the point we can ignore it) */
1545
1546 switch (enmType)
1547 {
1548 case STAMTYPE_COUNTER:
1549 pNode->Data.Counter = *(PSTAMCOUNTER)pvSample;
1550 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1551 && (enmVisibility == STAMVISIBILITY_ALWAYS || pNode->Data.Counter.c);
1552 break;
1553
1554 case STAMTYPE_PROFILE:
1555 case STAMTYPE_PROFILE_ADV:
1556 pNode->Data.Profile = *(PSTAMPROFILE)pvSample;
1557 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1558 && (enmVisibility == STAMVISIBILITY_ALWAYS || pNode->Data.Profile.cPeriods);
1559 break;
1560
1561 case STAMTYPE_RATIO_U32:
1562 case STAMTYPE_RATIO_U32_RESET:
1563 pNode->Data.RatioU32 = *(PSTAMRATIOU32)pvSample;
1564 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1565 && (enmVisibility == STAMVISIBILITY_ALWAYS || pNode->Data.RatioU32.u32A || pNode->Data.RatioU32.u32B);
1566 break;
1567
1568 case STAMTYPE_CALLBACK:
1569 {
1570 const char *pszString = (const char *)pvSample;
1571 size_t cchString = strlen(pszString);
1572 pNode->Data.psz = (char *)RTMemAlloc(256);
1573 if (pNode->Data.psz)
1574 return VERR_NO_MEMORY;
1575 memcpy(pNode->Data.psz, pszString, RT_MIN(256, cchString + 1));
1576
1577 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1578 && (enmVisibility == STAMVISIBILITY_ALWAYS || cchString);
1579 break;
1580 }
1581
1582 case STAMTYPE_U8:
1583 case STAMTYPE_U8_RESET:
1584 case STAMTYPE_X8:
1585 case STAMTYPE_X8_RESET:
1586 pNode->Data.u8 = *(uint8_t *)pvSample;
1587 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1588 && (enmVisibility == STAMVISIBILITY_ALWAYS || pNode->Data.u8);
1589 break;
1590
1591 case STAMTYPE_U16:
1592 case STAMTYPE_U16_RESET:
1593 case STAMTYPE_X16:
1594 case STAMTYPE_X16_RESET:
1595 pNode->Data.u16 = *(uint16_t *)pvSample;
1596 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1597 && (enmVisibility == STAMVISIBILITY_ALWAYS || pNode->Data.u16);
1598 break;
1599
1600 case STAMTYPE_U32:
1601 case STAMTYPE_U32_RESET:
1602 case STAMTYPE_X32:
1603 case STAMTYPE_X32_RESET:
1604 pNode->Data.u32 = *(uint32_t *)pvSample;
1605 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1606 && (enmVisibility == STAMVISIBILITY_ALWAYS || pNode->Data.u32);
1607 break;
1608
1609 case STAMTYPE_U64:
1610 case STAMTYPE_U64_RESET:
1611 case STAMTYPE_X64:
1612 case STAMTYPE_X64_RESET:
1613 pNode->Data.u64 = *(uint64_t *)pvSample;
1614 fVisible = enmVisibility != STAMVISIBILITY_NOT_GUI
1615 && (enmVisibility == STAMVISIBILITY_ALWAYS || pNode->Data.u64);
1616 break;
1617
1618 default:
1619 AssertMsgFailed(("%d\n", enmType));
1620 break;
1621 }
1622
1623 /*
1624 * Set the state according to the visibility.
1625 */
1626 pNode->enmState = fVisible
1627 ? kDbgGuiStatsNodeState_kVisible
1628 : kDbgGuiStatsNodeState_kInvisible;
1629 return VINF_SUCCESS;
1630}
1631
1632
1633/*static*/ DECLCALLBACK(int)
1634VBoxDbgStatsModelVM::createNewTreeCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
1635 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
1636{
1637 PDBGGUISTATSNODE pRoot = (PDBGGUISTATSNODE)pvUser;
1638 Log3(("createNewTreeCallback: %s\n", pszName));
1639
1640 /*
1641 * Skip the ones which shouldn't be visible in the GUI.
1642 */
1643 if (enmVisibility == STAMVISIBILITY_NOT_GUI)
1644 return 0;
1645
1646 /*
1647 * Perform a mkdir -p like operation till we've walked / created the entire path down
1648 * to the node specfied node. Remember the last node as that will be the one we will
1649 * stuff the data into.
1650 */
1651 AssertReturn(*pszName == '/' && pszName[1] != '/', VERR_INTERNAL_ERROR);
1652 PDBGGUISTATSNODE pNode = pRoot;
1653 const char *pszCur = pszName + 1;
1654 while (*pszCur)
1655 {
1656 /* find the end of this component. */
1657 const char *pszNext = strchr(pszCur, '/');
1658 if (!pszNext)
1659 pszNext = strchr(pszCur, '\0');
1660 size_t cchCur = pszNext - pszCur;
1661
1662 /* Create it if it doesn't exist (it will be last if it exists). */
1663 if ( !pNode->cChildren
1664 || strncmp(pNode->papChildren[pNode->cChildren - 1]->pszName, pszCur, cchCur)
1665 || pNode->papChildren[pNode->cChildren - 1]->pszName[cchCur])
1666 {
1667 pNode = createAndInsertNode(pNode, pszCur, pszNext - pszCur, UINT32_MAX);
1668 if (!pNode)
1669 return VERR_NO_MEMORY;
1670 }
1671 else
1672 pNode = pNode->papChildren[pNode->cChildren - 1];
1673
1674 /* Advance */
1675 pszCur = *pszNext ? pszNext + 1 : pszNext;
1676 }
1677
1678 /*
1679 * Save the data.
1680 */
1681 return initNode(pNode, enmType, pvSample, enmUnit, enmVisibility, pszDesc);
1682}
1683
1684
1685PDBGGUISTATSNODE
1686VBoxDbgStatsModelVM::createNewTree(QString &a_rPatStr)
1687{
1688 PDBGGUISTATSNODE pRoot = createRootNode();
1689 if (pRoot)
1690 {
1691 int rc = stamEnum(a_rPatStr, createNewTreeCallback, pRoot);
1692 if (VBOX_SUCCESS(rc))
1693 return pRoot;
1694
1695 /* failed, cleanup. */
1696 destroyTree(pRoot);
1697 }
1698
1699 return NULL;
1700}
1701
1702
1703
1704
1705#if 0 /* save for later */
1706
1707void VBoxDbgStatsLeafItem::update(STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, STAMVISIBILITY enmVisibility, const char *pszDesc)
1708{
1709 /*
1710 * Detect changes.
1711 * This path will be taken on the first update and if a item
1712 * is reregistred with a different unit/type (unlikely).
1713 */
1714 if ( enmType != m_enmType
1715 || enmUnit != m_enmUnit)
1716 {
1717 m_enmType = enmType;
1718 m_enmUnit = enmUnit;
1719
1720 /*
1721 * Unit.
1722 */
1723 setText(1, STAMR3GetUnit(enmUnit));
1724
1725 /**
1726 * Update the description.
1727 * Insert two spaces as gap after the last left-aligned field.
1728 * @todo dmik: How to make this better?
1729 */
1730 m_DescStr = QString(" ") + QString(pszDesc);
1731
1732 /*
1733 * Clear the content.
1734 */
1735 setText(2, "");
1736 setText(3, "");
1737 setText(4, "");
1738 setText(5, "");
1739 setText(6, "");
1740 setText(8, m_DescStr);
1741 }
1742
1743 /*
1744 * Update the data.
1745 */
1746 char sz[64];
1747 switch (enmType)
1748 {
1749 case STAMTYPE_COUNTER:
1750 {
1751 const uint64_t cPrev = m_Data.Counter.c;
1752 m_Data.Counter = *(PSTAMCOUNTER)pvSample;
1753 setText(2, formatNumber(sz, m_Data.Counter.c));
1754 setText(7, formatNumberSigned(sz, m_Data.Counter.c - cPrev));
1755 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1756 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.Counter.c));
1757 break;
1758 }
1759
1760 case STAMTYPE_PROFILE:
1761 case STAMTYPE_PROFILE_ADV:
1762 {
1763 const uint64_t cPeriodsPrev = m_Data.Profile.cPeriods;
1764 m_Data.Profile = *(PSTAMPROFILE)pvSample;
1765 if (m_Data.Profile.cPeriods)
1766 {
1767 setText(2, formatNumber(sz, m_Data.Profile.cPeriods));
1768 setText(3, formatNumber(sz, m_Data.Profile.cTicksMin));
1769 setText(4, formatNumber(sz, m_Data.Profile.cTicks / m_Data.Profile.cPeriods));
1770 setText(5, formatNumber(sz, m_Data.Profile.cTicksMax));
1771 setText(6, formatNumber(sz, m_Data.Profile.cTicks));
1772 setText(7, formatNumberSigned(sz, m_Data.Profile.cPeriods - cPeriodsPrev));
1773 setVisible(enmVisibility != STAMVISIBILITY_NOT_GUI);
1774 }
1775 else
1776 {
1777 setText(2, "0");
1778 setText(3, "0");
1779 setText(4, "0");
1780 setText(5, "0");
1781 setText(6, "0");
1782 setText(7, "0");
1783 setVisible(enmVisibility != STAMVISIBILITY_NOT_GUI && enmVisibility == STAMVISIBILITY_ALWAYS);
1784 }
1785 break;
1786 }
1787
1788 case STAMTYPE_RATIO_U32:
1789 case STAMTYPE_RATIO_U32_RESET:
1790 {
1791 const STAMRATIOU32 RatioU32 = m_Data.RatioU32;
1792 m_Data.RatioU32 = *(PSTAMRATIOU32)pvSample;
1793
1794 char sz2[64];
1795 char sz3[128];
1796 strcat(strcat(strcpy(sz3, formatNumber(sz, m_Data.RatioU32.u32A)), " : "), formatNumber(sz2, m_Data.RatioU32.u32B));
1797 setText(2, sz3);
1798 ///@todo ratio: setText(7, formatNumberSigned(sz, m_Data.u64 - u64Prev));
1799 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1800 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.RatioU32.u32A || m_Data.RatioU32.u32B));
1801 break;
1802 }
1803
1804 case STAMTYPE_CALLBACK:
1805 {
1806 const char *pszString = (const char *)pvSample;
1807 setText(2, pszString);
1808 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1809 && (enmVisibility == STAMVISIBILITY_ALWAYS || *pszString));
1810 break;
1811 }
1812
1813 case STAMTYPE_U8:
1814 case STAMTYPE_U8_RESET:
1815 {
1816 const uint8_t u8Prev = m_Data.u8;
1817 m_Data.u8 = *(uint8_t *)pvSample;
1818 setText(2, formatNumber(sz, m_Data.u8));
1819 setText(7, formatNumberSigned(sz, (int32_t)m_Data.u8 - u8Prev));
1820 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1821 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u8));
1822 break;
1823 }
1824
1825 case STAMTYPE_X8:
1826 case STAMTYPE_X8_RESET:
1827 {
1828 const uint8_t u8Prev = m_Data.u8;
1829 m_Data.u8 = *(uint8_t *)pvSample;
1830 setText(2, formatHexNumber(sz, m_Data.u8, 2));
1831 setText(7, formatHexNumber(sz, m_Data.u8 - u8Prev, 1));
1832 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1833 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u8));
1834 break;
1835 }
1836
1837 case STAMTYPE_U16:
1838 case STAMTYPE_U16_RESET:
1839 {
1840 const uint16_t u16Prev = m_Data.u16;
1841 m_Data.u16 = *(uint16_t *)pvSample;
1842 setText(2, formatNumber(sz, m_Data.u16));
1843 setText(7, formatNumberSigned(sz, (int32_t)m_Data.u16 - u16Prev));
1844 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1845 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u16));
1846 break;
1847 }
1848
1849 case STAMTYPE_X16:
1850 case STAMTYPE_X16_RESET:
1851 {
1852 const uint16_t u16Prev = m_Data.u16;
1853 m_Data.u16 = *(uint16_t *)pvSample;
1854 setText(2, formatHexNumber(sz, m_Data.u16, 4));
1855 setText(7, formatHexNumber(sz, m_Data.u16 - u16Prev, 1));
1856 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1857 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u16));
1858 break;
1859 }
1860
1861 case STAMTYPE_U32:
1862 case STAMTYPE_U32_RESET:
1863 {
1864 const uint32_t u32Prev = m_Data.u32;
1865 m_Data.u32 = *(uint32_t *)pvSample;
1866 setText(2, formatNumber(sz, m_Data.u32));
1867 setText(7, formatNumberSigned(sz, (int64_t)m_Data.u32 - u32Prev));
1868 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1869 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u32));
1870 break;
1871 }
1872
1873 case STAMTYPE_X32:
1874 case STAMTYPE_X32_RESET:
1875 {
1876 const uint32_t u32Prev = m_Data.u32;
1877 m_Data.u32 = *(uint32_t *)pvSample;
1878 setText(2, formatHexNumber(sz, m_Data.u32, 8));
1879 setText(7, formatHexNumber(sz, m_Data.u32 - u32Prev, 1));
1880 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1881 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u32));
1882 break;
1883 }
1884
1885 case STAMTYPE_U64:
1886 case STAMTYPE_U64_RESET:
1887 {
1888 const uint64_t u64Prev = m_Data.u64;
1889 m_Data.u64 = *(uint64_t *)pvSample;
1890 setText(2, formatNumber(sz, m_Data.u64));
1891 setText(7, formatNumberSigned(sz, m_Data.u64 - u64Prev));
1892 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1893 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u64));
1894 break;
1895 }
1896
1897 case STAMTYPE_X64:
1898 case STAMTYPE_X64_RESET:
1899 {
1900 const uint64_t u64Prev = m_Data.u64;
1901 m_Data.u64 = *(uint64_t *)pvSample;
1902 setText(2, formatHexNumber(sz, m_Data.u64, 16));
1903 setText(7, formatHexNumber(sz, m_Data.u64 - u64Prev, 1));
1904 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
1905 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u64));
1906 break;
1907 }
1908
1909 default:
1910 break;
1911 }
1912}
1913
1914
1915QString VBoxDbgStatsLeafItem::key(int iColumn, bool /*fAscending*/) const
1916{
1917 /* name and description */
1918 if (iColumn <= 1 || iColumn >= 8)
1919 return text(iColumn);
1920
1921 /* the number columns */
1922 char sz[128];
1923 switch (m_enmType)
1924 {
1925 case STAMTYPE_COUNTER:
1926 switch (iColumn)
1927 {
1928 case 2: formatSortKey(sz, m_Data.Counter.c); break;
1929 case 7: return text(iColumn);
1930 default: sz[0] = '\0'; break;
1931 }
1932 break;
1933
1934 case STAMTYPE_PROFILE:
1935 case STAMTYPE_PROFILE_ADV:
1936 if (m_Data.Profile.cPeriods)
1937 {
1938 switch (iColumn)
1939 {
1940 case 2: formatSortKey(sz, m_Data.Profile.cPeriods); break;
1941 case 3: formatSortKey(sz, m_Data.Profile.cTicksMin); break;
1942 case 4: formatSortKey(sz, m_Data.Profile.cTicks / m_Data.Profile.cPeriods); break;
1943 case 5: formatSortKey(sz, m_Data.Profile.cTicksMax); break;
1944 case 6: formatSortKey(sz, m_Data.Profile.cTicks); break;
1945 case 7: return text(iColumn);
1946 default: sz[0] = '\0'; break;
1947 }
1948 }
1949 else
1950 sz[0] = '\0';
1951 break;
1952
1953 case STAMTYPE_RATIO_U32:
1954 case STAMTYPE_RATIO_U32_RESET:
1955 if (m_Data.RatioU32.u32B)
1956 formatSortKey(sz, (m_Data.RatioU32.u32A * (uint64_t)1000) / m_Data.RatioU32.u32B);
1957 else if (m_Data.RatioU32.u32A)
1958 formatSortKey(sz, m_Data.RatioU32.u32A * (uint64_t)1000);
1959 else
1960 formatSortKey(sz, 1000);
1961 break;
1962
1963 case STAMTYPE_U8:
1964 case STAMTYPE_U8_RESET:
1965 switch (iColumn)
1966 {
1967 case 2: formatSortKey(sz, m_Data.u8); break;
1968 case 7: return text(iColumn);
1969 default: sz[0] = '\0'; break;
1970 }
1971 break;
1972
1973 case STAMTYPE_U16:
1974 case STAMTYPE_U16_RESET:
1975 switch (iColumn)
1976 {
1977 case 2: formatSortKey(sz, m_Data.u16); break;
1978 case 7: return text(iColumn);
1979 default: sz[0] = '\0'; break;
1980 }
1981 break;
1982
1983 case STAMTYPE_U32:
1984 case STAMTYPE_U32_RESET:
1985 switch (iColumn)
1986 {
1987 case 2: formatSortKey(sz, m_Data.u32); break;
1988 case 7: return text(iColumn);
1989 default: sz[0] = '\0'; break;
1990 }
1991 break;
1992
1993 case STAMTYPE_U64:
1994 case STAMTYPE_U64_RESET:
1995 switch (iColumn)
1996 {
1997 case 2: formatSortKey(sz, m_Data.u64); break;
1998 case 7: return text(iColumn);
1999 default: sz[0] = '\0'; break;
2000 }
2001 break;
2002
2003 case STAMTYPE_CALLBACK:
2004 default:
2005 return text(iColumn);
2006 }
2007
2008 return QString(sz);
2009}
2010
2011#endif /* saved for later reuse */
2012
2013
2014
2015
2016
2017/*
2018 *
2019 * V B o x D b g S t a t s V i e w
2020 * V B o x D b g S t a t s V i e w
2021 * V B o x D b g S t a t s V i e w
2022 *
2023 *
2024 */
2025
2026
2027VBoxDbgStatsView::VBoxDbgStatsView(PVM a_pVM, VBoxDbgStatsModel *a_pModel, VBoxDbgStats *a_pParent/* = NULL*/)
2028 : QTreeView(a_pParent), VBoxDbgBase(a_pVM), m_pModel(a_pModel), m_pParent(a_pParent),
2029 m_pLeafMenu(NULL), m_pBranchMenu(NULL), m_pViewMenu(NULL), m_pContextNode(NULL)
2030
2031{
2032 /*
2033 * Set the model and view defaults.
2034 */
2035 setModel(m_pModel);
2036 setRootIndex(m_pModel->getRootIndex());
2037 //setRootIsDecorated(true);
2038 setItemsExpandable(true);
2039 setSortingEnabled(true);
2040
2041 /*
2042 * We've got three menus to populate and link up.
2043 */
2044#ifdef VBOXDBG_USE_QT4
2045 /** @todo */
2046
2047#else /* QT3 */
2048 m_pLeafMenu = new QPopupMenu(this);
2049 m_pLeafMenu->insertItem("Rese&t", eReset);
2050 m_pLeafMenu->insertItem("&Refresh", eRefresh);
2051 m_pLeafMenu->insertItem("&Copy", eCopy);
2052 m_pLeafMenu->insertItem("To &Log", eLog);
2053 m_pLeafMenu->insertItem("T&o Release Log", eLogRel);
2054 connect(m_pLeafMenu, SIGNAL(activated(int)), this, SLOT(leafMenuActivated(int)));
2055
2056 m_pBranchMenu = new QPopupMenu(this);
2057 m_pBranchMenu->insertItem("&Expand Tree", eExpand);
2058 m_pBranchMenu->insertItem("&Collaps Tree", eCollaps);
2059 m_pBranchMenu->insertItem("&Refresh", eRefresh);
2060 m_pBranchMenu->insertItem("Rese&t", eReset);
2061 m_pBranchMenu->insertItem("&Copy", eCopy);
2062 m_pBranchMenu->insertItem("To &Log", eLog);
2063 m_pBranchMenu->insertItem("T&o Release Log", eLogRel);
2064 connect(m_pBranchMenu, SIGNAL(activated(int)), this, SLOT(branchMenuActivated(int)));
2065
2066 m_pViewMenu = new QPopupMenu(this);
2067 m_pViewMenu->insertItem("&Expand All", eExpand);
2068 m_pViewMenu->insertItem("&Collaps All", eCollaps);
2069 m_pViewMenu->insertItem("&Refresh", eRefresh);
2070 m_pViewMenu->insertItem("Rese&t", eReset);
2071 m_pViewMenu->insertItem("&Copy", eCopy);
2072 m_pViewMenu->insertItem("To &Log", eLog);
2073 m_pViewMenu->insertItem("T&o Release Log", eLogRel);
2074 connect(m_pViewMenu, SIGNAL(activated(int)), this, SLOT(viewMenuActivated(int)));
2075
2076 connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), this,
2077 SLOT(contextMenuReq(QListViewItem *, const QPoint &, int)));
2078#endif /* QT3 */
2079}
2080
2081
2082VBoxDbgStatsView::~VBoxDbgStatsView()
2083{
2084#if 0 /// @todo check who has to delete the model...
2085 setModel(NULL);
2086 delete m_pModel;
2087#endif
2088 m_pModel = NULL;
2089}
2090
2091
2092#if 0
2093/**
2094 * Hides all parent branches which doesn't have any visible leafs.
2095 */
2096static void hideParentBranches(VBoxDbgStatsLeafItem *pItem)
2097{
2098#ifdef VBOXDBG_USE_QT4
2099 /// @todo
2100 NOREF(pItem);
2101#else
2102 for (VBoxDbgStatsItem *pParent = pItem->getParent(); pParent; pParent = pParent->getParent())
2103 {
2104 QListViewItem *pChild = pParent->firstChild();
2105 while (pChild && !pChild->isVisible())
2106 pChild = pChild->nextSibling();
2107 if (pChild)
2108 return;
2109 pParent->setVisible(false);
2110 }
2111#endif
2112}
2113
2114/**
2115 * Shows all parent branches
2116 */
2117static void showParentBranches(VBoxDbgStatsLeafItem *pItem)
2118{
2119 for (VBoxDbgStatsItem *pParent = pItem->getParent(); pParent; pParent = pParent->getParent())
2120 pParent->setVisible(true);
2121}
2122#endif
2123
2124
2125void VBoxDbgStatsView::update(const QString &rPatStr)
2126{
2127 m_pModel->update(rPatStr);
2128
2129#if 0 /* later */
2130 m_pCur = m_pHead;
2131 m_PatStr = rPatStr;
2132 int rc = stamEnum(m_PatStr, updateCallback, this);
2133 if (VBOX_SUCCESS(rc))
2134 {
2135 /* hide what's left */
2136 for (VBoxDbgStatsLeafItem *pCur = m_pCur; pCur; pCur = pCur->m_pNext)
2137 if (pCur->isVisible())
2138 {
2139 pCur->setVisible(false);
2140 hideParentBranches(pCur);
2141 }
2142 }
2143 m_pCur = NULL;
2144#endif
2145 NOREF(rPatStr);
2146}
2147
2148void VBoxDbgStatsView::reset(const QString &rPatStr)
2149{
2150 stamReset(rPatStr);
2151}
2152
2153
2154#if 0 /* later */
2155
2156static void setOpenTree(QListViewItem *pItem, bool f)
2157{
2158#ifdef VBOXDBG_USE_QT4
2159 pItem->setExpanded(f);
2160 int cChildren = pItem->childCount();
2161 for (int i = 0; i < cChildren; i++)
2162 pItem->child(i)->setExpanded(f);
2163#else
2164 pItem->setOpen(f);
2165 for (pItem = pItem->firstChild(); pItem; pItem = pItem->nextSibling())
2166 setOpenTree(pItem, f);
2167#endif
2168}
2169
2170#ifndef VBOXDBG_USE_QT4
2171void VBoxDbgStatsView::expandAll()
2172{
2173 setOpenTree(m_pRoot, true);
2174}
2175
2176void VBoxDbgStatsView::collapsAll()
2177{
2178 setOpenTree(m_pRoot, false);
2179}
2180#endif /* QT3 */
2181
2182
2183/*static*/ DECLCALLBACK(int) VBoxDbgStatsView::updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
2184 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
2185{
2186 Log3(("updateCallback: %s\n", pszName));
2187 VBoxDbgStatsView *pThis = (VBoxDbgStatsView *)pvUser;
2188
2189 /*
2190 * Skip the ones which shouldn't be visible in the GUI.
2191 */
2192 if (enmVisibility == STAMVISIBILITY_NOT_GUI)
2193 return 0;
2194
2195 /*
2196 * Advance to the matching item.
2197 */
2198 VBoxDbgStatsLeafItem *pCur = pThis->m_pCur;
2199 while (pCur)
2200 {
2201 /*
2202 * ASSUMES ascending order of STAM items.
2203 */
2204 int iDiff = strcmp(pszName, pCur->getName());
2205 if (!iDiff)
2206 break;
2207 if (iDiff > 0)
2208 {
2209 /*
2210 * Removed / filtered out.
2211 */
2212 Log2(("updateCallback: %s - filtered out\n", pCur->getName()));
2213 if (pCur->isVisible())
2214 {
2215 pCur->setVisible(false);
2216 hideParentBranches(pCur);
2217 }
2218
2219 pCur = pCur->m_pNext;
2220 }
2221 else if (iDiff < 0)
2222 {
2223 /*
2224 * New item, insert before pCur.
2225 */
2226 Log2(("updateCallback: %s - new\n", pszName));
2227 VBoxDbgStatsLeafItem *pNew = new VBoxDbgStatsLeafItem(pszName, pThis->createPath(pszName));
2228 pNew->m_pNext = pCur;
2229 pNew->m_pPrev = pCur->m_pPrev;
2230 if (pNew->m_pPrev)
2231 pNew->m_pPrev->m_pNext = pNew;
2232 else
2233 pThis->m_pHead = pNew;
2234 pCur->m_pPrev = pNew;
2235 pCur = pNew;
2236 Assert(!strcmp(pszName, pCur->getName()));
2237 break;
2238 }
2239 }
2240
2241 /*
2242 * End of items, insert it at the tail.
2243 */
2244 if (!pCur)
2245 {
2246 Log2(("updateCallback: %s - new end\n", pszName));
2247 pCur = new VBoxDbgStatsLeafItem(pszName, pThis->createPath(pszName));
2248 pCur->m_pNext = NULL;
2249 pCur->m_pPrev = pThis->m_pTail;
2250 if (pCur->m_pPrev)
2251 pCur->m_pPrev->m_pNext = pCur;
2252 else
2253 pThis->m_pHead = pCur;
2254 pThis->m_pTail = pCur;
2255 }
2256 Assert(pThis->m_pHead);
2257 Assert(pThis->m_pTail);
2258
2259 /*
2260 * Update it and move on.
2261 */
2262 if (!pCur->isVisible())
2263 showParentBranches(pCur);
2264 pCur->update(enmType, pvSample, enmUnit, enmVisibility, pszDesc);
2265 pThis->m_pCur = pCur->m_pNext;
2266
2267 return 0;
2268}
2269
2270VBoxDbgStatsItem *VBoxDbgStatsView::createPath(const char *pszName)
2271{
2272 const char * const pszFullName = pszName;
2273
2274 /*
2275 * Start at root.
2276 */
2277 while (*pszName == '/')
2278 pszName++;
2279 VBoxDbgStatsItem *pParent = m_pRoot;
2280
2281 /*
2282 * Walk down the path creating what's missing.
2283 */
2284 for (;;)
2285 {
2286 /*
2287 * Extract the path component.
2288 */
2289 const char *pszEnd = strchr(pszName, '/');
2290 if (!pszEnd)
2291 return pParent;
2292 QString NameStr = QString::fromUtf8(pszName, pszEnd - pszName);
2293 /* advance */
2294 pszName = pszEnd + 1;
2295
2296 /*
2297 * Try find the name among the children of that parent guy.
2298 */
2299#ifdef VBOXDBG_USE_QT4
2300 QListViewItem *pChild = NULL;
2301 int cChildren = pParent->childCount();
2302 for (int i = 0; i < cChildren; i++)
2303 {
2304 pChild = pParent->child(i);
2305 if (pChild->text(0) == NameStr)
2306 break;
2307 }
2308#else
2309 QListViewItem *pChild = pParent->firstChild();
2310 while (pChild && pChild->text(0) != NameStr)
2311 pChild = pChild->nextSibling();
2312#endif
2313
2314 if (pChild)
2315 pParent = (VBoxDbgStatsItem *)pChild;
2316 else
2317 {
2318 Log3(("createPath: %.*s\n", pszEnd - pszFullName, pszFullName));
2319 NameStr = QString::fromUtf8(pszFullName, pszEnd - pszFullName);
2320#ifdef VBOXDBG_USE_QT4
2321 QByteArray NameArray = NameStr.toUtf8();
2322 pParent = new VBoxDbgStatsItem(NameArray.constData(), pParent);
2323#else
2324 pParent = new VBoxDbgStatsItem(NameStr, pParent);
2325#endif
2326 }
2327 pParent->setVisible(true);
2328 }
2329}
2330
2331void VBoxDbgStatsView::contextMenuReq(QListViewItem *pItem, const QPoint &rPoint, int /*iColumn*/)
2332{
2333 if (pItem)
2334 {
2335 m_pContextMenuItem = (VBoxDbgStatsItem *)pItem;
2336 if (m_pContextMenuItem->isLeaf())
2337 {
2338#ifdef VBOXDBG_USE_QT4
2339#else
2340 m_pLeafMenu->setItemEnabled(eReset, isVMOk());
2341 m_pLeafMenu->setItemEnabled(eRefresh, isVMOk());
2342#endif
2343 m_pLeafMenu->popup(rPoint);
2344 }
2345 else
2346 {
2347#ifdef VBOXDBG_USE_QT4
2348#else
2349 m_pBranchMenu->setItemEnabled(eReset, isVMOk());
2350 m_pBranchMenu->setItemEnabled(eRefresh, isVMOk());
2351#endif
2352 m_pBranchMenu->popup(rPoint);
2353 }
2354 }
2355 else
2356 {
2357 m_pContextMenuItem = NULL;
2358#ifdef VBOXDBG_USE_QT4
2359#else
2360 m_pViewMenu->setItemEnabled(eReset, isVMOk());
2361 m_pViewMenu->setItemEnabled(eRefresh, isVMOk());
2362#endif
2363 m_pViewMenu->popup(rPoint);
2364 }
2365}
2366
2367void VBoxDbgStatsView::leafMenuActivated(int iId)
2368{
2369 VBoxDbgStatsLeafItem *pItem = (VBoxDbgStatsLeafItem *)m_pContextMenuItem;
2370 AssertReturn(pItem, (void)0);
2371
2372 switch ((MenuId)iId)
2373 {
2374 case eReset:
2375 stamReset(m_pContextMenuItem->getName());
2376 /* fall thru */
2377
2378 case eRefresh:
2379 m_pCur = pItem;
2380 stamEnum(m_pContextMenuItem->getName(), updateCallback, this);
2381 break;
2382
2383 case eCopy:
2384 m_pContextMenuItem->copyTreeToClipboard();
2385 break;
2386
2387 case eLog:
2388 m_pContextMenuItem->logTree(false /* !release log */);
2389 break;
2390
2391 case eLogRel:
2392 m_pContextMenuItem->logTree(true /* release log */);
2393 break;
2394
2395 default: /* keep gcc quite */
2396 break;
2397 }
2398 m_pContextMenuItem = NULL;
2399}
2400
2401void VBoxDbgStatsView::branchMenuActivated(int iId)
2402{
2403 AssertReturn(m_pContextMenuItem, (void)0);
2404
2405 /** @todo make enum for iId */
2406 switch ((MenuId)iId)
2407 {
2408 case eExpand:
2409 setOpenTree(m_pContextMenuItem, true);
2410 break;
2411
2412 case eCollaps:
2413 setOpenTree(m_pContextMenuItem, false);
2414 break;
2415
2416 case eReset:
2417 {
2418 QString Str = QString::fromUtf8(m_pContextMenuItem->getName());
2419 Str.append((Str != "/") ? "/*" : "*");
2420 stamReset(Str);
2421 }
2422 /* fall thru */
2423
2424 case eRefresh:
2425 {
2426 const char *psz = m_pContextMenuItem->getName();
2427 QString Str = QString::fromUtf8(psz);
2428 if (strcmp(psz, "/"))
2429 {
2430 int cch = strlen(psz);
2431 m_pCur = m_pHead;
2432 while ( m_pCur
2433 && ( strncmp(psz, m_pCur->getName(), cch)
2434 || m_pCur->getName()[cch] != '/'))
2435 {
2436 m_pCur = m_pCur->m_pNext;
2437 }
2438 if (!m_pCur)
2439 return;
2440 Str.append("/*");
2441 }
2442 else
2443 {
2444 m_pCur = m_pHead;
2445 Str.append("*");
2446 }
2447 stamEnum(Str, updateCallback, this);
2448 m_pCur = NULL;
2449 break;
2450 }
2451
2452 case eCopy:
2453 m_pContextMenuItem->copyTreeToClipboard();
2454 break;
2455
2456 case eLog:
2457 m_pContextMenuItem->logTree(false /* !release log */);
2458 break;
2459
2460 case eLogRel:
2461 m_pContextMenuItem->logTree(true /* release log */);
2462 break;
2463
2464 }
2465 m_pContextMenuItem = NULL;
2466}
2467
2468void VBoxDbgStatsView::viewMenuActivated(int iId)
2469{
2470 switch ((MenuId)iId)
2471 {
2472 case eExpand:
2473 setOpenTree(m_pRoot, true);
2474 break;
2475
2476 case eCollaps:
2477 setOpenTree(m_pRoot, false);
2478 break;
2479
2480 case eReset:
2481 reset(m_PatStr);
2482 /* fall thru */
2483
2484 case eRefresh:
2485 update(QString(m_PatStr));
2486 break;
2487
2488 case eCopy:
2489 m_pRoot->copyTreeToClipboard();
2490 break;
2491
2492 case eLog:
2493 m_pRoot->logTree(false /* !release log */);
2494 break;
2495
2496 case eLogRel:
2497 m_pRoot->logTree(true /* release log */);
2498 break;
2499 }
2500}
2501
2502#endif /* later */
2503
2504
2505
2506
2507/*
2508 *
2509 * V B o x D b g S t a t s
2510 * V B o x D b g S t a t s
2511 * V B o x D b g S t a t s
2512 *
2513 *
2514 */
2515
2516
2517VBoxDbgStats::VBoxDbgStats(PVM pVM, const char *pszPat/* = NULL*/, unsigned uRefreshRate/* = 0*/, QWidget *pParent/* = NULL*/)
2518 : QWidget(pParent), VBoxDbgBase(pVM), m_PatStr(pszPat), m_uRefreshRate(0)
2519{
2520 setWindowTitle("VBoxDbg - Statistics");
2521
2522 /*
2523 * On top, a horizontal box with the pattern field, buttons and refresh interval.
2524 */
2525 QHBoxLayout *pHLayout = new QHBoxLayout;
2526
2527 QLabel *pLabel = new QLabel(" Pattern ");
2528 pHLayout->addWidget(pLabel);
2529 pLabel->setMaximumSize(pLabel->sizeHint());
2530 pLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
2531
2532 m_pPatCB = new QComboBox();
2533 pHLayout->addWidget(m_pPatCB);
2534 if (!m_PatStr.isEmpty())
2535 m_pPatCB->addItem(m_PatStr);
2536 m_pPatCB->setDuplicatesEnabled(false);
2537 m_pPatCB->setEditable(true);
2538 connect(m_pPatCB, SIGNAL(activated(const QString &)), this, SLOT(apply(const QString &)));
2539
2540 QPushButton *pPB = new QPushButton("&All");
2541 pHLayout->addWidget(pPB);
2542 pPB->setMaximumSize(pPB->sizeHint());
2543 connect(pPB, SIGNAL(clicked()), this, SLOT(applyAll()));
2544
2545 pLabel = new QLabel(" Interval ");
2546 pHLayout->addWidget(pLabel);
2547 pLabel->setMaximumSize(pLabel->sizeHint());
2548 pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2549
2550 QSpinBox *pSB = new QSpinBox();
2551 pHLayout->addWidget(pSB);
2552 pSB->setMinimum(0);
2553 pSB->setMaximum(60);
2554 pSB->setSingleStep(1.0);
2555 pSB->setValue(m_uRefreshRate);
2556 pSB->setSuffix(" s");
2557 pSB->setWrapping(false);
2558 pSB->setButtonSymbols(QSpinBox::PlusMinus);
2559 pSB->setMaximumSize(pSB->sizeHint());
2560 connect(pSB, SIGNAL(valueChanged(int)), this, SLOT(setRefresh(int)));
2561
2562
2563 /*
2564 * Create the tree view and setup the layout.
2565 */
2566 VBoxDbgStatsModelVM *pModel = new VBoxDbgStatsModelVM(pVM, m_PatStr, NULL);
2567 m_pView = new VBoxDbgStatsView(pVM, pModel, this);
2568
2569 QWidget *pHBox = new QWidget;
2570 pHBox->setLayout(pHLayout);
2571
2572 QVBoxLayout *pVLayout = new QVBoxLayout;
2573 pVLayout->addWidget(pHBox);
2574 pVLayout->addWidget(m_pView);
2575 this->setLayout(pVLayout);
2576
2577#if 0 //fixme
2578 /*
2579 * Perform the first refresh to get a good window size.
2580 * We do this with sorting disabled because it's horribly slow otherwise.
2581 */
2582//later: int iColumn = m_pView->sortColumn();
2583 m_pView->setUpdatesEnabled(false);
2584 m_pView->setSortingEnabled(false);
2585 refresh();
2586//later: m_pView->sortItems(iColumn, Qt::AscendingOrder);
2587 // QTreeView::expandAll
2588#endif
2589 m_pView->expandAll();
2590 for (int i = 0; i <= 8; i++)
2591 {
2592 printf("%#x: %d", i, m_pView->columnWidth(i));
2593 m_pView->resizeColumnToContents(i);
2594 printf(" -> %d\n", m_pView->columnWidth(i));
2595 }
2596
2597 /*
2598 * Create a refresh timer and start it.
2599 */
2600 m_pTimer = new QTimer(this);
2601 connect(m_pTimer, SIGNAL(timeout()), this, SLOT(refresh()));
2602 setRefresh(uRefreshRate);
2603}
2604
2605VBoxDbgStats::~VBoxDbgStats()
2606{
2607 //????
2608}
2609
2610void VBoxDbgStats::apply(const QString &Str)
2611{
2612 m_PatStr = Str;
2613 refresh();
2614}
2615
2616void VBoxDbgStats::applyAll()
2617{
2618 apply("");
2619}
2620
2621void VBoxDbgStats::refresh()
2622{
2623 m_pView->update(m_PatStr);
2624}
2625
2626void VBoxDbgStats::setRefresh(int iRefresh)
2627{
2628 if ((unsigned)iRefresh != m_uRefreshRate)
2629 {
2630 if (!m_uRefreshRate || iRefresh)
2631 m_pTimer->start(iRefresh * 1000);
2632 else
2633 m_pTimer->stop();
2634 m_uRefreshRate = iRefresh;
2635 }
2636}
2637
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