VirtualBox

source: vbox/trunk/src/VBox/Debugger/VBoxDbgStatsQt.cpp@ 101521

Last change on this file since 101521 was 101101, checked in by vboxsync, 15 months ago

VBoxDbg: Crude implemenation of the 'Refresh' / Ctrl-R action.

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