VirtualBox

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

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

debugger: nextNode

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