VirtualBox

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

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

Debugger: air

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