VirtualBox

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

Last change on this file since 2392 was 1, checked in by vboxsync, 55 years ago

import

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