VirtualBox

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

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

Debugger: more hacking.

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

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