VirtualBox

source: vbox/trunk/src/VBox/Debugger/VBoxDbgStats.cpp@ 6290

Last change on this file since 6290 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1/** @file
2 *
3 * VBox Debugger GUI - Statistics.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGG
23#include "VBoxDbgStats.h"
24#include <qlocale.h>
25#include <qpushbutton.h>
26#include <qspinbox.h>
27#include <qlabel.h>
28#include <qclipboard.h>
29#include <qapplication.h>
30
31#include <VBox/err.h>
32#include <VBox/log.h>
33#include <iprt/string.h>
34#include <iprt/assert.h>
35
36
37
38
39/**
40 * Gets the last component of a statistics name string.
41 *
42 * @returns the last component in the name string.
43 */
44const char *getNodeName(const char *pszName)
45{
46 const char *pszRet = strrchr(pszName, '/');
47 return pszRet && pszName[1] ? pszRet + 1 : pszName;
48}
49
50
51
52
53
54
55/*
56 *
57 * V B o x D b g S t a t s I t e m
58 * V B o x D b g S t a t s I t e m
59 * V B o x D b g S t a t s I t e m
60 *
61 */
62
63
64VBoxDbgStatsItem::VBoxDbgStatsItem(const char *pszName, VBoxDbgStatsItem *pParent, bool fBranch /*= true*/)
65 : QListViewItem(pParent, QString(getNodeName(pszName))), m_pszName(RTStrDup(pszName)), m_fBranch(fBranch), m_pParent(pParent)
66
67{
68}
69
70VBoxDbgStatsItem::VBoxDbgStatsItem(const char *pszName, QListView *pParent, bool fBranch/* = true*/)
71 : QListViewItem(pParent, QString(getNodeName(pszName))), m_pszName(RTStrDup(pszName)), m_fBranch(fBranch), m_pParent(NULL)
72
73{
74}
75
76VBoxDbgStatsItem::~VBoxDbgStatsItem()
77{
78 RTStrFree(m_pszName);
79 m_pszName = NULL;
80}
81
82void VBoxDbgStatsItem::logTree(bool fReleaseLog) const
83{
84 /* Iterate and print our children. */
85 QListViewItem *pItem;
86 for (pItem = firstChild(); pItem; pItem = pItem->nextSibling())
87 {
88 VBoxDbgStatsItem *pMyItem = (VBoxDbgStatsItem *)pItem;
89 pMyItem->logTree(fReleaseLog);
90 }
91}
92
93void VBoxDbgStatsItem::stringifyTree(QString &String) const
94{
95 /* Iterate and stringify our children. */
96 QListViewItem *pItem;
97 for (pItem = firstChild(); pItem; pItem = pItem->nextSibling())
98 {
99 VBoxDbgStatsItem *pMyItem = (VBoxDbgStatsItem *)pItem;
100 pMyItem->stringifyTree(String);
101 }
102}
103
104void VBoxDbgStatsItem::copyTreeToClipboard(void) const
105{
106 QString String;
107 stringifyTree(String);
108
109 QClipboard *pClipboard = QApplication::clipboard();
110 if (pClipboard)
111 pClipboard->setText(String, QClipboard::Clipboard);
112}
113
114
115
116
117/*
118 *
119 * V B o x D b g S t a t s L e a f I t e m
120 * V B o x D b g S t a t s L e a f I t e m
121 * V B o x D b g S t a t s L e a f I t e m
122 *
123 */
124
125
126VBoxDbgStatsLeafItem::VBoxDbgStatsLeafItem(const char *pszName, VBoxDbgStatsItem *pParent)
127 : VBoxDbgStatsItem(pszName, pParent, false),
128 m_pNext(NULL), m_pPrev(NULL), m_enmType(STAMTYPE_INVALID),
129 m_enmUnit(STAMUNIT_INVALID), m_DescStr()
130{
131 memset(&m_Data, 0, sizeof(m_Data));
132}
133
134
135VBoxDbgStatsLeafItem::~VBoxDbgStatsLeafItem()
136{
137}
138
139
140/**
141 * Formats a number into a 64-byte buffer.
142 */
143static char *formatNumber(char *psz, uint64_t u64)
144{
145 static const char s_szDigits[] = "0123456789";
146 psz += 63;
147 *psz-- = '\0';
148 unsigned cDigits = 0;
149 for (;;)
150 {
151 const unsigned iDigit = u64 % 10;
152 u64 /= 10;
153 *psz = s_szDigits[iDigit];
154 if (!u64)
155 break;
156 psz--;
157 if (!(++cDigits % 3))
158 *psz-- = ',';
159 }
160 return psz;
161}
162
163
164/**
165 * Formats a number into a 64-byte buffer.
166 * (18.446.744.073.709.551.615)
167 */
168static char *formatNumberSigned(char *psz, int64_t i64)
169{
170 static const char s_szDigits[] = "0123456789";
171 psz += 63;
172 *psz-- = '\0';
173 const bool fNegative = i64 < 0;
174 uint64_t u64 = fNegative ? -i64 : i64;
175 unsigned cDigits = 0;
176 for (;;)
177 {
178 const unsigned iDigit = u64 % 10;
179 u64 /= 10;
180 *psz = s_szDigits[iDigit];
181 if (!u64)
182 break;
183 psz--;
184 if (!(++cDigits % 3))
185 *psz-- = ',';
186 }
187 if (fNegative)
188 *--psz = '-';
189 return psz;
190}
191
192
193/**
194 * Formats a unsigned hexadecimal number into a into a 64-byte buffer.
195 */
196static char *formatHexNumber(char *psz, uint64_t u64, unsigned cZeros)
197{
198 static const char s_szDigits[] = "0123456789abcdef";
199 psz += 63;
200 *psz-- = '\0';
201 unsigned cDigits = 0;
202 for (;;)
203 {
204 const unsigned iDigit = u64 % 16;
205 u64 /= 16;
206 *psz = s_szDigits[iDigit];
207 ++cDigits;
208 if (!u64 && cDigits >= cZeros)
209 break;
210 psz--;
211 if (!(cDigits % 8))
212 *psz-- = '\'';
213 }
214 return psz;
215}
216
217
218/**
219 * Formats a sort key number.
220 */
221static void formatSortKey(char *psz, uint64_t u64)
222{
223 static const char s_szDigits[] = "0123456789abcdef";
224 /* signed */
225 *psz++ = '+';
226
227 /* 16 hex digits */
228 psz[16] = '\0';
229 unsigned i = 16;
230 while (i-- > 0)
231 {
232 if (u64)
233 {
234 const unsigned iDigit = u64 % 16;
235 u64 /= 16;
236 psz[i] = s_szDigits[iDigit];
237 }
238 else
239 psz[i] = '0';
240 }
241}
242
243
244#if 0/* unused */
245/**
246 * Formats a sort key number.
247 */
248static void formatSortKeySigned(char *psz, int64_t i64)
249{
250 static const char s_szDigits[] = "0123456789abcdef";
251
252 /* signed */
253 uint64_t u64;
254 if (i64 >= 0)
255 {
256 *psz++ = '+';
257 u64 = i64;
258 }
259 else
260 {
261 *psz++ = '-';
262 u64 = -i64;
263 }
264
265 /* 16 hex digits */
266 psz[16] = '\0';
267 unsigned i = 16;
268 while (i-- > 0)
269 {
270 if (u64)
271 {
272 const unsigned iDigit = u64 % 16;
273 u64 /= 16;
274 psz[i] = s_szDigits[iDigit];
275 }
276 else
277 psz[i] = '0';
278 }
279}
280#endif
281
282
283void VBoxDbgStatsLeafItem::update(STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, STAMVISIBILITY enmVisibility, const char *pszDesc)
284{
285 /*
286 * Detect changes.
287 * This path will be taken on the first update and if a item
288 * is reregistred with a different unit/type (unlikely).
289 */
290 if ( enmType != m_enmType
291 || enmUnit != m_enmUnit)
292 {
293 m_enmType = enmType;
294 m_enmUnit = enmUnit;
295
296 /*
297 * Unit.
298 */
299 setText(1, STAMR3GetUnit(enmUnit));
300
301 /**
302 * Update the description.
303 * Insert two spaces as gap after the last left-aligned field.
304 * @todo dmik: How to make this better?
305 */
306 m_DescStr = QString(" ") + QString(pszDesc);
307
308 /*
309 * Clear the content.
310 */
311 setText(2, "");
312 setText(3, "");
313 setText(4, "");
314 setText(5, "");
315 setText(6, "");
316 setText(8, m_DescStr);
317 }
318
319 /*
320 * Update the data.
321 */
322 char sz[64];
323 switch (enmType)
324 {
325 case STAMTYPE_COUNTER:
326 {
327 const uint64_t cPrev = m_Data.Counter.c;
328 m_Data.Counter = *(PSTAMCOUNTER)pvSample;
329 setText(2, formatNumber(sz, m_Data.Counter.c));
330 setText(7, formatNumberSigned(sz, m_Data.Counter.c - cPrev));
331 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
332 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.Counter.c));
333 break;
334 }
335
336 case STAMTYPE_PROFILE:
337 case STAMTYPE_PROFILE_ADV:
338 {
339 const uint64_t cPeriodsPrev = m_Data.Profile.cPeriods;
340 m_Data.Profile = *(PSTAMPROFILE)pvSample;
341 if (m_Data.Profile.cPeriods)
342 {
343 setText(2, formatNumber(sz, m_Data.Profile.cPeriods));
344 setText(3, formatNumber(sz, m_Data.Profile.cTicksMin));
345 setText(4, formatNumber(sz, m_Data.Profile.cTicks / m_Data.Profile.cPeriods));
346 setText(5, formatNumber(sz, m_Data.Profile.cTicksMax));
347 setText(6, formatNumber(sz, m_Data.Profile.cTicks));
348 setText(7, formatNumberSigned(sz, m_Data.Profile.cPeriods - cPeriodsPrev));
349 setVisible(enmVisibility != STAMVISIBILITY_NOT_GUI);
350 }
351 else
352 {
353 setText(2, "0");
354 setText(3, "0");
355 setText(4, "0");
356 setText(5, "0");
357 setText(6, "0");
358 setText(7, "0");
359 setVisible(enmVisibility != STAMVISIBILITY_NOT_GUI && enmVisibility == STAMVISIBILITY_ALWAYS);
360 }
361 break;
362 }
363
364 case STAMTYPE_RATIO_U32:
365 case STAMTYPE_RATIO_U32_RESET:
366 {
367 const STAMRATIOU32 RatioU32 = m_Data.RatioU32;
368 m_Data.RatioU32 = *(PSTAMRATIOU32)pvSample;
369
370 char sz2[64];
371 char sz3[128];
372 strcat(strcat(strcpy(sz3, formatNumber(sz, m_Data.RatioU32.u32A)), " : "), formatNumber(sz2, m_Data.RatioU32.u32B));
373 setText(2, sz3);
374 ///@todo ratio: setText(7, formatNumberSigned(sz, m_Data.u64 - u64Prev));
375 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
376 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.RatioU32.u32A || m_Data.RatioU32.u32B));
377 break;
378 }
379
380 case STAMTYPE_CALLBACK:
381 {
382 const char *pszString = (const char *)pvSample;
383 setText(2, pszString);
384 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
385 && (enmVisibility == STAMVISIBILITY_ALWAYS || *pszString));
386 break;
387 }
388
389 case STAMTYPE_U8:
390 case STAMTYPE_U8_RESET:
391 {
392 const uint8_t u8Prev = m_Data.u8;
393 m_Data.u8 = *(uint8_t *)pvSample;
394 setText(2, formatNumber(sz, m_Data.u8));
395 setText(7, formatNumberSigned(sz, (int32_t)m_Data.u8 - u8Prev));
396 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
397 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u8));
398 break;
399 }
400
401 case STAMTYPE_X8:
402 case STAMTYPE_X8_RESET:
403 {
404 const uint8_t u8Prev = m_Data.u8;
405 m_Data.u8 = *(uint8_t *)pvSample;
406 setText(2, formatHexNumber(sz, m_Data.u8, 2));
407 setText(7, formatHexNumber(sz, m_Data.u8 - u8Prev, 1));
408 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
409 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u8));
410 break;
411 }
412
413 case STAMTYPE_U16:
414 case STAMTYPE_U16_RESET:
415 {
416 const uint16_t u16Prev = m_Data.u16;
417 m_Data.u16 = *(uint16_t *)pvSample;
418 setText(2, formatNumber(sz, m_Data.u16));
419 setText(7, formatNumberSigned(sz, (int32_t)m_Data.u16 - u16Prev));
420 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
421 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u16));
422 break;
423 }
424
425 case STAMTYPE_X16:
426 case STAMTYPE_X16_RESET:
427 {
428 const uint16_t u16Prev = m_Data.u16;
429 m_Data.u16 = *(uint16_t *)pvSample;
430 setText(2, formatHexNumber(sz, m_Data.u16, 4));
431 setText(7, formatHexNumber(sz, m_Data.u16 - u16Prev, 1));
432 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
433 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u16));
434 break;
435 }
436
437 case STAMTYPE_U32:
438 case STAMTYPE_U32_RESET:
439 {
440 const uint32_t u32Prev = m_Data.u32;
441 m_Data.u32 = *(uint32_t *)pvSample;
442 setText(2, formatNumber(sz, m_Data.u32));
443 setText(7, formatNumberSigned(sz, (int64_t)m_Data.u32 - u32Prev));
444 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
445 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u32));
446 break;
447 }
448
449 case STAMTYPE_X32:
450 case STAMTYPE_X32_RESET:
451 {
452 const uint32_t u32Prev = m_Data.u32;
453 m_Data.u32 = *(uint32_t *)pvSample;
454 setText(2, formatHexNumber(sz, m_Data.u32, 8));
455 setText(7, formatHexNumber(sz, m_Data.u32 - u32Prev, 1));
456 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
457 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u32));
458 break;
459 }
460
461 case STAMTYPE_U64:
462 case STAMTYPE_U64_RESET:
463 {
464 const uint64_t u64Prev = m_Data.u64;
465 m_Data.u64 = *(uint64_t *)pvSample;
466 setText(2, formatNumber(sz, m_Data.u64));
467 setText(7, formatNumberSigned(sz, m_Data.u64 - u64Prev));
468 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
469 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u64));
470 break;
471 }
472
473 case STAMTYPE_X64:
474 case STAMTYPE_X64_RESET:
475 {
476 const uint64_t u64Prev = m_Data.u64;
477 m_Data.u64 = *(uint64_t *)pvSample;
478 setText(2, formatHexNumber(sz, m_Data.u64, 16));
479 setText(7, formatHexNumber(sz, m_Data.u64 - u64Prev, 1));
480 setVisible( enmVisibility != STAMVISIBILITY_NOT_GUI
481 && (enmVisibility == STAMVISIBILITY_ALWAYS || m_Data.u64));
482 break;
483 }
484
485 default:
486 break;
487 }
488}
489
490
491QString VBoxDbgStatsLeafItem::key(int iColumn, bool /*fAscending*/) const
492{
493 /* name and description */
494 if (iColumn <= 1 || iColumn >= 8)
495 return text(iColumn);
496
497 /* the number columns */
498 char sz[128];
499 switch (m_enmType)
500 {
501 case STAMTYPE_COUNTER:
502 switch (iColumn)
503 {
504 case 2: formatSortKey(sz, m_Data.Counter.c); break;
505 case 7: return text(iColumn);
506 default: sz[0] = '\0'; break;
507 }
508 break;
509
510 case STAMTYPE_PROFILE:
511 case STAMTYPE_PROFILE_ADV:
512 if (m_Data.Profile.cPeriods)
513 {
514 switch (iColumn)
515 {
516 case 2: formatSortKey(sz, m_Data.Profile.cPeriods); break;
517 case 3: formatSortKey(sz, m_Data.Profile.cTicksMin); break;
518 case 4: formatSortKey(sz, m_Data.Profile.cTicks / m_Data.Profile.cPeriods); break;
519 case 5: formatSortKey(sz, m_Data.Profile.cTicksMax); break;
520 case 6: formatSortKey(sz, m_Data.Profile.cTicks); break;
521 case 7: return text(iColumn);
522 default: sz[0] = '\0'; break;
523 }
524 }
525 else
526 sz[0] = '\0';
527 break;
528
529 case STAMTYPE_RATIO_U32:
530 case STAMTYPE_RATIO_U32_RESET:
531 if (m_Data.RatioU32.u32B)
532 formatSortKey(sz, (m_Data.RatioU32.u32A * (uint64_t)1000) / m_Data.RatioU32.u32B);
533 else if (m_Data.RatioU32.u32A)
534 formatSortKey(sz, m_Data.RatioU32.u32A * (uint64_t)1000);
535 else
536 formatSortKey(sz, 1000);
537 break;
538
539 case STAMTYPE_U8:
540 case STAMTYPE_U8_RESET:
541 switch (iColumn)
542 {
543 case 2: formatSortKey(sz, m_Data.u8); break;
544 case 7: return text(iColumn);
545 default: sz[0] = '\0'; break;
546 }
547 break;
548
549 case STAMTYPE_U16:
550 case STAMTYPE_U16_RESET:
551 switch (iColumn)
552 {
553 case 2: formatSortKey(sz, m_Data.u16); break;
554 case 7: return text(iColumn);
555 default: sz[0] = '\0'; break;
556 }
557 break;
558
559 case STAMTYPE_U32:
560 case STAMTYPE_U32_RESET:
561 switch (iColumn)
562 {
563 case 2: formatSortKey(sz, m_Data.u32); break;
564 case 7: return text(iColumn);
565 default: sz[0] = '\0'; break;
566 }
567 break;
568
569 case STAMTYPE_U64:
570 case STAMTYPE_U64_RESET:
571 switch (iColumn)
572 {
573 case 2: formatSortKey(sz, m_Data.u64); break;
574 case 7: return text(iColumn);
575 default: sz[0] = '\0'; break;
576 }
577 break;
578
579 case STAMTYPE_CALLBACK:
580 default:
581 return text(iColumn);
582 }
583
584 return QString(sz);
585}
586
587void VBoxDbgStatsLeafItem::logTree(bool fReleaseLog) const
588{
589 /*
590 * Generic printing.
591 */
592 if (isVisible())
593 {
594 if (fReleaseLog)
595 RTLogRelPrintf("%-50s %-10s %18s %18s %18s %18s %16s %s\n",
596 getName(), (const char *)text(1), (const char *)text(2), (const char *)text(3),
597 (const char *)text(4), (const char *)text(5), (const char *)text(7), (const char *)text(8));
598 else
599 RTLogPrintf("%-50s %-10s %18s %18s %18s %18s %16s %s\n",
600 getName(), (const char *)text(1), (const char *)text(2), (const char *)text(3),
601 (const char *)text(4), (const char *)text(5), (const char *)text(7), (const char *)text(8));
602 }
603
604 /*
605 * Let the super class to do the rest.
606 */
607 VBoxDbgStatsItem::logTree(fReleaseLog);
608}
609
610void VBoxDbgStatsLeafItem::stringifyTree(QString &String) const
611{
612 /*
613 * Generic printing.
614 */
615 if (isVisible())
616 {
617 QString ItemString;
618 ItemString.sprintf("%-50s %-10s %18s %18s %18s %18s %16s %s\n",
619 getName(), (const char *)text(1), (const char *)text(2), (const char *)text(3),
620 (const char *)text(4), (const char *)text(5), (const char *)text(7), (const char *)text(8));
621 String += ItemString;
622 }
623
624 /*
625 * Let the super class to do the rest.
626 */
627 VBoxDbgStatsItem::stringifyTree(String);
628}
629
630
631
632
633
634/*
635 *
636 * V B o x D b g S t a t s V i e w
637 * V B o x D b g S t a t s V i e w
638 * V B o x D b g S t a t s V i e w
639 *
640 *
641 */
642
643
644VBoxDbgStatsView::VBoxDbgStatsView(PVM pVM, VBoxDbgStats *pParent/* = NULL*/, const char *pszName/* = NULL*/, WFlags f/* = 0*/)
645 : QListView(pParent, pszName, f), VBoxDbgBase(pVM),
646 m_pParent(pParent), m_pHead(NULL), m_pTail(NULL), m_pCur(NULL), m_pRoot(NULL),
647 m_pLeafMenu(NULL), m_pBranchMenu(NULL), m_pViewMenu(NULL), m_pContextMenuItem(NULL)
648
649{
650 /*
651 * Create the columns.
652 */
653 addColumn("Name"); // 0
654 addColumn("Unit"); // 1
655 setColumnAlignment(1, Qt::AlignCenter);
656 addColumn("Value/Times"); // 2
657 setColumnAlignment(2, Qt::AlignRight);
658 addColumn("Min"); // 3
659 setColumnAlignment(3, Qt::AlignRight);
660 addColumn("Average"); // 4
661 setColumnAlignment(4, Qt::AlignRight);
662 addColumn("Max"); // 5
663 setColumnAlignment(5, Qt::AlignRight);
664 addColumn("Total"); // 6
665 setColumnAlignment(6, Qt::AlignRight);
666 addColumn("dInt"); // 7
667 setColumnAlignment(7, Qt::AlignRight);
668 int i = addColumn("Description"); // 8
669 NOREF(i);
670 Assert(i == 8);
671 setShowSortIndicator(true);
672
673 /*
674 * Create the root node.
675 */
676 setRootIsDecorated(true);
677 m_pRoot = new VBoxDbgStatsItem("/", this);
678 m_pRoot->setOpen(true);
679
680 /*
681 * We've got three menus to populate and link up.
682 */
683 m_pLeafMenu = new QPopupMenu(this);
684 m_pLeafMenu->insertItem("Rese&t", eReset);
685 m_pLeafMenu->insertItem("&Refresh", eRefresh);
686 m_pLeafMenu->insertItem("&Copy", eCopy);
687 m_pLeafMenu->insertItem("To &Log", eLog);
688 m_pLeafMenu->insertItem("T&o Release Log", eLogRel);
689 connect(m_pLeafMenu, SIGNAL(activated(int)), this, SLOT(leafMenuActivated(int)));
690
691 m_pBranchMenu = new QPopupMenu(this);
692 m_pBranchMenu->insertItem("&Expand Tree", eExpand);
693 m_pBranchMenu->insertItem("&Collaps Tree", eCollaps);
694 m_pBranchMenu->insertItem("&Refresh", eRefresh);
695 m_pBranchMenu->insertItem("Rese&t", eReset);
696 m_pBranchMenu->insertItem("&Copy", eCopy);
697 m_pBranchMenu->insertItem("To &Log", eLog);
698 m_pBranchMenu->insertItem("T&o Release Log", eLogRel);
699 connect(m_pBranchMenu, SIGNAL(activated(int)), this, SLOT(branchMenuActivated(int)));
700
701 m_pViewMenu = new QPopupMenu(this);
702 m_pViewMenu->insertItem("&Expand All", eExpand);
703 m_pViewMenu->insertItem("&Collaps All", eCollaps);
704 m_pViewMenu->insertItem("&Refresh", eRefresh);
705 m_pViewMenu->insertItem("Rese&t", eReset);
706 m_pViewMenu->insertItem("&Copy", eCopy);
707 m_pViewMenu->insertItem("To &Log", eLog);
708 m_pViewMenu->insertItem("T&o Release Log", eLogRel);
709 connect(m_pViewMenu, SIGNAL(activated(int)), this, SLOT(viewMenuActivated(int)));
710
711 connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), this,
712 SLOT(contextMenuReq(QListViewItem *, const QPoint &, int)));
713}
714
715VBoxDbgStatsView::~VBoxDbgStatsView()
716{
717 /* Who frees the items? What happens to the reference in QListView? Does the parent free things in some way? */
718#if 0
719 VBoxDbgStatsLeafItem *pCur = m_pHead;
720 while (pCur)
721 {
722 VBoxDbgStatsLeafItem *pFree = pCur;
723 pCur = pCur->m_pNext;
724 delete pFree;
725 }
726
727 delete m_pRoot;
728#endif
729 m_pHead = NULL;
730 m_pTail = NULL;
731 m_pCur = NULL;
732 m_pRoot = NULL;
733}
734
735/**
736 * Hides all parent branches which doesn't have any visible leafs.
737 */
738static void hideParentBranches(VBoxDbgStatsLeafItem *pItem)
739{
740 for (VBoxDbgStatsItem *pParent = pItem->getParent(); pParent; pParent = pParent->getParent())
741 {
742 QListViewItem *pChild = pParent->firstChild();
743 while (pChild && !pChild->isVisible())
744 pChild = pChild->nextSibling();
745 if (pChild)
746 return;
747 pParent->setVisible(false);
748 }
749}
750
751/**
752 * Shows all parent branches
753 */
754static void showParentBranches(VBoxDbgStatsLeafItem *pItem)
755{
756 for (VBoxDbgStatsItem *pParent = pItem->getParent(); pParent; pParent = pParent->getParent())
757 pParent->setVisible(true);
758}
759
760void VBoxDbgStatsView::update(const QString &rPatStr)
761{
762 m_pCur = m_pHead;
763 m_PatStr = rPatStr;
764 int rc = stamEnum(m_PatStr.isEmpty() ? NULL : m_PatStr, updateCallback, this);
765 if (VBOX_SUCCESS(rc))
766 {
767 /* hide what's left */
768 for (VBoxDbgStatsLeafItem *pCur = m_pCur; pCur; pCur = pCur->m_pNext)
769 if (pCur->isVisible())
770 {
771 pCur->setVisible(false);
772 hideParentBranches(pCur);
773 }
774 }
775 m_pCur = NULL;
776}
777
778void VBoxDbgStatsView::reset(const QString &rPatStr)
779{
780 stamReset(rPatStr.isEmpty() ? NULL : rPatStr);
781}
782
783static void setOpenTree(QListViewItem *pItem, bool f)
784{
785 pItem->setOpen(f);
786 for (pItem = pItem->firstChild(); pItem; pItem = pItem->nextSibling())
787 setOpenTree(pItem, f);
788}
789
790void VBoxDbgStatsView::expandAll()
791{
792 setOpenTree(m_pRoot, true);
793}
794
795void VBoxDbgStatsView::collapsAll()
796{
797 setOpenTree(m_pRoot, false);
798}
799
800/*static*/ DECLCALLBACK(int) VBoxDbgStatsView::updateCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
801 STAMVISIBILITY enmVisibility, const char *pszDesc, void *pvUser)
802{
803 Log3(("updateCallback: %s\n", pszName));
804 VBoxDbgStatsView *pThis = (VBoxDbgStatsView *)pvUser;
805
806 /*
807 * Skip the ones which shouldn't be visible in the GUI.
808 */
809 if (enmVisibility == STAMVISIBILITY_NOT_GUI)
810 return 0;
811
812 /*
813 * Advance to the matching item.
814 */
815 VBoxDbgStatsLeafItem *pCur = pThis->m_pCur;
816 while (pCur)
817 {
818 /*
819 * ASSUMES ascending order of STAM items.
820 */
821 int iDiff = strcmp(pszName, pCur->getName());
822 if (!iDiff)
823 break;
824 if (iDiff > 0)
825 {
826 /*
827 * Removed / filtered out.
828 */
829 Log2(("updateCallback: %s - filtered out\n", pCur->getName()));
830 if (pCur->isVisible())
831 {
832 pCur->setVisible(false);
833 hideParentBranches(pCur);
834 }
835
836 pCur = pCur->m_pNext;
837 }
838 else if (iDiff < 0)
839 {
840 /*
841 * New item, insert before pCur.
842 */
843 Log2(("updateCallback: %s - new\n", pszName));
844 VBoxDbgStatsLeafItem *pNew = new VBoxDbgStatsLeafItem(pszName, pThis->createPath(pszName));
845 pNew->m_pNext = pCur;
846 pNew->m_pPrev = pCur->m_pPrev;
847 if (pNew->m_pPrev)
848 pNew->m_pPrev->m_pNext = pNew;
849 else
850 pThis->m_pHead = pNew;
851 pCur->m_pPrev = pNew;
852 pCur = pNew;
853 Assert(!strcmp(pszName, pCur->getName()));
854 break;
855 }
856 }
857
858 /*
859 * End of items, insert it at the tail.
860 */
861 if (!pCur)
862 {
863 Log2(("updateCallback: %s - new end\n", pszName));
864 pCur = new VBoxDbgStatsLeafItem(pszName, pThis->createPath(pszName));
865 pCur->m_pNext = NULL;
866 pCur->m_pPrev = pThis->m_pTail;
867 if (pCur->m_pPrev)
868 pCur->m_pPrev->m_pNext = pCur;
869 else
870 pThis->m_pHead = pCur;
871 pThis->m_pTail = pCur;
872 }
873 Assert(pThis->m_pHead);
874 Assert(pThis->m_pTail);
875
876 /*
877 * Update it and move on.
878 */
879 if (!pCur->isVisible())
880 showParentBranches(pCur);
881 pCur->update(enmType, pvSample, enmUnit, enmVisibility, pszDesc);
882 pThis->m_pCur = pCur->m_pNext;
883
884 return 0;
885}
886
887VBoxDbgStatsItem *VBoxDbgStatsView::createPath(const char *pszName)
888{
889 const char * const pszFullName = pszName;
890
891 /*
892 * Start at root.
893 */
894 while (*pszName == '/')
895 pszName++;
896 VBoxDbgStatsItem *pParent = m_pRoot;
897
898 /*
899 * Walk down the path creating what's missing.
900 */
901 for (;;)
902 {
903 /*
904 * Extract the path component.
905 */
906 const char *pszEnd = strchr(pszName, '/');
907 if (!pszEnd)
908 return pParent;
909 QString NameStr = QString::fromUtf8(pszName, pszEnd - pszName);
910 /* advance */
911 pszName = pszEnd + 1;
912
913 /*
914 * Try find the name among the children of that parent guy.
915 */
916 QListViewItem *pChild = pParent->firstChild();
917 while (pChild && pChild->text(0) != NameStr)
918 pChild = pChild->nextSibling();
919 if (pChild)
920 pParent = (VBoxDbgStatsItem *)pChild;
921 else
922 {
923 Log3(("createPath: %.*s\n", pszEnd - pszFullName, pszFullName));
924 NameStr = QString::fromUtf8(pszFullName, pszEnd - pszFullName);
925 pParent = new VBoxDbgStatsItem(NameStr, pParent);
926 }
927 pParent->setVisible(true);
928 }
929}
930
931void VBoxDbgStatsView::contextMenuReq(QListViewItem *pItem, const QPoint &rPoint, int /*iColumn*/)
932{
933 if (pItem)
934 {
935 m_pContextMenuItem = (VBoxDbgStatsItem *)pItem;
936 if (m_pContextMenuItem->isLeaf())
937 {
938 m_pLeafMenu->setItemEnabled(eReset, isVMOk());
939 m_pLeafMenu->setItemEnabled(eRefresh, isVMOk());
940 m_pLeafMenu->popup(rPoint);
941 }
942 else
943 {
944 m_pBranchMenu->setItemEnabled(eReset, isVMOk());
945 m_pBranchMenu->setItemEnabled(eRefresh, isVMOk());
946 m_pBranchMenu->popup(rPoint);
947 }
948 }
949 else
950 {
951 m_pContextMenuItem = NULL;
952 m_pViewMenu->setItemEnabled(eReset, isVMOk());
953 m_pViewMenu->setItemEnabled(eRefresh, isVMOk());
954 m_pViewMenu->popup(rPoint);
955 }
956}
957
958void VBoxDbgStatsView::leafMenuActivated(int iId)
959{
960 VBoxDbgStatsLeafItem *pItem = (VBoxDbgStatsLeafItem *)m_pContextMenuItem;
961 AssertReturn(pItem, (void)0);
962
963 switch ((MenuId)iId)
964 {
965 case eReset:
966 stamReset(m_pContextMenuItem->getName());
967 /* fall thru */
968
969 case eRefresh:
970 m_pCur = pItem;
971 stamEnum(m_pContextMenuItem->getName(), updateCallback, this);
972 break;
973
974 case eCopy:
975 m_pContextMenuItem->copyTreeToClipboard();
976 break;
977
978 case eLog:
979 m_pContextMenuItem->logTree(false /* !release log */);
980 break;
981
982 case eLogRel:
983 m_pContextMenuItem->logTree(true /* release log */);
984 break;
985
986 default: /* keep gcc quite */
987 break;
988 }
989 m_pContextMenuItem = NULL;
990}
991
992void VBoxDbgStatsView::branchMenuActivated(int iId)
993{
994 AssertReturn(m_pContextMenuItem, (void)0);
995
996 /** @todo make enum for iId */
997 switch ((MenuId)iId)
998 {
999 case eExpand:
1000 setOpenTree(m_pContextMenuItem, true);
1001 break;
1002
1003 case eCollaps:
1004 setOpenTree(m_pContextMenuItem, false);
1005 break;
1006
1007 case eReset:
1008 {
1009 QString Str = QString::fromUtf8(m_pContextMenuItem->getName());
1010 Str.append((Str != "/") ? "/*" : "*");
1011 stamReset(Str);
1012 }
1013 /* fall thru */
1014
1015 case eRefresh:
1016 {
1017 const char *psz = m_pContextMenuItem->getName();
1018 QString Str = QString::fromUtf8(psz);
1019 if (strcmp(psz, "/"))
1020 {
1021 int cch = strlen(psz);
1022 m_pCur = m_pHead;
1023 while ( m_pCur
1024 && ( strncmp(psz, m_pCur->getName(), cch)
1025 || m_pCur->getName()[cch] != '/'))
1026 {
1027 m_pCur = m_pCur->m_pNext;
1028 }
1029 if (!m_pCur)
1030 return;
1031 Str.append("/*");
1032 }
1033 else
1034 {
1035 m_pCur = m_pHead;
1036 Str.append("*");
1037 }
1038 stamEnum(Str, updateCallback, this);
1039 m_pCur = NULL;
1040 break;
1041 }
1042
1043 case eCopy:
1044 m_pContextMenuItem->copyTreeToClipboard();
1045 break;
1046
1047 case eLog:
1048 m_pContextMenuItem->logTree(false /* !release log */);
1049 break;
1050
1051 case eLogRel:
1052 m_pContextMenuItem->logTree(true /* release log */);
1053 break;
1054
1055 }
1056 m_pContextMenuItem = NULL;
1057}
1058
1059void VBoxDbgStatsView::viewMenuActivated(int iId)
1060{
1061 switch ((MenuId)iId)
1062 {
1063 case eExpand:
1064 setOpenTree(m_pRoot, true);
1065 break;
1066
1067 case eCollaps:
1068 setOpenTree(m_pRoot, false);
1069 break;
1070
1071 case eReset:
1072 reset(m_PatStr);
1073 /* fall thru */
1074
1075 case eRefresh:
1076 update(QString(m_PatStr));
1077 break;
1078
1079 case eCopy:
1080 m_pRoot->copyTreeToClipboard();
1081 break;
1082
1083 case eLog:
1084 m_pRoot->logTree(false /* !release log */);
1085 break;
1086
1087 case eLogRel:
1088 m_pRoot->logTree(true /* release log */);
1089 break;
1090 }
1091}
1092
1093
1094
1095
1096
1097
1098/*
1099 *
1100 * V B o x D b g S t a t s
1101 * V B o x D b g S t a t s
1102 * V B o x D b g S t a t s
1103 *
1104 *
1105 */
1106
1107
1108VBoxDbgStats::VBoxDbgStats(PVM pVM, const char *pszPat/* = NULL*/, unsigned uRefreshRate/* = 0*/,
1109 QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/, WFlags f/* = 0*/)
1110 : QVBox(pParent, pszName, f), VBoxDbgBase(pVM),
1111 m_PatStr(pszPat), m_uRefreshRate(0)
1112{
1113 setCaption("VBoxDbg - Statistics");
1114
1115 /*
1116 * On top, a horizontal box with the pattern field, buttons and refresh interval.
1117 */
1118 QHBox *pHBox = new QHBox(this);
1119
1120 QLabel *pLabel = new QLabel(NULL, " Pattern ", pHBox);
1121 pLabel->setMaximumSize(pLabel->sizeHint());
1122 pLabel->setAlignment(AlignHCenter | AlignVCenter);
1123
1124 m_pPatCB = new QComboBox(true, pHBox, "Pattern");
1125 if (pszPat && *pszPat)
1126 m_pPatCB->insertItem(pszPat);
1127 m_pPatCB->setDuplicatesEnabled(false);
1128 connect(m_pPatCB, SIGNAL(activated(const QString &)), this, SLOT(apply(const QString &)));
1129
1130 QPushButton *pPB = new QPushButton("&All", pHBox);
1131 connect(pPB, SIGNAL(clicked()), this, SLOT(applyAll()));
1132 pPB->setMaximumSize(pPB->sizeHint());
1133
1134 pLabel = new QLabel(NULL, " Interval ", pHBox);
1135 pLabel->setMaximumSize(pLabel->sizeHint());
1136 pLabel->setAlignment(AlignRight | AlignVCenter);
1137
1138 QSpinBox *pSB = new QSpinBox(0, 60, 1, pHBox, "Interval");
1139 pSB->setValue(m_uRefreshRate);
1140 pSB->setSuffix(" s");
1141 pSB->setWrapping(false);
1142 pSB->setButtonSymbols(QSpinBox::PlusMinus);
1143 pSB->setMaximumSize(pSB->sizeHint());
1144 connect(pSB, SIGNAL(valueChanged(int)), this, SLOT(setRefresh(int)));
1145
1146
1147 /*
1148 * Place the view at the top.
1149 */
1150 m_pView = new VBoxDbgStatsView(pVM, this, pszName, f);
1151
1152 /*
1153 * Perform the first refresh to get a good window size.
1154 * We do this with sorting disabled because it's horribly slow otherwise.
1155 */
1156 int iColumn = m_pView->sortColumn();
1157 m_pView->setSortColumn(-1);
1158 refresh();
1159 m_pView->setSortColumn(iColumn);
1160 m_pView->sort();
1161
1162 /*
1163 * Create a refresh timer and start it.
1164 */
1165 m_pTimer = new QTimer(this);
1166 connect(m_pTimer, SIGNAL(timeout()), this, SLOT(refresh()));
1167 setRefresh(uRefreshRate);
1168}
1169
1170VBoxDbgStats::~VBoxDbgStats()
1171{
1172 //????
1173}
1174
1175void VBoxDbgStats::apply(const QString &Str)
1176{
1177 m_PatStr = Str;
1178 refresh();
1179}
1180
1181void VBoxDbgStats::applyAll()
1182{
1183 apply("");
1184}
1185
1186void VBoxDbgStats::refresh()
1187{
1188 m_pView->update(m_PatStr);
1189}
1190
1191void VBoxDbgStats::setRefresh(int iRefresh)
1192{
1193 if ((unsigned)iRefresh != m_uRefreshRate)
1194 {
1195 if (!m_uRefreshRate)
1196 m_pTimer->start(iRefresh * 1000);
1197 else if (iRefresh)
1198 m_pTimer->changeInterval(iRefresh * 1000);
1199 else
1200 m_pTimer->stop();
1201 m_uRefreshRate = iRefresh;
1202 }
1203}
1204
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