From 514ec8338aefdbe90bedd2e2ae645451651fed8b Mon Sep 17 00:00:00 2001 From: Ernie Pasveer Date: Wed, 1 Sep 2021 14:01:00 -0500 Subject: [PATCH] Initial add of files. --- SeerSharedLibraryBrowserWidget.cpp | 178 +++++++++++ SeerSharedLibraryBrowserWidget.h | 28 ++ SeerSharedLibraryBrowserWidget.ui | 68 +++++ SeerSourceBrowserWidget.cpp | 215 +++++++++++++ SeerSourceBrowserWidget.h | 31 ++ SeerSourceBrowserWidget.ui | 48 +++ SeerSourceLibraryManagerWidget.cpp | 48 +++ SeerSourceLibraryManagerWidget.h | 30 ++ SeerSourceLibraryManagerWidget.ui | 43 +++ SeerStackArgumentsBrowserWidget.cpp | 136 +++++++++ SeerStackArgumentsBrowserWidget.h | 28 ++ SeerStackArgumentsBrowserWidget.ui | 43 +++ SeerStackFramesBrowserWidget.cpp | 158 ++++++++++ SeerStackFramesBrowserWidget.h | 32 ++ SeerStackFramesBrowserWidget.ui | 63 ++++ SeerStackLocalsBrowserWidget.cpp | 214 +++++++++++++ SeerStackLocalsBrowserWidget.h | 33 ++ SeerStackLocalsBrowserWidget.ui | 48 +++ SeerStackManagerWidget.cpp | 55 ++++ SeerStackManagerWidget.h | 33 ++ SeerStackManagerWidget.ui | 43 +++ SeerThreadFramesBrowserWidget.cpp | 208 +++++++++++++ SeerThreadFramesBrowserWidget.h | 31 ++ SeerThreadFramesBrowserWidget.ui | 93 ++++++ SeerThreadIdsBrowserWidget.cpp | 111 +++++++ SeerThreadIdsBrowserWidget.h | 32 ++ SeerThreadIdsBrowserWidget.ui | 33 ++ SeerThreadManagerWidget.cpp | 48 +++ SeerThreadManagerWidget.h | 30 ++ SeerThreadManagerWidget.ui | 43 +++ SeerTildeEqualAmpersandLogWidget.cpp | 86 ++++++ SeerTildeEqualAmpersandLogWidget.h | 15 + SeerTildeLogWidget.cpp | 32 ++ SeerTildeLogWidget.h | 15 + SeerUtl.cpp | 238 +++++++++++++++ SeerUtl.h | 18 ++ SeerVariableLoggerBrowserWidget.cpp | 169 +++++++++++ SeerVariableLoggerBrowserWidget.h | 31 ++ SeerVariableLoggerBrowserWidget.ui | 93 ++++++ SeerVariableManagerWidget.cpp | 54 ++++ SeerVariableManagerWidget.h | 33 ++ SeerVariableManagerWidget.ui | 43 +++ SeerVariableTrackerBrowserWidget.cpp | 241 +++++++++++++++ SeerVariableTrackerBrowserWidget.h | 38 +++ SeerVariableTrackerBrowserWidget.ui | 88 ++++++ SeerWatchpointCreateDialog.cpp | 79 +++++ SeerWatchpointCreateDialog.h | 34 +++ SeerWatchpointCreateDialog.ui | 129 ++++++++ SeerWatchpointsBrowserWidget.cpp | 435 +++++++++++++++++++++++++++ SeerWatchpointsBrowserWidget.h | 40 +++ SeerWatchpointsBrowserWidget.ui | 190 ++++++++++++ test_utl.cpp | 47 +++ 52 files changed, 4352 insertions(+) create mode 100644 SeerSharedLibraryBrowserWidget.cpp create mode 100644 SeerSharedLibraryBrowserWidget.h create mode 100644 SeerSharedLibraryBrowserWidget.ui create mode 100644 SeerSourceBrowserWidget.cpp create mode 100644 SeerSourceBrowserWidget.h create mode 100644 SeerSourceBrowserWidget.ui create mode 100644 SeerSourceLibraryManagerWidget.cpp create mode 100644 SeerSourceLibraryManagerWidget.h create mode 100644 SeerSourceLibraryManagerWidget.ui create mode 100644 SeerStackArgumentsBrowserWidget.cpp create mode 100644 SeerStackArgumentsBrowserWidget.h create mode 100644 SeerStackArgumentsBrowserWidget.ui create mode 100644 SeerStackFramesBrowserWidget.cpp create mode 100644 SeerStackFramesBrowserWidget.h create mode 100644 SeerStackFramesBrowserWidget.ui create mode 100644 SeerStackLocalsBrowserWidget.cpp create mode 100644 SeerStackLocalsBrowserWidget.h create mode 100644 SeerStackLocalsBrowserWidget.ui create mode 100644 SeerStackManagerWidget.cpp create mode 100644 SeerStackManagerWidget.h create mode 100644 SeerStackManagerWidget.ui create mode 100644 SeerThreadFramesBrowserWidget.cpp create mode 100644 SeerThreadFramesBrowserWidget.h create mode 100644 SeerThreadFramesBrowserWidget.ui create mode 100644 SeerThreadIdsBrowserWidget.cpp create mode 100644 SeerThreadIdsBrowserWidget.h create mode 100644 SeerThreadIdsBrowserWidget.ui create mode 100644 SeerThreadManagerWidget.cpp create mode 100644 SeerThreadManagerWidget.h create mode 100644 SeerThreadManagerWidget.ui create mode 100644 SeerTildeEqualAmpersandLogWidget.cpp create mode 100644 SeerTildeEqualAmpersandLogWidget.h create mode 100644 SeerTildeLogWidget.cpp create mode 100644 SeerTildeLogWidget.h create mode 100644 SeerUtl.cpp create mode 100644 SeerUtl.h create mode 100644 SeerVariableLoggerBrowserWidget.cpp create mode 100644 SeerVariableLoggerBrowserWidget.h create mode 100644 SeerVariableLoggerBrowserWidget.ui create mode 100644 SeerVariableManagerWidget.cpp create mode 100644 SeerVariableManagerWidget.h create mode 100644 SeerVariableManagerWidget.ui create mode 100644 SeerVariableTrackerBrowserWidget.cpp create mode 100644 SeerVariableTrackerBrowserWidget.h create mode 100644 SeerVariableTrackerBrowserWidget.ui create mode 100644 SeerWatchpointCreateDialog.cpp create mode 100644 SeerWatchpointCreateDialog.h create mode 100644 SeerWatchpointCreateDialog.ui create mode 100644 SeerWatchpointsBrowserWidget.cpp create mode 100644 SeerWatchpointsBrowserWidget.h create mode 100644 SeerWatchpointsBrowserWidget.ui create mode 100644 test_utl.cpp diff --git a/SeerSharedLibraryBrowserWidget.cpp b/SeerSharedLibraryBrowserWidget.cpp new file mode 100644 index 0000000..f7c3adc --- /dev/null +++ b/SeerSharedLibraryBrowserWidget.cpp @@ -0,0 +1,178 @@ +#include "SeerSharedLibraryBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include +#include +#include +#include + +SeerSharedLibraryBrowserWidget::SeerSharedLibraryBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + sharedLibrarySearchLineEdit->setPlaceholderText("Search..."); + sharedLibrarySearchLineEdit->setClearButtonEnabled(true); + sharedLibraryTreeWidget->resizeColumnToContents(0); + sharedLibraryTreeWidget->resizeColumnToContents(1); + sharedLibraryTreeWidget->resizeColumnToContents(2); + sharedLibraryTreeWidget->resizeColumnToContents(3); + sharedLibraryTreeWidget->resizeColumnToContents(4); + sharedLibraryTreeWidget->resizeColumnToContents(5); + sharedLibraryTreeWidget->clear(); + sharedLibraryTreeWidget->setSortingEnabled(false); + + // Connect things. + QObject::connect(sharedLibrarySearchLineEdit, &QLineEdit::textChanged, this, &SeerSharedLibraryBrowserWidget::handleSearchLineEdit); +} + +SeerSharedLibraryBrowserWidget::~SeerSharedLibraryBrowserWidget () { +} + +void SeerSharedLibraryBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,shared-libraries=[") && text.endsWith("]")) { + + sharedLibraryTreeWidget->clear(); + sharedLibraryTreeWidget->setSortingEnabled(false); + sharedLibraryTreeWidget->sortByColumn(-1, Qt::AscendingOrder); + + // -file-list-shared-libraries + // ^done,shared-libraries=[ + // {id="/lib/libfoo.so",target-name="/lib/libfoo.so",host-name="/lib/libfoo.so",symbols-loaded="1",thread-group="i1",ranges=[{from="0x72815989",to="0x728162c0"}]}, + // {id="/lib/libbar.so",target-name="/lib/libbar.so",host-name="/lib/libbar.so",symbols-loaded="1",thread-group="i1",ranges=[{from="0x76ee48c0",to="0x76ee9160"}]} + // ] + + QString libraries_text = Seer::parseFirst(text, "shared-libraries=", '[', ']', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << libraries_text; + + QStringList libraries_list = Seer::parse(libraries_text, "", '{', '}', false); + + for ( const auto& entry_text : libraries_list ) { + + QString id_text = Seer::parseFirst(entry_text, "id=", '"', '"', false); + QString target_name_text = Seer::parseFirst(entry_text, "target-name=", '"', '"', false); + QString host_name_text = Seer::parseFirst(entry_text, "host-name=", '"', '"', false); + QString symbols_loaded_text = Seer::parseFirst(entry_text, "symbols-loaded=", '"', '"', false); + QString thread_group_text = Seer::parseFirst(entry_text, "thread-group=", '"', '"', false); + QString ranges_text = Seer::parseFirst(entry_text, "ranges=", '[', ']', false); + + // Add the file to the tree. + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, id_text); + item->setText(1, target_name_text); + item->setText(2, host_name_text); + item->setText(3, symbols_loaded_text); + item->setText(4, thread_group_text); + item->setText(5, ranges_text); + + sharedLibraryTreeWidget->addTopLevelItem(item); + } + + }else{ + // Ignore others. + } + + sharedLibraryTreeWidget->resizeColumnToContents(0); + sharedLibraryTreeWidget->resizeColumnToContents(1); + sharedLibraryTreeWidget->resizeColumnToContents(2); + sharedLibraryTreeWidget->resizeColumnToContents(3); + sharedLibraryTreeWidget->resizeColumnToContents(4); + sharedLibraryTreeWidget->resizeColumnToContents(5); + sharedLibraryTreeWidget->sortByColumn(0, Qt::AscendingOrder); + sharedLibraryTreeWidget->setSortingEnabled(true); + + sharedLibrarySearchLineEdit->clear(); + + QApplication::restoreOverrideCursor(); +} + +void SeerSharedLibraryBrowserWidget::handleSearchLineEdit (const QString& text) { + + // Set everything to a normal font. If there is no search text, unhide everything. + // If there is search text, hide everything so the matching ones can be unhidden later on. + QTreeWidgetItemIterator it(sharedLibraryTreeWidget); + + if (*it) { + + QFont f0 = (*it)->font(0); + + f0.setBold(false); + + if (text == "") { + while (*it) { + (*it)->setHidden(false); // No search text, unhide everything. + (*it)->setFont(0,f0); + ++it; + } + }else{ + while (*it) { + (*it)->setHidden(true); // Has serach text, hide everything. Matching items to be unhidden below. + (*it)->setFont(0,f0); + ++it; + } + } + } + + // Set selected items to a bold font and unhidden. Move to the first match. + if (text != "") { + + QList matches; + + if (text.contains('*')) { + matches = sharedLibraryTreeWidget->findItems(text, Qt::MatchWildcard | Qt::MatchRecursive, 0); + }else{ + matches = sharedLibraryTreeWidget->findItems(text, Qt::MatchStartsWith | Qt::MatchRecursive, 0); + } + + QList::const_iterator it = matches.begin(); + QList::const_iterator e = matches.end(); + + if (it != e) { + + sharedLibraryTreeWidget->setCurrentItem(*it); + + QFont f0 = (*it)->font(0); + + f0.setBold(true); + + while (it != e) { + (*it)->setHidden(false); + (*it)->setFont(0,f0); + it++; + } + } + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text << matches.size(); + } + + sharedLibraryTreeWidget->resizeColumnToContents(0); + sharedLibraryTreeWidget->resizeColumnToContents(1); + sharedLibraryTreeWidget->resizeColumnToContents(2); + sharedLibraryTreeWidget->resizeColumnToContents(3); + sharedLibraryTreeWidget->resizeColumnToContents(4); + sharedLibraryTreeWidget->resizeColumnToContents(5); +} + +void SeerSharedLibraryBrowserWidget::refresh () { + emit refreshSharedLibraryList(); +} + +void SeerSharedLibraryBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/SeerSharedLibraryBrowserWidget.h b/SeerSharedLibraryBrowserWidget.h new file mode 100644 index 0000000..802d57d --- /dev/null +++ b/SeerSharedLibraryBrowserWidget.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include "ui_SeerSharedLibraryBrowserWidget.h" + +class SeerSharedLibraryBrowserWidget : public QWidget, protected Ui::SeerSharedLibraryBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerSharedLibraryBrowserWidget (QWidget* parent = 0); + ~SeerSharedLibraryBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleSearchLineEdit (const QString& text); + void refresh (); + + signals: + void refreshSharedLibraryList (); + + protected: + void showEvent (QShowEvent* event); + + private: +}; + diff --git a/SeerSharedLibraryBrowserWidget.ui b/SeerSharedLibraryBrowserWidget.ui new file mode 100644 index 0000000..79a5f1e --- /dev/null +++ b/SeerSharedLibraryBrowserWidget.ui @@ -0,0 +1,68 @@ + + + SeerSharedLibraryBrowserWidgetForm + + + + 0 + 0 + 730 + 698 + + + + Shared Library Browser + + + + + + 6 + + + + Id + + + + + Target-Name + + + + + Host-Name + + + + + Symbols Loaded + + + + + Thread Group + + + + + Ranges + + + + + + + + Search in the list of libraries. "*" is allowed. + + + + + + + + + + + diff --git a/SeerSourceBrowserWidget.cpp b/SeerSourceBrowserWidget.cpp new file mode 100644 index 0000000..5d6a8bd --- /dev/null +++ b/SeerSourceBrowserWidget.cpp @@ -0,0 +1,215 @@ +#include "SeerSourceBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include +#include +#include +#include + +SeerSourceBrowserWidget::SeerSourceBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + sourceSearchLineEdit->setPlaceholderText("Search..."); + sourceSearchLineEdit->setClearButtonEnabled(true); + sourceTreeWidget->resizeColumnToContents(0); + sourceTreeWidget->resizeColumnToContents(1); + sourceTreeWidget->clear(); + sourceTreeWidget->setSortingEnabled(false); + + _sourceFilesItems = 0; + _headerFilesItems = 0; + _miscFilesItems = 0; + + // Connect things. + QObject::connect(sourceTreeWidget, &QTreeWidget::itemDoubleClicked, this, &SeerSourceBrowserWidget::handleItemDoubleClicked); + QObject::connect(sourceSearchLineEdit, &QLineEdit::textChanged, this, &SeerSourceBrowserWidget::handleSearchLineEdit); +} + +SeerSourceBrowserWidget::~SeerSourceBrowserWidget () { +} + +void SeerSourceBrowserWidget::handleText (const QString& text) { + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,files=[") && text.endsWith("]")) { + + sourceTreeWidget->clear(); + sourceTreeWidget->setSortingEnabled(false); + sourceTreeWidget->sortByColumn(-1, Qt::AscendingOrder); + + _sourceFilesItems = new QTreeWidgetItem; + _sourceFilesItems->setText(0, "Source files"); + + _headerFilesItems = new QTreeWidgetItem; + _headerFilesItems->setText(0, "Header files"); + + _miscFilesItems = new QTreeWidgetItem; + _miscFilesItems->setText(0, "Misc files"); + + sourceTreeWidget->addTopLevelItem(_sourceFilesItems); + sourceTreeWidget->addTopLevelItem(_headerFilesItems); + sourceTreeWidget->addTopLevelItem(_miscFilesItems); + + // ^done,files=[ + // {file=\"../sysdeps/x86_64/start.S\",fullname=\"/home/abuild/rpmbuild/BUILD/glibc-2.26/csu/../sysdeps/x86_64/start.S\"}, + // {file=\"helloworld.cpp\",fullname=\"/home/erniep/Development/Peak/src/Seer/helloworld/helloworld.cpp\"} + // ] + + QString files_text = Seer::parseFirst(text, "files=", '[', ']', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << files_text; + + QStringList files_list = Seer::parse(files_text, "", '{', '}', false); + + for ( const auto& entry_text : files_list ) { + + QString file_text = Seer::parseFirst(entry_text, "file=", '"', '"', false); + QString fullname_text = Seer::parseFirst(entry_text, "fullname=", '"', '"', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << file_text << fullname_text; + + // Get information about the file. + QFileInfo fileInfo(fullname_text); + + // Add the file to the tree. + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, file_text); + item->setText(1, fullname_text); + + // Skip fully specified paths. Include files??? + if (file_text[0] == '/') { + continue; + } + + // Skip files that can't be found on disk??? + if (fileInfo.exists() == false) { + continue; + } + + // Look at the filename suffix. + if (fileInfo.suffix() == "cpp" || fileInfo.suffix() == "c" || fileInfo.suffix() == "f" || fileInfo.suffix() == "f90") { + _sourceFilesItems->addChild(item); + + }else if (fileInfo.suffix() == "hpp" || fileInfo.suffix() == "h") { + _headerFilesItems->addChild(item); + + }else{ + _miscFilesItems->addChild(item); + } + } + + _sourceFilesItems->setExpanded(true); + _headerFilesItems->setExpanded(false); + _miscFilesItems->setExpanded(false); + + }else{ + // Ignore others. + } + + sourceTreeWidget->resizeColumnToContents(0); + sourceTreeWidget->resizeColumnToContents(1); + sourceTreeWidget->sortByColumn(0, Qt::AscendingOrder); + sourceTreeWidget->setSortingEnabled(true); + + sourceSearchLineEdit->clear(); + + QApplication::restoreOverrideCursor(); +} + +void SeerSourceBrowserWidget::handleItemDoubleClicked (QTreeWidgetItem* item, int column) { + + Q_UNUSED(column); + + emit selectedFile(item->text(0), item->text(1), 0); +} + +void SeerSourceBrowserWidget::handleSearchLineEdit (const QString& text) { + + // Set everything to a normal font. If there is no search text, unhide everything. + // If there is search text, hide everything so the matching ones can be unhidden later on. + QTreeWidgetItemIterator it(sourceTreeWidget); + + if (*it) { + + QFont f0 = (*it)->font(0); + QFont f1 = (*it)->font(1); + + f0.setBold(false); + f1.setBold(false); + + if (text == "") { + while (*it) { + (*it)->setHidden(false); // No search text, unhide everything. + (*it)->setFont(0,f0); + (*it)->setFont(1,f1); + ++it; + } + }else{ + while (*it) { + (*it)->setHidden(true); // Has serach text, hide everything. Matching items to be unhidden below. + (*it)->setFont(0,f0); + (*it)->setFont(1,f1); + ++it; + } + } + } + + // Set selected items to a bold font and unhidden. Move to the first match. + if (text != "") { + + _sourceFilesItems->setHidden(false); + _headerFilesItems->setHidden(false); + _miscFilesItems->setHidden(false); + + QList matches; + + if (text.contains('*')) { + matches = sourceTreeWidget->findItems(text, Qt::MatchWildcard | Qt::MatchRecursive, 0); + }else{ + matches = sourceTreeWidget->findItems(text, Qt::MatchStartsWith | Qt::MatchRecursive, 0); + } + + QList::const_iterator it = matches.begin(); + QList::const_iterator e = matches.end(); + + if (it != e) { + + sourceTreeWidget->setCurrentItem(*it); + + QFont f0 = (*it)->font(0); + QFont f1 = (*it)->font(1); + + f0.setBold(true); + f1.setBold(true); + + while (it != e) { + if (*it != _sourceFilesItems && *it != _headerFilesItems && *it != _miscFilesItems) { + (*it)->setHidden(false); + (*it)->setFont(0,f0); + (*it)->setFont(1,f1); + } + it++; + } + } + + _sourceFilesItems->setExpanded(true); + _headerFilesItems->setExpanded(false); + _miscFilesItems->setExpanded(false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text << matches.size(); + } + + sourceTreeWidget->resizeColumnToContents(0); + sourceTreeWidget->resizeColumnToContents(1); +} + +void SeerSourceBrowserWidget::refresh () { + emit refreshSourceList(); +} + diff --git a/SeerSourceBrowserWidget.h b/SeerSourceBrowserWidget.h new file mode 100644 index 0000000..f845a3f --- /dev/null +++ b/SeerSourceBrowserWidget.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include "ui_SeerSourceBrowserWidget.h" + +class SeerSourceBrowserWidget : public QWidget, protected Ui::SeerSourceBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerSourceBrowserWidget (QWidget* parent = 0); + ~SeerSourceBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleItemDoubleClicked (QTreeWidgetItem* item, int column); + void handleSearchLineEdit (const QString& text); + void refresh (); + + signals: + void refreshSourceList (); + void selectedFile (QString file, QString fullname, int lineno); + + protected: + private: + QTreeWidgetItem* _sourceFilesItems; + QTreeWidgetItem* _headerFilesItems; + QTreeWidgetItem* _miscFilesItems; +}; + diff --git a/SeerSourceBrowserWidget.ui b/SeerSourceBrowserWidget.ui new file mode 100644 index 0000000..ff80b6c --- /dev/null +++ b/SeerSourceBrowserWidget.ui @@ -0,0 +1,48 @@ + + + SeerSourceBrowserWidgetForm + + + + 0 + 0 + 730 + 698 + + + + Source Browser + + + + + + 2 + + + + File + + + + + Full Name + + + + + + + + Search in the list of files. "*" is allowed. + + + + + + + + + + + diff --git a/SeerSourceLibraryManagerWidget.cpp b/SeerSourceLibraryManagerWidget.cpp new file mode 100644 index 0000000..3cb1889 --- /dev/null +++ b/SeerSourceLibraryManagerWidget.cpp @@ -0,0 +1,48 @@ +#include "SeerSourceLibraryManagerWidget.h" +#include +#include +#include + +SeerSourceLibraryManagerWidget::SeerSourceLibraryManagerWidget (QWidget* parent) : QWidget(parent) { + + // Initialize private data + + // Setup UI + setupUi(this); + + // Setup the widgets + tabWidget->setMovable(true); + tabWidget->setTabsClosable(false); + + _sourceBrowserWidget = new SeerSourceBrowserWidget(this); + _sharedLibraryBrowserWidget = new SeerSharedLibraryBrowserWidget(this); + + tabWidget->addTab(_sourceBrowserWidget, "Source"); + tabWidget->addTab(_sharedLibraryBrowserWidget, "Libraries"); + + QToolButton* refreshToolButton = new QToolButton(tabWidget); + refreshToolButton->setIcon(QIcon::fromTheme("view-refresh")); + refreshToolButton->setToolTip("Refresh the source/libraries information."); + tabWidget->setCornerWidget(refreshToolButton, Qt::TopRightCorner); + + // Connect things. + QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerSourceLibraryManagerWidget::handleRefreshToolButtonClicked); +} + +SeerSourceLibraryManagerWidget::~SeerSourceLibraryManagerWidget () { +} + +SeerSourceBrowserWidget* SeerSourceLibraryManagerWidget::sourceBrowserWidget () { + return _sourceBrowserWidget; +} + +SeerSharedLibraryBrowserWidget* SeerSourceLibraryManagerWidget::sharedLibraryBrowserWidget () { + return _sharedLibraryBrowserWidget; +} + +void SeerSourceLibraryManagerWidget::handleRefreshToolButtonClicked () { + + sourceBrowserWidget()->refresh(); + sharedLibraryBrowserWidget()->refresh(); +} + diff --git a/SeerSourceLibraryManagerWidget.h b/SeerSourceLibraryManagerWidget.h new file mode 100644 index 0000000..2805043 --- /dev/null +++ b/SeerSourceLibraryManagerWidget.h @@ -0,0 +1,30 @@ +#pragma once + +#include "SeerSourceBrowserWidget.h" +#include "SeerSharedLibraryBrowserWidget.h" + +#include + +#include "ui_SeerSourceLibraryManagerWidget.h" + +class SeerSourceLibraryManagerWidget : public QWidget, protected Ui::SeerSourceLibraryManagerWidgetForm { + + Q_OBJECT + + public: + explicit SeerSourceLibraryManagerWidget (QWidget* parent = 0); + ~SeerSourceLibraryManagerWidget (); + + SeerSourceBrowserWidget* sourceBrowserWidget (); + SeerSharedLibraryBrowserWidget* sharedLibraryBrowserWidget (); + + signals: + public slots: + private slots: + void handleRefreshToolButtonClicked (); + + private: + SeerSourceBrowserWidget* _sourceBrowserWidget; + SeerSharedLibraryBrowserWidget* _sharedLibraryBrowserWidget; +}; + diff --git a/SeerSourceLibraryManagerWidget.ui b/SeerSourceLibraryManagerWidget.ui new file mode 100644 index 0000000..4fa8e07 --- /dev/null +++ b/SeerSourceLibraryManagerWidget.ui @@ -0,0 +1,43 @@ + + + SeerSourceLibraryManagerWidgetForm + + + + 0 + 0 + 854 + 571 + + + + Form + + + + 0 + + + 0 + + + + + Source/Libraries + + + + + + -1 + + + + + + + + + + + diff --git a/SeerStackArgumentsBrowserWidget.cpp b/SeerStackArgumentsBrowserWidget.cpp new file mode 100644 index 0000000..ae022f8 --- /dev/null +++ b/SeerStackArgumentsBrowserWidget.cpp @@ -0,0 +1,136 @@ +#include "SeerStackArgumentsBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include + +SeerStackArgumentsBrowserWidget::SeerStackArgumentsBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + argumentsTreeWidget->setSortingEnabled(false); + argumentsTreeWidget->resizeColumnToContents(0); // level + argumentsTreeWidget->resizeColumnToContents(1); // Name + argumentsTreeWidget->resizeColumnToContents(2); // Value + + argumentsTreeWidget->clear(); + + // Connect things. +} + +SeerStackArgumentsBrowserWidget::~SeerStackArgumentsBrowserWidget () { +} + +void SeerStackArgumentsBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,stack-args=[") && text.endsWith("]")) { + + argumentsTreeWidget->clear(); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << newtext; + + // ^done,stack-args=[ + // frame={level="0",args=[ + // {name="message",value="\"Hello, World!\""} + // ]}, + // frame={level="1",args=[ + // {name="argc",value="1"}, + // {name="argv",value="0x7fffffffd5b8"} + // ]} + // ] + + // "level=\"0\",args=[{name=\"message\",value=\"\\\"Hello, World!\\\"\"}]", + // "level=\"1\",args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value=\"0x7fffffffd5b8\"}]" + + QStringList frame_list = Seer::parse(text, "frame=", '{', '}', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << frame_list; + + for ( const auto& frame_text : frame_list ) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << frame_text; + + QString level_text = Seer::parseFirst(frame_text, "level=", '"', '"', false); + QString args_text = Seer::parseFirst(frame_text, "args=", '[', ']', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << level_text; + //qDebug() << __PRETTY_FUNCTION__ << ":" << args_text; + + if (level_text == "" || args_text == "") { + break; + } + + QStringList namevalue_list = Seer::parse(args_text, "", '{', '}', false); + + if (namevalue_list.size() == 0) { + break; + } + + // Add the level to the tree. + QTreeWidgetItem* topItem = new QTreeWidgetItem; + topItem->setText(0, level_text); + + argumentsTreeWidget->addTopLevelItem(topItem); + + // Get the argument names and values for the level. + for ( const auto& namevalue_text : namevalue_list ) { + + QString name_text = Seer::parseFirst(namevalue_text, "name=", '"', '"', false); + QString value_text = Seer::parseFirst(namevalue_text, "value=", '"', '"', false); + + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(1, name_text); // Set the name and value. Don't set the level. + item->setText(2, Seer::filterEscapes(value_text)); + + topItem->addChild(item); + } + + // Expand all items for the level. + argumentsTreeWidget->expandItem(topItem); + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + argumentsTreeWidget->clear(); + + }else{ + // Ignore others. + } + + argumentsTreeWidget->resizeColumnToContents(0); + argumentsTreeWidget->resizeColumnToContents(1); + argumentsTreeWidget->resizeColumnToContents(2); + + QApplication::restoreOverrideCursor(); +} + +void SeerStackArgumentsBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + refresh(); +} + +void SeerStackArgumentsBrowserWidget::refresh () { + emit refreshStackArguments(); +} + +void SeerStackArgumentsBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/SeerStackArgumentsBrowserWidget.h b/SeerStackArgumentsBrowserWidget.h new file mode 100644 index 0000000..fde02b7 --- /dev/null +++ b/SeerStackArgumentsBrowserWidget.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include "ui_SeerStackArgumentsBrowserWidget.h" + +class SeerStackArgumentsBrowserWidget : public QWidget, protected Ui::SeerStackArgumentsBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerStackArgumentsBrowserWidget (QWidget* parent = 0); + ~SeerStackArgumentsBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleStoppingPointReached (); + void refresh (); + + signals: + void refreshStackArguments (); + + protected: + void showEvent (QShowEvent* event); + + private: +}; + diff --git a/SeerStackArgumentsBrowserWidget.ui b/SeerStackArgumentsBrowserWidget.ui new file mode 100644 index 0000000..9e2f457 --- /dev/null +++ b/SeerStackArgumentsBrowserWidget.ui @@ -0,0 +1,43 @@ + + + SeerStackArgumentsBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + 3 + + + + Level + + + + + Name + + + + + Value + + + + + + + + + diff --git a/SeerStackFramesBrowserWidget.cpp b/SeerStackFramesBrowserWidget.cpp new file mode 100644 index 0000000..c4bd4fb --- /dev/null +++ b/SeerStackFramesBrowserWidget.cpp @@ -0,0 +1,158 @@ +#include "SeerStackFramesBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include + +SeerStackFramesBrowserWidget::SeerStackFramesBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + _previousStackFrameText = ""; + + // Setup the widgets + stackTreeWidget->setSortingEnabled(false); + stackTreeWidget->resizeColumnToContents(0); // level + stackTreeWidget->resizeColumnToContents(1); // addr + stackTreeWidget->resizeColumnToContents(2); // func + stackTreeWidget->resizeColumnToContents(3); // file + stackTreeWidget->resizeColumnToContents(4); // fullname + stackTreeWidget->resizeColumnToContents(5); // line + stackTreeWidget->resizeColumnToContents(6); // arch + + /* + stackTreeWidget->setColumnHidden(1, true); // ??? Hide or have a config to hide/show columns. + stackTreeWidget->setColumnHidden(6, true); + */ + + stackTreeWidget->clear(); + + // Connect things. + QObject::connect(stackTreeWidget, &QTreeWidget::itemDoubleClicked, this, &SeerStackFramesBrowserWidget::handleItemDoubleClicked); +} + +SeerStackFramesBrowserWidget::~SeerStackFramesBrowserWidget () { +} + +void SeerStackFramesBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,stack=[") && text.endsWith("]")) { + + // ^done,stack=[ + // frame={level=\"0\",addr=\"0x0000000000400d72\",func=\"function1\",file=\"function1.cpp\",fullname=\"/home/erniep/Development/Peak/src/Seer/helloworld/function1.cpp\",line=\"7\",arch=\"i386:x86-64\"}, + // frame={level=\"1\",addr=\"0x0000000000400cc3\",func=\"main\",file=\"helloworld.cpp\",fullname=\"/home/erniep/Development/Peak/src/Seer/helloworld/helloworld.cpp\",line=\"14\",arch=\"i386:x86-64\"} + // ] + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + // If we are simply moving up and down the stack (via frame-select) then the text won't change. + // If it is different, reconstruct the tree. Select the first frame. + if (text != _previousStackFrameText) { + + _previousStackFrameText = text; + + stackTreeWidget->clear(); + + QString stack_text = Seer::parseFirst(text, "stack=", '[', ']', false); + + if (stack_text != "") { + + // Parse through the frame list and set the current lines that are in the frame list. + QStringList frame_list = Seer::parse(text, "frame=", '{', '}', false); + + for ( const auto& frame_text : frame_list ) { + + QString level_text = Seer::parseFirst(frame_text, "level=", '"', '"', false); + QString addr_text = Seer::parseFirst(frame_text, "addr=", '"', '"', false); + QString func_text = Seer::parseFirst(frame_text, "func=", '"', '"', false); + QString file_text = Seer::parseFirst(frame_text, "file=", '"', '"', false); + QString fullname_text = Seer::parseFirst(frame_text, "fullname=", '"', '"', false); + QString line_text = Seer::parseFirst(frame_text, "line=", '"', '"', false); + QString arch_text = Seer::parseFirst(frame_text, "arch=", '"', '"', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << file_text << fullname_text; + + // Add the frame to the tree. + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, level_text); + item->setText(1, addr_text); + item->setText(2, func_text); + item->setText(3, file_text); + item->setText(4, fullname_text); + item->setText(5, line_text); + item->setText(6, arch_text); + + stackTreeWidget->addTopLevelItem(item); + } + } + + // Select the first frame level. + // ??? Perhaps show the current frame level somehow. + stackTreeWidget->clearSelection(); + + QList matches = stackTreeWidget->findItems("0", 0); + if (matches.size() > 0) { + stackTreeWidget->setCurrentItem(matches.first()); + } + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + stackTreeWidget->clear(); + + }else{ + // Ignore others. + } + + stackTreeWidget->resizeColumnToContents(0); + stackTreeWidget->resizeColumnToContents(1); + stackTreeWidget->resizeColumnToContents(2); + stackTreeWidget->resizeColumnToContents(3); + stackTreeWidget->resizeColumnToContents(4); + stackTreeWidget->resizeColumnToContents(5); + stackTreeWidget->resizeColumnToContents(6); + + QApplication::restoreOverrideCursor(); +} + +void SeerStackFramesBrowserWidget::handleItemDoubleClicked (QTreeWidgetItem* item, int column) { + + Q_UNUSED(column); + + int lineno = item->text(5).toInt(); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << "Emit selectedFile and selectedFrame"; + + emit selectedFile(item->text(3), item->text(4), lineno); + emit selectedFrame(item->text(0).toInt()); +} + +void SeerStackFramesBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + refresh(); +} + +void SeerStackFramesBrowserWidget::refresh () { + emit refreshStackFrames(); +} + +void SeerStackFramesBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/SeerStackFramesBrowserWidget.h b/SeerStackFramesBrowserWidget.h new file mode 100644 index 0000000..75ef9ff --- /dev/null +++ b/SeerStackFramesBrowserWidget.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include "ui_SeerStackFramesBrowserWidget.h" + +class SeerStackFramesBrowserWidget : public QWidget, protected Ui::SeerStackFramesBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerStackFramesBrowserWidget (QWidget* parent = 0); + ~SeerStackFramesBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleItemDoubleClicked (QTreeWidgetItem* item, int column); + void handleStoppingPointReached (); + void refresh (); + + signals: + void refreshStackFrames (); + void selectedFile (QString file, QString fullname, int lineno); + void selectedFrame (int frameno); + + protected: + void showEvent (QShowEvent* event); + + private: + QString _previousStackFrameText; +}; + diff --git a/SeerStackFramesBrowserWidget.ui b/SeerStackFramesBrowserWidget.ui new file mode 100644 index 0000000..077f28a --- /dev/null +++ b/SeerStackFramesBrowserWidget.ui @@ -0,0 +1,63 @@ + + + SeerStackFramesBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + 7 + + + + Level + + + + + Address + + + + + Function + + + + + File + + + + + Fullname + + + + + Line + + + + + Arch + + + + + + + + + diff --git a/SeerStackLocalsBrowserWidget.cpp b/SeerStackLocalsBrowserWidget.cpp new file mode 100644 index 0000000..48ab51e --- /dev/null +++ b/SeerStackLocalsBrowserWidget.cpp @@ -0,0 +1,214 @@ +#include "SeerStackLocalsBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include + +SeerStackLocalsBrowserWidget::SeerStackLocalsBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + localsTreeWidget->setSortingEnabled(false); + localsTreeWidget->resizeColumnToContents(0); // name + localsTreeWidget->resizeColumnToContents(1); // arg + localsTreeWidget->resizeColumnToContents(2); // value + localsTreeWidget->resizeColumnToContents(3); // used + + localsTreeWidget->setColumnHidden(3, true); // Hide the 'used' column. + + localsTreeWidget->clear(); + + _frameNumber = 0; + + // Connect things. + QObject::connect(localsTreeWidget, &QTreeWidget::itemCollapsed, this, &SeerStackLocalsBrowserWidget::handleItemCollapsed); + QObject::connect(localsTreeWidget, &QTreeWidget::itemExpanded, this, &SeerStackLocalsBrowserWidget::handleItemExpanded); +} + +SeerStackLocalsBrowserWidget::~SeerStackLocalsBrowserWidget () { +} + +void SeerStackLocalsBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,variables=[") && text.endsWith("]")) { + + // Mark each entry initially as "unused". + // Later, some will be marked as "reused" or "new". Then the "unused" ones will + // be deleted. + QTreeWidgetItemIterator it(localsTreeWidget); + while (*it) { + (*it)->setText(3, "unused"); + ++it; + } + + // ^done,variables=[ + // {name=\"message\",arg=\"1\",value=\"\\\"Hello, World!\\\"\"}, + // {name=\"something\",arg=\"1\",value=\"\\\"Hello, World!\\\"\"} + // ] + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + QString frame_text = Seer::parseFirst(text, "variables=", '[', ']', false); + + QStringList variable_list = Seer::parse(frame_text, "", '{', '}', false); + + for ( const auto& variable_text : variable_list ) { + + QString name_text = Seer::parseFirst(variable_text, "name=", '"', '"', false); + QString arg_text = Seer::parseFirst(variable_text, "arg=", '"', '"', false); + QString value_text = Seer::parseFirst(variable_text, "value=", '"', '"', false); + + // Instead of creating a new tree each time, we will reuse existing items, if they are there. + // This allows the expanded items to remain expanded. + QList matches = localsTreeWidget->findItems(name_text, Qt::MatchExactly, 0); + + // No matches. So can't reuse. Add the new entry. + if (matches.size() == 0) { + + // Add a complex entry to the tree. + if (value_text.startsWith("{") && value_text.endsWith("}")) { + + QTreeWidgetItem* topItem = new QTreeWidgetItem; + topItem->setText(0, name_text); + topItem->setText(1, ""); + topItem->setText(2, ""); + topItem->setText(3, "new"); + + localsTreeWidget->addTopLevelItem(topItem); + + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, name_text); + item->setText(1, arg_text); + item->setText(2, Seer::filterEscapes(value_text)); + item->setText(3, "new"); + + topItem->addChild(item); + + // Add the simple entry to the tree. + }else{ + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, name_text); + item->setText(1, arg_text); + item->setText(2, Seer::filterEscapes(value_text)); + item->setText(3, "new"); + + localsTreeWidget->addTopLevelItem(item); + } + + // Found a match. Reuse it. + }else{ + + QTreeWidgetItem* item = matches.takeFirst(); + + // Add a complex entry to the tree. + if (value_text.startsWith("{") && value_text.endsWith("}")) { + // Complex entries have a child. Reuse one or create one. + QTreeWidgetItem* child; + + if (item->childCount() > 0) { + child = item->child(0); + + }else{ + child = new QTreeWidgetItem; + item->addChild(item); + } + + item->setText(0, name_text); + item->setText(1, ""); + item->setText(2, ""); + item->setText(3, "reused"); + + child->setText(0, name_text); + child->setText(1, arg_text); + child->setText(2, Seer::filterEscapes(value_text)); + child->setText(3, "reused"); + + // Add the simple entry to the tree. + }else{ + // Simple entries don't have children. Delete them. + if (item->childCount() > 0) { + QList children = item->takeChildren(); + + qDeleteAll(children); + } + + item->setText(0, name_text); + item->setText(1, arg_text); + item->setText(2, Seer::filterEscapes(value_text)); + item->setText(3, "reused"); + } + } + } + + // At this point, there are some new entries, some reused entries, and some unused ones. + // Delete the unused ones. They are obsolete. + QList matches = localsTreeWidget->findItems("unused", Qt::MatchExactly, 3); + + qDeleteAll(matches); + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + localsTreeWidget->clear(); + + }else{ + // Ignore others. + } + + localsTreeWidget->resizeColumnToContents(0); + localsTreeWidget->resizeColumnToContents(1); + localsTreeWidget->resizeColumnToContents(2); + localsTreeWidget->resizeColumnToContents(3); + + QApplication::restoreOverrideCursor(); +} + +void SeerStackLocalsBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + refresh(); +} + +void SeerStackLocalsBrowserWidget::refresh () { + emit refreshStackLocals(); +} + +void SeerStackLocalsBrowserWidget::handleItemExpanded (QTreeWidgetItem* item) { + + Q_UNUSED(item); + + localsTreeWidget->resizeColumnToContents(0); + localsTreeWidget->resizeColumnToContents(1); + localsTreeWidget->resizeColumnToContents(2); + localsTreeWidget->resizeColumnToContents(3); +} + +void SeerStackLocalsBrowserWidget::handleItemCollapsed (QTreeWidgetItem* item) { + + Q_UNUSED(item); + + localsTreeWidget->resizeColumnToContents(0); + localsTreeWidget->resizeColumnToContents(1); + localsTreeWidget->resizeColumnToContents(2); + localsTreeWidget->resizeColumnToContents(3); +} + +void SeerStackLocalsBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/SeerStackLocalsBrowserWidget.h b/SeerStackLocalsBrowserWidget.h new file mode 100644 index 0000000..26b7985 --- /dev/null +++ b/SeerStackLocalsBrowserWidget.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "ui_SeerStackLocalsBrowserWidget.h" + +class SeerStackLocalsBrowserWidget : public QWidget, protected Ui::SeerStackLocalsBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerStackLocalsBrowserWidget (QWidget* parent = 0); + ~SeerStackLocalsBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleStoppingPointReached (); + void refresh (); + + protected slots: + void handleItemExpanded (QTreeWidgetItem* item); + void handleItemCollapsed (QTreeWidgetItem* item); + + signals: + void refreshStackLocals (); + + protected: + void showEvent (QShowEvent* event); + + private: + int _frameNumber; +}; + diff --git a/SeerStackLocalsBrowserWidget.ui b/SeerStackLocalsBrowserWidget.ui new file mode 100644 index 0000000..6f009e0 --- /dev/null +++ b/SeerStackLocalsBrowserWidget.ui @@ -0,0 +1,48 @@ + + + SeerStackLocalsBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + 4 + + + + Name + + + + + Arg + + + + + Value + + + + + Used + + + + + + + + + diff --git a/SeerStackManagerWidget.cpp b/SeerStackManagerWidget.cpp new file mode 100644 index 0000000..03bee61 --- /dev/null +++ b/SeerStackManagerWidget.cpp @@ -0,0 +1,55 @@ +#include "SeerStackManagerWidget.h" +#include +#include +#include + +SeerStackManagerWidget::SeerStackManagerWidget (QWidget* parent) : QWidget(parent) { + + // Initialize private data + + // Setup UI + setupUi(this); + + // Setup the widgets + tabWidget->setMovable(true); + tabWidget->setTabsClosable(false); + + _stackFramesBrowserWidget = new SeerStackFramesBrowserWidget(this); + _stackArgumentsBrowserWidget = new SeerStackArgumentsBrowserWidget(this); + _stackLocalsBrowserWidget = new SeerStackLocalsBrowserWidget(this); + + tabWidget->addTab(_stackFramesBrowserWidget, "Frames"); + tabWidget->addTab(_stackArgumentsBrowserWidget, "Arguments"); + tabWidget->addTab(_stackLocalsBrowserWidget, "Locals"); + + QToolButton* refreshToolButton = new QToolButton(tabWidget); + refreshToolButton->setIcon(QIcon::fromTheme("view-refresh")); + refreshToolButton->setToolTip("Refresh the stack information."); + tabWidget->setCornerWidget(refreshToolButton, Qt::TopRightCorner); + + // Connect things. + QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerStackManagerWidget::handleRefreshToolButtonClicked); +} + +SeerStackManagerWidget::~SeerStackManagerWidget () { +} + +SeerStackFramesBrowserWidget* SeerStackManagerWidget::stackFramesBrowserWidget () { + return _stackFramesBrowserWidget; +} + +SeerStackArgumentsBrowserWidget* SeerStackManagerWidget::stackArgumentsBrowserWidget () { + return _stackArgumentsBrowserWidget; +} + +SeerStackLocalsBrowserWidget* SeerStackManagerWidget::stackLocalsBrowserWidget () { + return _stackLocalsBrowserWidget; +} + +void SeerStackManagerWidget::handleRefreshToolButtonClicked () { + + stackFramesBrowserWidget()->refresh(); + stackArgumentsBrowserWidget()->refresh(); + stackLocalsBrowserWidget()->refresh(); +} + diff --git a/SeerStackManagerWidget.h b/SeerStackManagerWidget.h new file mode 100644 index 0000000..f4fe3d7 --- /dev/null +++ b/SeerStackManagerWidget.h @@ -0,0 +1,33 @@ +#pragma once + +#include "SeerStackFramesBrowserWidget.h" +#include "SeerStackArgumentsBrowserWidget.h" +#include "SeerStackLocalsBrowserWidget.h" + +#include + +#include "ui_SeerStackManagerWidget.h" + +class SeerStackManagerWidget : public QWidget, protected Ui::SeerStackManagerWidgetForm { + + Q_OBJECT + + public: + explicit SeerStackManagerWidget (QWidget* parent = 0); + ~SeerStackManagerWidget (); + + SeerStackFramesBrowserWidget* stackFramesBrowserWidget (); + SeerStackArgumentsBrowserWidget* stackArgumentsBrowserWidget (); + SeerStackLocalsBrowserWidget* stackLocalsBrowserWidget (); + + signals: + public slots: + private slots: + void handleRefreshToolButtonClicked (); + + private: + SeerStackFramesBrowserWidget* _stackFramesBrowserWidget; + SeerStackArgumentsBrowserWidget* _stackArgumentsBrowserWidget; + SeerStackLocalsBrowserWidget* _stackLocalsBrowserWidget; +}; + diff --git a/SeerStackManagerWidget.ui b/SeerStackManagerWidget.ui new file mode 100644 index 0000000..b728898 --- /dev/null +++ b/SeerStackManagerWidget.ui @@ -0,0 +1,43 @@ + + + SeerStackManagerWidgetForm + + + + 0 + 0 + 854 + 571 + + + + Form + + + + 0 + + + 0 + + + + + Stack Info + + + + + + -1 + + + + + + + + + + + diff --git a/SeerThreadFramesBrowserWidget.cpp b/SeerThreadFramesBrowserWidget.cpp new file mode 100644 index 0000000..86c2116 --- /dev/null +++ b/SeerThreadFramesBrowserWidget.cpp @@ -0,0 +1,208 @@ +#include "SeerThreadFramesBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include + +SeerThreadFramesBrowserWidget::SeerThreadFramesBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + threadTreeWidget->setSortingEnabled(false); + threadTreeWidget->resizeColumnToContents(0); // id + threadTreeWidget->resizeColumnToContents(1); // target-id + threadTreeWidget->resizeColumnToContents(2); // name + threadTreeWidget->resizeColumnToContents(3); // level + threadTreeWidget->resizeColumnToContents(4); // addr + threadTreeWidget->resizeColumnToContents(5); // func + threadTreeWidget->resizeColumnToContents(6); // args + threadTreeWidget->resizeColumnToContents(7); // file + threadTreeWidget->resizeColumnToContents(8); // fullname + threadTreeWidget->resizeColumnToContents(9); // line + threadTreeWidget->resizeColumnToContents(10); // arch + threadTreeWidget->resizeColumnToContents(11); // state + threadTreeWidget->resizeColumnToContents(12); // core + + threadTreeWidget->clear(); + + // Connect things. + QObject::connect(threadTreeWidget, &QTreeWidget::itemDoubleClicked, this, &SeerThreadFramesBrowserWidget::handleItemDoubleClicked); +} + +SeerThreadFramesBrowserWidget::~SeerThreadFramesBrowserWidget () { +} + +void SeerThreadFramesBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,threads=[")) { + + // + // ^done,threads=[ + // { + // id=\"1\", + // target-id=\"Thread 0x7ffff7fbd740 (LWP 22356)\", + // name=\"hellothreads\", + // thread={ + // level=\"0\", + // addr=\"0x00000000004006bf\", + // func=\"main\", + // args=[], + // file=\"hellothreads.cpp\", + // fullname=\"/home/erniep/Development/Peak/src/Seer/hellothreads/hellothreads.cpp\", + // line=\"27\", + // arch=\"i386:x86-64\" + // }, + // state=\"stopped\", + // core=\"0\" + // }, + // + // { + // id=\"2\", + // target-id=\"Thread 0x7ffff6ed1700 (LWP 22370)\", + // details=\"Exiting\", + // name=\"hellothreads\", + // frame={ + // level=\"0\", + // addr=\"0x00007ffff6f0b1c3\", + // func=\"__longjmp\", + // args=[], + // from=\"/lib64/libc.so.6\", + // arch=\"i386:x86-64\" + // }, + // state=\"stopped\", + // core=\"6\" + // } + // ], + // current-thread-id=\"1\" + // + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + threadTreeWidget->clear(); + + QString threads_text = Seer::parseFirst(text, "threads=", '[', ']', false); + QString currentthreadid_text = Seer::parseFirst(text, "current-thread-id=", '"', '"', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << threads_text; + + if (threads_text != "") { + + QStringList threads_list = Seer::parse(threads_text, "", '{', '}', false); + + for ( const auto& thread_text : threads_list ) { + QString id_text = Seer::parseFirst(thread_text, "id=", '"', '"', false); + QString targetid_text = Seer::parseFirst(thread_text, "target-id=", '"', '"', false); + QString name_text = Seer::parseFirst(thread_text, "name=", '"', '"', false); + + QString frame_text = Seer::parseFirst(thread_text, "frame=", '{', '}', false); + QString level_text = Seer::parseFirst(frame_text, "level=", '"', '"', false); + QString addr_text = Seer::parseFirst(frame_text, "addr=", '"', '"', false); + QString func_text = Seer::parseFirst(frame_text, "func=", '"', '"', false); + QString args_text = Seer::parseFirst(frame_text, "args=", '[', ']', false); + QString file_text = Seer::parseFirst(frame_text, "file=", '"', '"', false); + QString fullname_text = Seer::parseFirst(frame_text, "fullname=", '"', '"', false); + QString line_text = Seer::parseFirst(frame_text, "line=", '"', '"', false); + QString arch_text = Seer::parseFirst(frame_text, "arch=", '"', '"', false); + + QString state_text = Seer::parseFirst(thread_text, "state=", '"', '"', false); + QString core_text = Seer::parseFirst(thread_text, "core=", '"', '"', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << file_text << fullname_text; + + // Add the frame to the tree. + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, id_text); + item->setText(1, targetid_text); + item->setText(2, name_text); + item->setText(3, level_text); + item->setText(4, addr_text); + item->setText(5, func_text); + item->setText(6, args_text); + item->setText(7, file_text); + item->setText(8, fullname_text); + item->setText(9, line_text); + item->setText(10, arch_text); + item->setText(11, state_text); + item->setText(12, core_text); + + threadTreeWidget->addTopLevelItem(item); + } + + // Select the current thread id. + threadTreeWidget->clearSelection(); + + QList matches = threadTreeWidget->findItems(currentthreadid_text, 0); + if (matches.size() > 0) { + threadTreeWidget->setCurrentItem(matches.first()); + } + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + threadTreeWidget->clear(); + + }else{ + // Ignore others. + } + + threadTreeWidget->resizeColumnToContents(0); + threadTreeWidget->resizeColumnToContents(1); + threadTreeWidget->resizeColumnToContents(2); + threadTreeWidget->resizeColumnToContents(3); + threadTreeWidget->resizeColumnToContents(4); + threadTreeWidget->resizeColumnToContents(5); + //threadTreeWidget->resizeColumnToContents(6); // Don't resize. + threadTreeWidget->resizeColumnToContents(7); + threadTreeWidget->resizeColumnToContents(8); + threadTreeWidget->resizeColumnToContents(9); + threadTreeWidget->resizeColumnToContents(10); + threadTreeWidget->resizeColumnToContents(11); + threadTreeWidget->resizeColumnToContents(12); + + QApplication::restoreOverrideCursor(); +} + +void SeerThreadFramesBrowserWidget::handleItemDoubleClicked (QTreeWidgetItem* item, int column) { + + Q_UNUSED(column); + + int lineno = item->text(9).toInt(); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << "Emit selectedFile and selectedFrame"; + + emit selectedFile(item->text(7), item->text(8), lineno); + + // Comment out this signal. The frame number is always 0 from gdb. Why? + //emit selectedFrame(item->text(3).toInt()); +} + +void SeerThreadFramesBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + refresh(); +} + +void SeerThreadFramesBrowserWidget::refresh () { + emit refreshThreadFrames(); +} + +void SeerThreadFramesBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/SeerThreadFramesBrowserWidget.h b/SeerThreadFramesBrowserWidget.h new file mode 100644 index 0000000..e90ded2 --- /dev/null +++ b/SeerThreadFramesBrowserWidget.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include "ui_SeerThreadFramesBrowserWidget.h" + +class SeerThreadFramesBrowserWidget : public QWidget, protected Ui::SeerThreadFramesBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerThreadFramesBrowserWidget (QWidget* parent = 0); + ~SeerThreadFramesBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleItemDoubleClicked (QTreeWidgetItem* item, int column); + void handleStoppingPointReached (); + void refresh (); + + signals: + void refreshThreadFrames (); + void selectedFile (QString file, QString fullname, int lineno); + void selectedFrame (int frameno); + + protected: + void showEvent (QShowEvent* event); + + private: +}; + diff --git a/SeerThreadFramesBrowserWidget.ui b/SeerThreadFramesBrowserWidget.ui new file mode 100644 index 0000000..8398b6d --- /dev/null +++ b/SeerThreadFramesBrowserWidget.ui @@ -0,0 +1,93 @@ + + + SeerThreadFramesBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + 13 + + + + Thread Id + + + + + Target Id + + + + + Name + + + + + Level + + + + + Address + + + + + Function + + + + + Arguments + + + + + File + + + + + Fullname + + + + + Line + + + + + Arch + + + + + State + + + + + Core + + + + + + + + + diff --git a/SeerThreadIdsBrowserWidget.cpp b/SeerThreadIdsBrowserWidget.cpp new file mode 100644 index 0000000..4e7daaa --- /dev/null +++ b/SeerThreadIdsBrowserWidget.cpp @@ -0,0 +1,111 @@ +#include "SeerThreadIdsBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include + +SeerThreadIdsBrowserWidget::SeerThreadIdsBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + idsTreeWidget->setSortingEnabled(false); + idsTreeWidget->resizeColumnToContents(0); // id + + idsTreeWidget->clear(); + + // Connect things. + QObject::connect(idsTreeWidget, &QTreeWidget::itemDoubleClicked, this, &SeerThreadIdsBrowserWidget::handleItemDoubleClicked); +} + +SeerThreadIdsBrowserWidget::~SeerThreadIdsBrowserWidget () { +} + +void SeerThreadIdsBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,thread-ids={")) { + + QString newtext = Seer::filterEscapes(text); // Filter escaped characters. + + // ^done,thread-ids={ + // thread-id=\"1\", + // thread-id=\"2\" + // }, + // current-thread-id=\"1\", + // number-of-threads=\"2\" + + idsTreeWidget->clear(); + + QString threadids_text = Seer::parseFirst(newtext, "thread-ids=", '{', '}', false); + QStringList threadids_list = Seer::parse(threadids_text, "thread-id=", '"', '"', false); + QString currentthreadid_text = Seer::parseFirst(newtext, "current-thread-id=", '"', '"', false); + + // Add the thread-ids. + for ( const auto& threadid_text : threadids_list ) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << threadid_text; + + // Add the frame to the tree. + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, threadid_text); + + idsTreeWidget->addTopLevelItem(item); + } + + // Clear the selection and select the one for the current thread-id. + idsTreeWidget->clearSelection(); + + QList matches = idsTreeWidget->findItems(currentthreadid_text, 0); + if (matches.size() > 0) { + idsTreeWidget->setCurrentItem(matches.first()); + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + idsTreeWidget->clear(); + + }else{ + // Ignore others. + } + + idsTreeWidget->resizeColumnToContents(0); + + QApplication::restoreOverrideCursor(); +} + +void SeerThreadIdsBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + refresh(); +} + +void SeerThreadIdsBrowserWidget::handleItemDoubleClicked (QTreeWidgetItem* item, int column) { + + Q_UNUSED(column); + + emit selectedThread(item->text(0).toInt()); +} + +void SeerThreadIdsBrowserWidget::refresh () { + emit refreshThreadIds(); +} + +void SeerThreadIdsBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/SeerThreadIdsBrowserWidget.h b/SeerThreadIdsBrowserWidget.h new file mode 100644 index 0000000..25aa63e --- /dev/null +++ b/SeerThreadIdsBrowserWidget.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include "ui_SeerThreadIdsBrowserWidget.h" + +class SeerThreadIdsBrowserWidget : public QWidget, protected Ui::SeerThreadIdsBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerThreadIdsBrowserWidget (QWidget* parent = 0); + ~SeerThreadIdsBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleItemDoubleClicked (QTreeWidgetItem* item, int column); + void handleStoppingPointReached (); + void refresh (); + + protected slots: + + signals: + void refreshThreadIds (); + void selectedThread (int threadid); + + protected: + void showEvent (QShowEvent* event); + + private: +}; + diff --git a/SeerThreadIdsBrowserWidget.ui b/SeerThreadIdsBrowserWidget.ui new file mode 100644 index 0000000..feba56f --- /dev/null +++ b/SeerThreadIdsBrowserWidget.ui @@ -0,0 +1,33 @@ + + + SeerThreadIdsBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + 1 + + + + Id + + + + + + + + + diff --git a/SeerThreadManagerWidget.cpp b/SeerThreadManagerWidget.cpp new file mode 100644 index 0000000..c6031aa --- /dev/null +++ b/SeerThreadManagerWidget.cpp @@ -0,0 +1,48 @@ +#include "SeerThreadManagerWidget.h" +#include +#include +#include + +SeerThreadManagerWidget::SeerThreadManagerWidget (QWidget* parent) : QWidget(parent) { + + // Initialize private data + + // Setup UI + setupUi(this); + + // Setup the widgets + tabWidget->setMovable(true); + tabWidget->setTabsClosable(false); + + _threadIdsBrowserWidget = new SeerThreadIdsBrowserWidget(this); + _threadFramesBrowserWidget = new SeerThreadFramesBrowserWidget(this); + + tabWidget->addTab(_threadIdsBrowserWidget, "Ids"); + tabWidget->addTab(_threadFramesBrowserWidget, "Frames"); + + QToolButton* refreshToolButton = new QToolButton(tabWidget); + refreshToolButton->setIcon(QIcon::fromTheme("view-refresh")); + refreshToolButton->setToolTip("Refresh the thread information."); + tabWidget->setCornerWidget(refreshToolButton, Qt::TopRightCorner); + + // Connect things. + QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerThreadManagerWidget::handleRefreshToolButtonClicked); +} + +SeerThreadManagerWidget::~SeerThreadManagerWidget () { +} + +SeerThreadIdsBrowserWidget* SeerThreadManagerWidget::threadIdsBrowserWidget () { + return _threadIdsBrowserWidget; +} + +SeerThreadFramesBrowserWidget* SeerThreadManagerWidget::threadFramesBrowserWidget () { + return _threadFramesBrowserWidget; +} + +void SeerThreadManagerWidget::handleRefreshToolButtonClicked () { + + threadIdsBrowserWidget()->refresh(); + threadFramesBrowserWidget()->refresh(); +} + diff --git a/SeerThreadManagerWidget.h b/SeerThreadManagerWidget.h new file mode 100644 index 0000000..3834652 --- /dev/null +++ b/SeerThreadManagerWidget.h @@ -0,0 +1,30 @@ +#pragma once + +#include "SeerThreadIdsBrowserWidget.h" +#include "SeerThreadFramesBrowserWidget.h" + +#include + +#include "ui_SeerThreadManagerWidget.h" + +class SeerThreadManagerWidget : public QWidget, protected Ui::SeerThreadManagerWidgetForm { + + Q_OBJECT + + public: + explicit SeerThreadManagerWidget (QWidget* parent = 0); + ~SeerThreadManagerWidget (); + + SeerThreadIdsBrowserWidget* threadIdsBrowserWidget (); + SeerThreadFramesBrowserWidget* threadFramesBrowserWidget (); + + signals: + public slots: + private slots: + void handleRefreshToolButtonClicked (); + + private: + SeerThreadIdsBrowserWidget* _threadIdsBrowserWidget; + SeerThreadFramesBrowserWidget* _threadFramesBrowserWidget; +}; + diff --git a/SeerThreadManagerWidget.ui b/SeerThreadManagerWidget.ui new file mode 100644 index 0000000..dbc64fb --- /dev/null +++ b/SeerThreadManagerWidget.ui @@ -0,0 +1,43 @@ + + + SeerThreadManagerWidgetForm + + + + 0 + 0 + 854 + 571 + + + + Form + + + + 0 + + + 0 + + + + + Thread Info + + + + + + -1 + + + + + + + + + + + diff --git a/SeerTildeEqualAmpersandLogWidget.cpp b/SeerTildeEqualAmpersandLogWidget.cpp new file mode 100644 index 0000000..88c5729 --- /dev/null +++ b/SeerTildeEqualAmpersandLogWidget.cpp @@ -0,0 +1,86 @@ +#include "SeerTildeEqualAmpersandLogWidget.h" +#include "SeerUtl.h" +#include +#include + +SeerTildeEqualAmpersandLogWidget::SeerTildeEqualAmpersandLogWidget (QWidget* parent) : SeerLogWidget(parent) { +} + +SeerTildeEqualAmpersandLogWidget::~SeerTildeEqualAmpersandLogWidget () { +} + +void SeerTildeEqualAmpersandLogWidget::processText (const QString& text) { + + QString str; + + // Remove leading "~" + // ~"For help, type "help". + // " + if (text.front() == '~') { + + str = text.mid(1); + + // Remove leading """ + if (str.front() == '"') { + str = str.mid(1); + } + + // Remove trailing """ + if (str.back() == '"') { + str.chop(1); + } + + // Remove trailing "\n" + if (str.back() == '\n') { + str.chop(1); + } + + + // Remove leading "=" + }else if (text.front() == '=') { + + str = text.mid(1); + + + // Remove leading "&" + // &"p name + // " + }else if (text.front() == '&') { + + str = text.mid(1); + + // Remove leading """ + if (str.front() == '"') { + str = str.mid(1); + } + + // Remove trailing """ + if (str.back() == '"') { + str.chop(1); + } + + // Remove trailing "\n" + if (str.back() == '\n') { + str.chop(1); + } + + + // Use string as it is. + }else{ + str = text; + } + + // Filter escape characters. + str = Seer::filterEscapes(str); + + // Remove trailing "\n" + while (str.back() == '\n') { + str.chop(1); + break; + } + + // Write the string to the log. + textEdit->append(str); + textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum()); +} + diff --git a/SeerTildeEqualAmpersandLogWidget.h b/SeerTildeEqualAmpersandLogWidget.h new file mode 100644 index 0000000..2e2eeb5 --- /dev/null +++ b/SeerTildeEqualAmpersandLogWidget.h @@ -0,0 +1,15 @@ +#pragma once + +#include "SeerLogWidget.h" + +class SeerTildeEqualAmpersandLogWidget : public SeerLogWidget { + + Q_OBJECT + + public: + explicit SeerTildeEqualAmpersandLogWidget (QWidget* parent = 0); + ~SeerTildeEqualAmpersandLogWidget (); + + void processText (const QString& text); +}; + diff --git a/SeerTildeLogWidget.cpp b/SeerTildeLogWidget.cpp new file mode 100644 index 0000000..2c0aa7b --- /dev/null +++ b/SeerTildeLogWidget.cpp @@ -0,0 +1,32 @@ +#include "SeerTildeLogWidget.h" +#include "SeerUtl.h" +#include + +SeerTildeLogWidget::SeerTildeLogWidget (QWidget* parent) : SeerLogWidget(parent) { +} + +SeerTildeLogWidget::~SeerTildeLogWidget () { +} + +void SeerTildeLogWidget::processText (const QString& text) { + + QString str = text.mid(1); // Remove leading "~" + + if (str.front() == '"') { // Remove leading """ + str = str.mid(1); + } + + if (str.back() == '"') { // Remove trailing """ + str.chop(1); + } + + str = Seer::filterEscapes(str); + + if (str.back() == '\n') { // Remove trailing "\n" + str.chop(1); + } + + textEdit->append(str); + textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum()); +} + diff --git a/SeerTildeLogWidget.h b/SeerTildeLogWidget.h new file mode 100644 index 0000000..55920c0 --- /dev/null +++ b/SeerTildeLogWidget.h @@ -0,0 +1,15 @@ +#pragma once + +#include "SeerLogWidget.h" + +class SeerTildeLogWidget : public SeerLogWidget { + + Q_OBJECT + + public: + explicit SeerTildeLogWidget (QWidget* parent = 0); + ~SeerTildeLogWidget (); + + void processText (const QString& text); +}; + diff --git a/SeerUtl.cpp b/SeerUtl.cpp new file mode 100644 index 0000000..6c2a82f --- /dev/null +++ b/SeerUtl.cpp @@ -0,0 +1,238 @@ +#include "SeerUtl.h" +#include + +namespace Seer { + + QString filterEscapes (const QString& str) { + + QString tmp = str; + + tmp.replace("\\r", "\r"); + tmp.replace("\\n", "\n"); + tmp.replace("\\t", "\t"); + tmp.replace("\\\"", "\""); + + return tmp; + } + + QStringList parse (const QString& str, const QString& search, QChar startBracket, QChar endBracket, bool includeSearchString) { + + // + // str = "^done,stack-args=[frame={level=\"0\",args=[{name=\"message\",value=\"\\\"Hello, World!\\\"\"}]},frame={level=\"1\",args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value=\"0x7fffffffd5b8\"}]}]" + // search = "frame=" + // startBracket = '{' + // endBracket = '}' + // + + QStringList list; + QString searchWord = search + startBracket; // 'frame={' + int from = 0; + + while (1) { + + // Look for the next occurance of the search word. + int index = str.indexOf(searchWord,from); + if (index < 0) { // If not found, we're done. + break; + } + + // Set things up to look for the matching end bracket. + int start = index + search.size() + 1; // Positioned after 'frame={' + int end = start + 1; + int bracketLevel = 1; // Start with one start bracket already encountered. + + for (end=start; end= 0) { + if (str[end-1] == '\\') { + escaped = true; + } + } + + if (startBracket != endBracket) { // Do this test if brackets are not the same character. + if (str[end] == startBracket && escaped == false) { // Encountered another start bracket, increment the level. + bracketLevel++; + } + } + + if (str[end] == endBracket && escaped == false) { // Encountered an end bracket, decrement the level. + bracketLevel--; + + if (bracketLevel == 0) { // If the level is 0, we're done. + break; + } + } + } + + // Are things valid? + if (bracketLevel != 0) { // No matching bracket. + break; + } + + if (end > str.size()) { // The end is past the end of the string. + break; + } + + // Build the string. + QString tmp; + + if (includeSearchString) { + tmp = search + startBracket + str.mid(start, end-start) + endBracket; + }else{ + tmp = str.mid(start, end-start); + } + + // Add it to the list. + list.append(tmp); + + // Go back for more + from = end + 1; + } + + // Return the list. + return list; + } + + QString parseFirst (const QString& str, const QString& search, QChar startBracket, QChar endBracket, bool includeSearchString) { + + QStringList list = Seer::parse(str, search, startBracket, endBracket, includeSearchString); + + if (list.size() == 0) { + return QString(); + } + + return list.front(); + } + + QString parseFirst (const QString& str, const QString& search, bool includeSearchString) { + + QString match; + + // Look for the next occurance of the search word. + int index = str.indexOf(search,0); + if (index < 0) { // If not found, we're done. + return match; + } + + // Set things up to look for the matching end bracket. + int start = index + search.size() + 0; // Position after 'msg=' + int end = str.size(); + + if (includeSearchString) { + match = search + str.mid(start, end-start); + }else{ + match = str.mid(start, end-start); + } + + return match; + } + + QString filterBookEnds (const QString& str, QChar startBracket, QChar endBracket) { + + // If the string starts with and ends with the bracket characters, then return + // the text between the brackets. + if (str.startsWith(startBracket) && str.endsWith(endBracket)) { + return str.mid(1,str.size()-2); + } + + // Otherwise, return the orginal string. + return str; + } + + static int Next_ID = 1; + static std::mutex ID_mutex; + + int createID () { + + std::lock_guard guard(ID_mutex); + + int id = Next_ID; + + Next_ID++; + + return id; + } + + unsigned char ebcdicToAscii (unsigned char byte) { + + static const unsigned char ebcdicToAsciiTable[256] = { + 46, 46, 46, 46, 46, 46, 46, 46, // 0 + 46, 46, 46, 46, 46, 46, 46, 46, // 8 + 46, 46, 46, 46, 46, 46, 46, 46, // 16 + 46, 46, 46, 46, 46, 46, 46, 46, // 24 + 46, 46, 46, 46, 46, 46, 46, 46, // 32 + 46, 46, 46, 46, 46, 46, 46, 46, // 40 + 46, 46, 46, 46, 46, 46, 46, 46, // 48 + 46, 46, 46, 46, 46, 46, 46, 46, // 56 + 32, 46, 46, 46, 46, 46, 46, 46, // 64 + 46, 46, 91, 46, 60, 40, 43, 33, // 72 + 38, 46, 46, 46, 46, 46, 46, 46, // 80 + 46, 46, 33, 36, 42, 41, 59, 94, // 88 + 45, 47, 46, 46, 46, 46, 46, 46, // 96 + 46, 46, 124, 44, 37, 95, 62, 63, // 104 + 46, 46, 46, 46, 46, 46, 46, 46, // 112 + 46, 96, 58, 35, 64, 39, 61, 34, // 120 + 46, 97, 98, 99, 100, 101, 102, 103, // 128 + 104, 105, 46, 46, 46, 46, 46, 46, // 136 + 46, 106, 107, 108, 109, 110, 111, 112, // 144 + 113, 114, 46, 46, 46, 46, 46, 46, // 152 + 46, 126, 115, 116, 117, 118, 119, 120, // 160 + 121, 122, 46, 46, 46, 46, 46, 46, // 168 + 46, 46, 46, 46, 46, 46, 46, 46, // 176 + 46, 46, 46, 46, 46, 46, 46, 46, // 184 + 123, 65, 66, 67, 68, 69, 70, 71, // 192 + 72, 73, 46, 46, 46, 46, 46, 46, // 200 + 125, 74, 75, 76, 77, 78, 79, 80, // 208 + 81, 82, 46, 46, 46, 46, 46, 46, // 216 + 92, 46, 83, 84, 85, 86, 87, 88, // 224 + 89, 90, 46, 46, 46, 46, 46, 46, // 232 + 48, 49, 50, 51, 52, 53, 54, 55, // 240 + 56, 57, 46, 46, 46, 46, 46, 46 // 248 + }; + + return ebcdicToAsciiTable[byte]; + } + + unsigned char ucharToAscii (unsigned char byte) { + + static const unsigned char ucharToAsciiTable[256] = { + 46, 46, 46, 46, 46, 46, 46, 46, // 0 + 46, 46, 46, 46, 46, 46, 46, 46, // 8 + 46, 46, 46, 46, 46, 46, 46, 46, // 16 + 46, 46, 46, 46, 46, 46, 46, 46, // 24 + 32, 33, 34, 35, 36, 37, 38, 39, // 32 + 40, 41, 42, 43, 44, 45, 46, 47, // 40 + 48, 49, 50, 51, 52, 53, 54, 55, // 48 + 56, 57, 58, 59, 60, 61, 62, 63, // 56 + 64, 65, 66, 67, 68, 69, 70, 71, // 64 + 72, 73, 74, 75, 76, 77, 78, 79, // 72 + 80, 81, 82, 83, 84, 85, 86, 87, // 80 + 88, 89, 90, 91, 92, 93, 94, 95, // 88 + 96, 97, 98, 99, 100, 101, 102, 103, // 96 + 104, 105, 106, 107, 108, 109, 110, 111, // 104 + 112, 113, 114, 115, 116, 117, 118, 119, // 112 + 120, 121, 122, 123, 124, 125, 126, 46, // 120 + 46, 46, 46, 46, 46, 46, 46, 46, // 128 + 46, 46, 46, 46, 46, 46, 46, 46, // 136 + 46, 46, 46, 46, 46, 46, 46, 46, // 144 + 46, 46, 46, 46, 46, 46, 46, 46, // 152 + 46, 46, 46, 46, 46, 46, 46, 46, // 160 + 46, 46, 46, 46, 46, 46, 46, 46, // 168 + 46, 46, 46, 46, 46, 46, 46, 46, // 176 + 46, 46, 46, 46, 46, 46, 46, 46, // 184 + 46, 46, 46, 46, 46, 46, 46, 46, // 192 + 46, 46, 46, 46, 46, 46, 46, 46, // 200 + 46, 46, 46, 46, 46, 46, 46, 46, // 208 + 46, 46, 46, 46, 46, 46, 46, 46, // 216 + 46, 46, 46, 46, 46, 46, 46, 46, // 224 + 46, 46, 46, 46, 46, 46, 46, 46, // 232 + 46, 46, 46, 46, 46, 46, 46, 46, // 240 + 46, 46, 46, 46, 46, 46, 46, 46 // 248 + }; + + return ucharToAsciiTable[byte]; + } +} + diff --git a/SeerUtl.h b/SeerUtl.h new file mode 100644 index 0000000..d4ee3e9 --- /dev/null +++ b/SeerUtl.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +namespace Seer { + + QString filterEscapes (const QString& str); + QStringList parse (const QString& str, const QString& search, QChar startBracket, QChar endBracket, bool includeSearch); + QString parseFirst (const QString& str, const QString& search, QChar startBracket, QChar endBracket, bool includeSearch); + QString parseFirst (const QString& str, const QString& search, bool includeSearch); + QString filterBookEnds (const QString& str, QChar startBracket, QChar endBracket); + + int createID (); + + unsigned char ebcdicToAscii (unsigned char byte); + unsigned char ucharToAscii (unsigned char byte); +}; + diff --git a/SeerVariableLoggerBrowserWidget.cpp b/SeerVariableLoggerBrowserWidget.cpp new file mode 100644 index 0000000..178bbbb --- /dev/null +++ b/SeerVariableLoggerBrowserWidget.cpp @@ -0,0 +1,169 @@ +#include "SeerVariableLoggerBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include +#include +#include +#include + +SeerVariableLoggerBrowserWidget::SeerVariableLoggerBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + variablesTreeWidget->setSortingEnabled(false); + variablesTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + variablesTreeWidget->resizeColumnToContents(0); // id + variablesTreeWidget->resizeColumnToContents(1); // timestamp + variablesTreeWidget->resizeColumnToContents(2); // name + variablesTreeWidget->resizeColumnToContents(3); // value + + variablesTreeWidget->setColumnHidden(0, true); // Hide the 'number' column. + + variablesTreeWidget->clear(); + + // Connect things. + QObject::connect(this, &SeerVariableLoggerBrowserWidget::evaluateVariableExpression, this, &SeerVariableLoggerBrowserWidget::handleEvaluateVariableExpression); + QObject::connect(variableAddLineEdit, &QLineEdit::returnPressed, this, &SeerVariableLoggerBrowserWidget::handleAddLineEdit); + QObject::connect(variableDeleteToolButton, &QToolButton::clicked, this, &SeerVariableLoggerBrowserWidget::handleDeleteToolButton); + QObject::connect(variableDeleteAllToolButton, &QToolButton::clicked, this, &SeerVariableLoggerBrowserWidget::handleDeleteAllToolButton); +} + +SeerVariableLoggerBrowserWidget::~SeerVariableLoggerBrowserWidget () { +} + +void SeerVariableLoggerBrowserWidget::handleText (const QString& text) { + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.contains(QRegExp("^([0-9]+)\\^done,value="))) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + // "6^done,value=\"\\\"abc\\\"\"" + + QString id_text = text.section('^', 0,0); + QString value_text = Seer::parseFirst(text, "value=", '"', '"', false); + + QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 0); + + if (matches.size() > 0) { + matches.first()->setText(3, Seer::filterEscapes(value_text)); + } + + }else if (text.contains(QRegExp("^([0-9]+)\\^error,msg="))) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + // "1^error,msg=\"No symbol \\\"j\\\" in current context.\"" + + QString id_text = text.section('^', 0,0); + QString msg_text = Seer::parseFirst(text, "msg=", '"', '"', false); + + QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 0); + + if (matches.size() > 0) { + matches.first()->setText(3, Seer::filterEscapes(msg_text)); + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + variablesTreeWidget->clear(); + + }else{ + // Ignore others. + } + + // Resize columns. + variablesTreeWidget->resizeColumnToContents(0); + variablesTreeWidget->resizeColumnToContents(1); + variablesTreeWidget->resizeColumnToContents(2); + variablesTreeWidget->resizeColumnToContents(3); + + // Scroll to the bottom. + QTreeWidgetItem* lastItem = variablesTreeWidget->topLevelItem(variablesTreeWidget->topLevelItemCount()-1); + if (lastItem) { + variablesTreeWidget->scrollToItem(lastItem); + } + + // Set the cursor back. + QApplication::restoreOverrideCursor(); +} + +void SeerVariableLoggerBrowserWidget::handleEvaluateVariableExpression (int expressionid, QString expression) { + + QString id_text = QString::number(expressionid); + + QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 0); + + // Reuse existing item. + if (matches.size() > 0) { + matches.first()->setText(1, QTime::currentTime().toString(Qt::TextDate)); + matches.first()->setText(2, ""); + matches.first()->setText(3, ""); + + // Add new item. + }else{ + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, id_text); + item->setText(1, QTime::currentTime().toString(Qt::TextDate)); + item->setText(2, expression); + item->setText(3, ""); + + variablesTreeWidget->addTopLevelItem(item); + } + + // Resize columns. + variablesTreeWidget->resizeColumnToContents(0); + variablesTreeWidget->resizeColumnToContents(1); + variablesTreeWidget->resizeColumnToContents(2); + variablesTreeWidget->resizeColumnToContents(3); +} + +void SeerVariableLoggerBrowserWidget::handleAddLineEdit () { + + //qDebug() << __PRETTY_FUNCTION__ << ":"; + + QString variable = variableAddLineEdit->text(); + + variableAddLineEdit->clear(); + + if (variable != "") { + + int id = Seer::createID(); + + emit evaluateVariableExpression(id, variable); + } +} + +void SeerVariableLoggerBrowserWidget::handleDeleteToolButton () { + + // Get selected tree items. + QList items = variablesTreeWidget->selectedItems(); + + // Remove them. + for(int i=0; iresizeColumnToContents(0); + variablesTreeWidget->resizeColumnToContents(1); + variablesTreeWidget->resizeColumnToContents(2); + variablesTreeWidget->resizeColumnToContents(3); +} + +void SeerVariableLoggerBrowserWidget::handleDeleteAllToolButton () { + + // Remove all. + variablesTreeWidget->clear(); + + // Resize columns. + variablesTreeWidget->resizeColumnToContents(0); + variablesTreeWidget->resizeColumnToContents(1); + variablesTreeWidget->resizeColumnToContents(2); + variablesTreeWidget->resizeColumnToContents(3); +} + diff --git a/SeerVariableLoggerBrowserWidget.h b/SeerVariableLoggerBrowserWidget.h new file mode 100644 index 0000000..aa9c2ac --- /dev/null +++ b/SeerVariableLoggerBrowserWidget.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include "ui_SeerVariableLoggerBrowserWidget.h" + +class SeerVariableLoggerBrowserWidget : public QWidget, protected Ui::SeerVariableLoggerBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerVariableLoggerBrowserWidget (QWidget* parent = 0); + ~SeerVariableLoggerBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleEvaluateVariableExpression (int expressionid, QString expression); + + private slots: + void handleAddLineEdit (); + void handleDeleteToolButton (); + void handleDeleteAllToolButton (); + + signals: + void evaluateVariableExpression (int expressionid, QString expression); + + protected: + private: + +}; + diff --git a/SeerVariableLoggerBrowserWidget.ui b/SeerVariableLoggerBrowserWidget.ui new file mode 100644 index 0000000..a1379ec --- /dev/null +++ b/SeerVariableLoggerBrowserWidget.ui @@ -0,0 +1,93 @@ + + + SeerVariableLoggerBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + + + Add a variable to the loggger. + + + Enter a variable name + + + true + + + + + + + Delete selected variables. + + + ... + + + + .. + + + + + + + Delete all variables. + + + ... + + + + .. + + + + + + + + + 4 + + + + ID + + + + + Timestamp + + + + + Name + + + + + Value + + + + + + + + + diff --git a/SeerVariableManagerWidget.cpp b/SeerVariableManagerWidget.cpp new file mode 100644 index 0000000..f4cc515 --- /dev/null +++ b/SeerVariableManagerWidget.cpp @@ -0,0 +1,54 @@ +#include "SeerVariableManagerWidget.h" +#include +#include +#include + +SeerVariableManagerWidget::SeerVariableManagerWidget (QWidget* parent) : QWidget(parent) { + + // Initialize private data + + // Setup UI + setupUi(this); + + // Setup the widgets + tabWidget->setMovable(true); + tabWidget->setTabsClosable(false); + + _variableLoggerBrowserWidget = new SeerVariableLoggerBrowserWidget(this); + _variableTrackerBrowserWidget = new SeerVariableTrackerBrowserWidget(this); + _registerValuesBrowserWidget = new SeerRegisterValuesBrowserWidget(this); + + tabWidget->addTab(_variableLoggerBrowserWidget, "Logger"); + tabWidget->addTab(_variableTrackerBrowserWidget, "Tracker"); + tabWidget->addTab(_registerValuesBrowserWidget, "Registers"); + + QToolButton* refreshToolButton = new QToolButton(tabWidget); + refreshToolButton->setIcon(QIcon::fromTheme("view-refresh")); + refreshToolButton->setToolTip("Refresh the variable/register information."); + tabWidget->setCornerWidget(refreshToolButton, Qt::TopRightCorner); + + // Connect things. + QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerVariableManagerWidget::handleRefreshToolButtonClicked); +} + +SeerVariableManagerWidget::~SeerVariableManagerWidget () { +} + +SeerVariableTrackerBrowserWidget* SeerVariableManagerWidget::variableTrackerBrowserWidget () { + return _variableTrackerBrowserWidget; +} + +SeerVariableLoggerBrowserWidget* SeerVariableManagerWidget::variableLoggerBrowserWidget () { + return _variableLoggerBrowserWidget; +} + +SeerRegisterValuesBrowserWidget* SeerVariableManagerWidget::registerValuesBrowserWidget () { + return _registerValuesBrowserWidget; +} + +void SeerVariableManagerWidget::handleRefreshToolButtonClicked () { + + variableTrackerBrowserWidget()->refresh(); + registerValuesBrowserWidget()->refresh(); +} + diff --git a/SeerVariableManagerWidget.h b/SeerVariableManagerWidget.h new file mode 100644 index 0000000..3568b47 --- /dev/null +++ b/SeerVariableManagerWidget.h @@ -0,0 +1,33 @@ +#pragma once + +#include "SeerVariableTrackerBrowserWidget.h" +#include "SeerVariableLoggerBrowserWidget.h" +#include "SeerRegisterValuesBrowserWidget.h" + +#include + +#include "ui_SeerVariableManagerWidget.h" + +class SeerVariableManagerWidget : public QWidget, protected Ui::SeerVariableManagerWidgetForm { + + Q_OBJECT + + public: + explicit SeerVariableManagerWidget (QWidget* parent = 0); + ~SeerVariableManagerWidget (); + + SeerVariableTrackerBrowserWidget* variableTrackerBrowserWidget (); + SeerVariableLoggerBrowserWidget* variableLoggerBrowserWidget (); + SeerRegisterValuesBrowserWidget* registerValuesBrowserWidget (); + + signals: + public slots: + private slots: + void handleRefreshToolButtonClicked (); + + private: + SeerVariableTrackerBrowserWidget* _variableTrackerBrowserWidget; + SeerVariableLoggerBrowserWidget* _variableLoggerBrowserWidget; + SeerRegisterValuesBrowserWidget* _registerValuesBrowserWidget; +}; + diff --git a/SeerVariableManagerWidget.ui b/SeerVariableManagerWidget.ui new file mode 100644 index 0000000..288bfbb --- /dev/null +++ b/SeerVariableManagerWidget.ui @@ -0,0 +1,43 @@ + + + SeerVariableManagerWidgetForm + + + + 0 + 0 + 854 + 571 + + + + Form + + + + 0 + + + 0 + + + + + Variable/Register Info + + + + + + -1 + + + + + + + + + + + diff --git a/SeerVariableTrackerBrowserWidget.cpp b/SeerVariableTrackerBrowserWidget.cpp new file mode 100644 index 0000000..4beee68 --- /dev/null +++ b/SeerVariableTrackerBrowserWidget.cpp @@ -0,0 +1,241 @@ +#include "SeerVariableTrackerBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include +#include +#include +#include + +SeerVariableTrackerBrowserWidget::SeerVariableTrackerBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + variablesTreeWidget->setSortingEnabled(false); + variablesTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + variablesTreeWidget->resizeColumnToContents(0); // id + variablesTreeWidget->resizeColumnToContents(1); // name + variablesTreeWidget->resizeColumnToContents(2); // value + + variablesTreeWidget->setColumnHidden(0, true); // Hide the 'number' column. + + variablesTreeWidget->clear(); + + // Connect things. + QObject::connect(variableAddLineEdit, &QLineEdit::returnPressed, this, &SeerVariableTrackerBrowserWidget::handleAddLineEdit); + QObject::connect(variableDeleteToolButton, &QToolButton::clicked, this, &SeerVariableTrackerBrowserWidget::handleDeleteToolButton); + QObject::connect(variableDeleteAllToolButton, &QToolButton::clicked, this, &SeerVariableTrackerBrowserWidget::handleDeleteAllToolButton); +} + +SeerVariableTrackerBrowserWidget::~SeerVariableTrackerBrowserWidget () { +} + +void SeerVariableTrackerBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,DataExpressionTable={") && text.endsWith("}")) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + // "^done,DataExpressionTable={ + // entry={id=\"1\",expression=\"s\"}, + // entry={id=\"2\",expression=\"v\"}, + // entry={id=\"4\",expression=\"l\"}, + // entry={id=\"5\",expression=\"m\"} + // }" + + variablesTreeWidget->clear(); + + QString frame_text = Seer::parseFirst(text, "DataExpressionTable=", '{', '}', false); + + QStringList entries_list = Seer::parse(frame_text, "entry=", '{', '}', false); + + for (int i=0; isetText(0, id_text); + topItem->setText(1, expression_text); + topItem->setText(2, ""); + + variablesTreeWidget->addTopLevelItem(topItem); + } + + }else if (text.startsWith("^done,DataExpressionAdded={") && text.endsWith("}")) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << "Refresh"; + + // "^done,DataExpressionAdded={ + // id=\"5\", + // expression=\"m\" + // }" + + QString frame_text = Seer::parseFirst(text, "DataExpressionAdded=", '{', '}', false); + + QString id_text = Seer::parseFirst(frame_text, "id=", '"', '"', false); + QString expression_text = Seer::parseFirst(frame_text, "expression=", '"', '"', false); + + QTreeWidgetItem* topItem = new QTreeWidgetItem; + topItem->setText(0, id_text); + topItem->setText(1, expression_text); + topItem->setText(2, ""); + + variablesTreeWidget->addTopLevelItem(topItem); + + }else if (text.startsWith("^done,DataExpressionDeleted={") && text.endsWith("}")) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + // "^done,DataExpressionDeleted={ + // entry={id=\"1\",expression=\"s\"}, + // entry={id=\"3\",expression=\"vb\"} + // }" + + QString frame_text = Seer::parseFirst(text, "DataExpressionDeleted=", '{', '}', false); + + QStringList entries_list = Seer::parse(frame_text, "entry=", '{', '}', false); + + for (int i=0; i matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 0); + + qDeleteAll(matches); + } + + }else if (text.contains(QRegExp("^([0-9]+)\\^done,value="))) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + // "6^done,value=\"\\\"abc\\\"\"" + + QString id_text = text.section('^', 0,0); + QString value_text = Seer::parseFirst(text, "value=", '"', '"', false); + + QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 0); + + if (matches.size() > 0) { + matches.first()->setText(2, Seer::filterEscapes(value_text)); + } + + }else if (text.contains(QRegExp("^([0-9]+)\\^error,msg="))) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + // "1^error,msg=\"No symbol \\\"j\\\" in current context.\"" + + QString id_text = text.section('^', 0,0); + QString msg_text = Seer::parseFirst(text, "msg=", '"', '"', false); + + QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 0); + + if (matches.size() > 0) { + matches.first()->setText(2, Seer::filterEscapes(msg_text)); + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + variablesTreeWidget->clear(); + + }else{ + // Ignore others. + } + + variablesTreeWidget->resizeColumnToContents(0); + variablesTreeWidget->resizeColumnToContents(1); + variablesTreeWidget->resizeColumnToContents(2); + + QApplication::restoreOverrideCursor(); +} + +void SeerVariableTrackerBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + refresh(); +} + +void SeerVariableTrackerBrowserWidget::refresh () { + emit refreshVariableNames(); + emit refreshVariableValues(); +} + +void SeerVariableTrackerBrowserWidget::refreshValues () { + emit refreshVariableValues(); +} + +void SeerVariableTrackerBrowserWidget::handleAddLineEdit () { + + //qDebug() << __PRETTY_FUNCTION__ << ":"; + + QString variable = variableAddLineEdit->text(); + + variableAddLineEdit->clear(); + + if (variable != "") { + emit addVariableExpression(variable); + + // After sending the 'add' signal, schedule a 'refresh' 200ms later. + QTimer::singleShot(200, this, &SeerVariableTrackerBrowserWidget::refresh); + } +} + +void SeerVariableTrackerBrowserWidget::handleDeleteToolButton () { + + //qDebug() << __PRETTY_FUNCTION__ << ":"; + + // Get selected tree items. + QList items = variablesTreeWidget->selectedItems(); + + // Build a string that is a list of variable ids. + QString variableids; + + QList::iterator i; + for (i = items.begin(); i != items.end(); ++i) { + + if (i != items.begin()) { + variableids += " "; + } + + variableids += (*i)->text(0); + } + + // Don't do anything if the list of variables is empty. + if (variableids == "") { + return; + } + + // Send the signal. + emit deleteVariableExpressions(variableids); +} + +void SeerVariableTrackerBrowserWidget::handleDeleteAllToolButton () { + emit deleteVariableExpressions("*"); +} + +void SeerVariableTrackerBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/SeerVariableTrackerBrowserWidget.h b/SeerVariableTrackerBrowserWidget.h new file mode 100644 index 0000000..7c7965a --- /dev/null +++ b/SeerVariableTrackerBrowserWidget.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include "ui_SeerVariableTrackerBrowserWidget.h" + +class SeerVariableTrackerBrowserWidget : public QWidget, protected Ui::SeerVariableTrackerBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerVariableTrackerBrowserWidget (QWidget* parent = 0); + ~SeerVariableTrackerBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleStoppingPointReached (); + void refresh (); + void refreshValues (); + + private slots: + void handleAddLineEdit (); + void handleDeleteToolButton (); + void handleDeleteAllToolButton (); + + signals: + void refreshVariableNames (); + void refreshVariableValues (); + void addVariableExpression (QString expression); + void deleteVariableExpressions (QString expressionids); + + protected: + void showEvent (QShowEvent* event); + + private: + +}; + diff --git a/SeerVariableTrackerBrowserWidget.ui b/SeerVariableTrackerBrowserWidget.ui new file mode 100644 index 0000000..23b7e9d --- /dev/null +++ b/SeerVariableTrackerBrowserWidget.ui @@ -0,0 +1,88 @@ + + + SeerVariableTrackerBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + + + Add a variable to track. + + + Enter a variable name + + + true + + + + + + + Delete selected variables. + + + ... + + + + .. + + + + + + + Delete all variables. + + + ... + + + + .. + + + + + + + + + 3 + + + + ID + + + + + Name + + + + + Value + + + + + + + + + diff --git a/SeerWatchpointCreateDialog.cpp b/SeerWatchpointCreateDialog.cpp new file mode 100644 index 0000000..a54e9c7 --- /dev/null +++ b/SeerWatchpointCreateDialog.cpp @@ -0,0 +1,79 @@ +#include "SeerWatchpointCreateDialog.h" +#include + +SeerWatchpointCreateDialog::SeerWatchpointCreateDialog (QWidget* parent) : QDialog(parent) { + + // Set up the UI. + setupUi(this); + + QButtonGroup* accessTypeButtonGroup = new QButtonGroup(this); + accessTypeButtonGroup->addButton(writeRadioButton); + accessTypeButtonGroup->addButton(readRadioButton); + accessTypeButtonGroup->addButton(readWriteRadioButton); + + // Setup the widgets + setExpression(""); + setReadAccessEnabled(false); + setReadWriteAccessEnabled(false); + setWriteAccessEnabled(true); + + // Connect things. +} + +SeerWatchpointCreateDialog::~SeerWatchpointCreateDialog () { +} + +void SeerWatchpointCreateDialog::setExpression (const QString& text) { + expressionLineEdit->setText(text); +} + +QString SeerWatchpointCreateDialog::expressionText () const { + return expressionLineEdit->text(); +} + +void SeerWatchpointCreateDialog::setReadAccessEnabled (bool flag) { + readRadioButton->setChecked(flag); +} + +void SeerWatchpointCreateDialog::setWriteAccessEnabled (bool flag) { + writeRadioButton->setChecked(flag); +} + +void SeerWatchpointCreateDialog::setReadWriteAccessEnabled (bool flag) { + readWriteRadioButton->setChecked(flag); +} + +bool SeerWatchpointCreateDialog::readAccessEnabled () const { + return readRadioButton->isChecked(); +} + +bool SeerWatchpointCreateDialog::writeAccessEnabled () const { + return writeRadioButton->isChecked(); +} + +bool SeerWatchpointCreateDialog::readWriteAccessEnabled () const { + return readWriteRadioButton->isChecked(); +} + +QString SeerWatchpointCreateDialog::watchpointText () const { + + // Build a watchpoint specification. + QString watchpointParameters; + + if (readAccessEnabled()) { + watchpointParameters += " -r"; + } + + if (readWriteAccessEnabled()) { + watchpointParameters += " -a"; + } + + if (writeAccessEnabled()) { + watchpointParameters += ""; + } + + watchpointParameters += " " + expressionText(); + + return watchpointParameters; +} + diff --git a/SeerWatchpointCreateDialog.h b/SeerWatchpointCreateDialog.h new file mode 100644 index 0000000..28821ee --- /dev/null +++ b/SeerWatchpointCreateDialog.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include "ui_SeerWatchpointCreateDialog.h" + +class SeerWatchpointCreateDialog : public QDialog, protected Ui::SeerWatchpointCreateDialogForm { + + Q_OBJECT + + public: + explicit SeerWatchpointCreateDialog (QWidget* parent = 0); + ~SeerWatchpointCreateDialog (); + + void setExpression (const QString& text); + + QString expressionText () const; + + void setReadAccessEnabled (bool flag); + void setWriteAccessEnabled (bool flag); + void setReadWriteAccessEnabled (bool flag); + + bool readAccessEnabled () const; + bool writeAccessEnabled () const; + bool readWriteAccessEnabled () const; + + QString watchpointText () const; + + public slots: + + private: +}; + diff --git a/SeerWatchpointCreateDialog.ui b/SeerWatchpointCreateDialog.ui new file mode 100644 index 0000000..d44b46d --- /dev/null +++ b/SeerWatchpointCreateDialog.ui @@ -0,0 +1,129 @@ + + + SeerWatchpointCreateDialogForm + + + + 0 + 0 + 348 + 251 + + + + Create a Watchpoint + + + + + + Watchpoint Location + + + + + + + 0 + 0 + + + + Expression + + + + + + + + 100 + 0 + + + + true + + + + + + + + + + Watchpoint Type + + + + + + Read Access + + + + + + + Read/Write Access + + + + + + + Write Access + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SeerWatchpointCreateDialogForm + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SeerWatchpointCreateDialogForm + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SeerWatchpointsBrowserWidget.cpp b/SeerWatchpointsBrowserWidget.cpp new file mode 100644 index 0000000..8d5a9b6 --- /dev/null +++ b/SeerWatchpointsBrowserWidget.cpp @@ -0,0 +1,435 @@ +#include "SeerWatchpointsBrowserWidget.h" +#include "SeerWatchpointCreateDialog.h" +#include "SeerUtl.h" +#include +#include +#include +#include + +SeerWatchpointsBrowserWidget::SeerWatchpointsBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + watchpointsTreeWidget->clear(); + + watchpointsTreeWidget->setSortingEnabled(false); + watchpointsTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + watchpointsTreeWidget->resizeColumnToContents(0); // number + watchpointsTreeWidget->resizeColumnToContents(1); // type + watchpointsTreeWidget->resizeColumnToContents(2); // disp + watchpointsTreeWidget->resizeColumnToContents(3); // enabled + watchpointsTreeWidget->resizeColumnToContents(4); // addr + //watchpointsTreeWidget->resizeColumnToContents(5); // func Too long to show + watchpointsTreeWidget->resizeColumnToContents(6); // file + //watchpointsTreeWidget->resizeColumnToContents(7); // fullname Too long to show + watchpointsTreeWidget->resizeColumnToContents(8); // line + watchpointsTreeWidget->resizeColumnToContents(9); // thread-groups + watchpointsTreeWidget->resizeColumnToContents(10); // times + watchpointsTreeWidget->resizeColumnToContents(11); // original-location / expression + watchpointsTreeWidget->resizeColumnToContents(12); // value + watchpointsTreeWidget->resizeColumnToContents(13); // new value + watchpointsTreeWidget->resizeColumnToContents(14); // used + + watchpointsTreeWidget->setColumnHidden(4, true); // Hide the 'addr' column. + watchpointsTreeWidget->setColumnHidden(5, true); // Hide the 'func' column. + watchpointsTreeWidget->setColumnHidden(14, true); // Hide the 'used' column. + watchpointsTreeWidget->clear(); + + // Connect things. + QObject::connect(watchpointsTreeWidget, &QTreeWidget::itemDoubleClicked, this, &SeerWatchpointsBrowserWidget::handleItemDoubleClicked); + QObject::connect(refreshWatchpointsToolButton, &QToolButton::clicked, this, &SeerWatchpointsBrowserWidget::handleRefreshToolButton); + QObject::connect(addWatchpointToolButton, &QToolButton::clicked, this, &SeerWatchpointsBrowserWidget::handleAddToolButton); + QObject::connect(deleteWatchpointsToolButton, &QToolButton::clicked, this, &SeerWatchpointsBrowserWidget::handleDeleteToolButton); + QObject::connect(enableWatchpointsToolButton, &QToolButton::clicked, this, &SeerWatchpointsBrowserWidget::handleEnableToolButton); + QObject::connect(disableWatchpointsToolButton, &QToolButton::clicked, this, &SeerWatchpointsBrowserWidget::handleDisableToolButton); +} + +SeerWatchpointsBrowserWidget::~SeerWatchpointsBrowserWidget () { +} + +void SeerWatchpointsBrowserWidget::handleText (const QString& text) { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + QApplication::setOverrideCursor(Qt::BusyCursor); + + if (text.startsWith("^done,BreakpointTable={") && text.endsWith("}")) { + + // + // ^done,BreakpointTable={ + // nr_rows="2",nr_cols="6", + // + // hdr=[ + // {width="7",alignment="-1",col_name="number",colhdr="Num"}, + // {width="14",alignment="-1",col_name="type",colhdr="Type"}, + // {width="4",alignment="-1",col_name="disp",colhdr="Disp"}, + // {width="3",alignment="-1",col_name="enabled",colhdr="Enb"}, + // {width="18",alignment="-1",col_name="addr",colhdr="Address"}, + // {width="40",alignment="2",col_name="what",colhdr="What"} + // ], + // + // body=[ + // bkpt={number="2",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400c17",func="main(int, char**)",file="helloworld.cpp",fullname="/home/erniep/Development/Peak/src/Seer/helloworld/helloworld.cpp",line="8",thread-groups=["i1"],times="0",original-location="main"}, + // bkpt={number="3", + // type="breakpoint", + // disp="keep", + // enabled="y", + // addr="0x0000000000400d72", + // func="function1(std::__cxx11::basic_string, std::allocator > const&)", + // file="function1.cpp", + // fullname="/home/erniep/Development/Peak/src/Seer/helloworld/function1.cpp", + // line="7", + // thread-groups=["i1"], + // times="0", + // original-location="function1"} + // ] + // } + // + + QString newtext = Seer::filterEscapes(text); // Filter escaped characters. + + QString body_text = Seer::parseFirst(newtext, "body=", '[', ']', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << body_text; + + // No rows? Just clear the tree. + if (body_text == "") { + watchpointsTreeWidget->clear(); + + // Otherwise, populate it. + }else{ + + // Mark each entry initially as "unused". + // Later, some will be marked as "reused" or "new". Then the "unused" ones will + // be deleted. + QTreeWidgetItemIterator it(watchpointsTreeWidget); + while (*it) { + (*it)->setText(14, "unused"); + ++it; + } + + QStringList bkpt_list = Seer::parse(newtext, "bkpt=", '{', '}', false); + + for ( const auto& bkpt_text : bkpt_list ) { + + QString number_text = Seer::parseFirst(bkpt_text, "number=", '"', '"', false); + QString type_text = Seer::parseFirst(bkpt_text, "type=", '"', '"', false); + QString disp_text = Seer::parseFirst(bkpt_text, "disp=", '"', '"', false); + QString enabled_text = Seer::parseFirst(bkpt_text, "enabled=", '"', '"', false); + QString addr_text = Seer::parseFirst(bkpt_text, "addr=", '"', '"', false); + QString func_text = Seer::parseFirst(bkpt_text, "func=", '"', '"', false); + QString file_text = Seer::parseFirst(bkpt_text, "file=", '"', '"', false); + QString fullname_text = Seer::parseFirst(bkpt_text, "fullname=", '"', '"', false); + QString line_text = Seer::parseFirst(bkpt_text, "line=", '"', '"', false); + QString thread_groups_text = Seer::parseFirst(bkpt_text, "thread-groups=", '[', ']', false); + QString times_text = Seer::parseFirst(bkpt_text, "times=", '"', '"', false); + QString original_location_text = Seer::parseFirst(bkpt_text, "original-location=", '"', '"', false); + + // Only look for 'watchpoint' type break points. + if (type_text != "hw watchpoint" && type_text != "watchpoint" && type_text != "read watchpoint" && type_text != "acc watchpoint") { + continue; + } + + // Instead of creating a new tree each time, we will reuse existing items, if they are there. + // This allows the expanded items to remain expanded. + QList matches = watchpointsTreeWidget->findItems(number_text, Qt::MatchExactly, 0); + + // No matches. So can't reuse. Add the new entry. + if (matches.size() == 0) { + + // Add the level to the tree. + QTreeWidgetItem* topItem = new QTreeWidgetItem; + topItem->setText(0, number_text); + topItem->setText(1, type_text); + topItem->setText(2, disp_text); + topItem->setText(3, enabled_text); + topItem->setText(4, addr_text); + topItem->setText(5, func_text); + topItem->setText(6, file_text); + topItem->setText(7, fullname_text); + topItem->setText(8, line_text); + topItem->setText(9, thread_groups_text); + topItem->setText(10, times_text); + topItem->setText(11, original_location_text); + topItem->setText(12, ""); + topItem->setText(13, ""); + topItem->setText(14, "new"); + + watchpointsTreeWidget->addTopLevelItem(topItem); + + // Found a match. Reuse it. + // But don't overwrite the file, fullname, line, value, and new value. + }else{ + + QTreeWidgetItem* topItem = matches.takeFirst(); + + topItem->setText(0, number_text); + topItem->setText(1, type_text); + topItem->setText(2, disp_text); + topItem->setText(3, enabled_text); + topItem->setText(4, addr_text); + topItem->setText(5, func_text); + //topItem->setText(6, file_text); + //topItem->setText(7, fullname_text); + //topItem->setText(8, line_text); + topItem->setText(9, thread_groups_text); + topItem->setText(10, times_text); + topItem->setText(11, original_location_text); + //topItem->setText(12, ""); + //topItem->setText(13, ""); + topItem->setText(14, "reused"); + } + } + + // At this point, there are some new entries, some reused entries, and some unused ones. + // Delete the unused ones. They are obsolete. + QList matches = watchpointsTreeWidget->findItems("unused", Qt::MatchExactly, 14); + + qDeleteAll(matches); + } + + }else if (text.startsWith("*stopped,reason=\"") || text.startsWith("*stopped,hw-awpt={")) { + + QString reason_text = Seer::parseFirst(text, "reason=", '"', '"', false); + + if (reason_text == "watchpoint-trigger") { + //*stopped,reason="watchpoint-trigger",wpt={number="3",exp="i"},value={old="32767",new="42"},frame={addr="0x0000000000400d79",func="function1",args=[{name="text",value="\"Hello, World!\""}],file="function1.cpp",fullname="/home/erniep/Development/Peak/src/Seer/helloworld/function1.cpp",line="9",arch="i386:x86-64"},thread-id="1",stopped-threads="all",core="0" + + QString wpt_text = Seer::parseFirst(text, "wpt=", '{', '}', false); + QString number_text = Seer::parseFirst(wpt_text, "number=", '"', '"', false); + QString exp_text = Seer::parseFirst(wpt_text, "exp=", '"', '"', false); + QString value_text = Seer::parseFirst(text, "value=", '{', '}', false); + QString old_text = Seer::parseFirst(value_text, "old=", '"', '"', false); + QString new_text = Seer::parseFirst(value_text, "new=", '"', '"', false); + QString frame_text = Seer::parseFirst(text, "frame=", '{', '}', false); + QString file_text = Seer::parseFirst(frame_text, "file=", '"', '"', false); + QString fullname_text = Seer::parseFirst(frame_text, "fullname=", '"', '"', false); + QString line_text = Seer::parseFirst(frame_text, "line=", '"', '"', false); + + // Find watchpoint number in the tree + QList matches = watchpointsTreeWidget->findItems(number_text, Qt::MatchExactly, 0); + if (matches.size() > 0) { + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + QTreeWidgetItem* item = matches.first(); + item->setText(6, file_text); + item->setText(7, fullname_text); + item->setText(8, line_text); + item->setText(12, old_text); + item->setText(13, new_text); + } + + }else if (reason_text == "read-watchpoint-trigger") { + //*stopped,reason="read-watchpoint-trigger",hw-rwpt={number="5",exp="i"},value={value="42"},frame={addr="0x0000000000400d9a",func="function1",args=[{name="text",value="\"Hello, World!\""}],file="function1.cpp",fullname="/home/erniep/Development/Peak/src/Seer/helloworld/function1.cpp",line="11",arch="i386:x86-64"},thread-id="1",stopped-threads="all",core="4" + + QString hwwpt_text = Seer::parseFirst(text, "hw-rwpt=", '{', '}', false); + QString number_text = Seer::parseFirst(hwwpt_text, "number=", '"', '"', false); + QString exp_text = Seer::parseFirst(hwwpt_text, "exp=", '"', '"', false); + QString value_text = Seer::parseFirst(text, "value=", '{', '}', false); + QString value_text2 = Seer::parseFirst(value_text, "value=", '"', '"', false); + QString frame_text = Seer::parseFirst(text, "frame=", '{', '}', false); + QString file_text = Seer::parseFirst(frame_text, "file=", '"', '"', false); + QString fullname_text = Seer::parseFirst(frame_text, "fullname=", '"', '"', false); + QString line_text = Seer::parseFirst(frame_text, "line=", '"', '"', false); + + // Find watchpoint number in the tree + QList matches = watchpointsTreeWidget->findItems(number_text, Qt::MatchExactly, 0); + if (matches.size() > 0) { + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + QTreeWidgetItem* item = matches.first(); + item->setText(6, file_text); + item->setText(7, fullname_text); + item->setText(8, line_text); + item->setText(12, value_text2); + item->setText(13, ""); + } + + }else if (reason_text == "access-watchpoint-trigger") { + //*stopped,reason="access-watchpoint-trigger",hw-awpt={number="3",exp="v"},value={old="1",new="11"},frame={addr="0x000000000040059a",func="bar",args=[{name="v",value="11"}],file="helloonefile.cpp",fullname="/home/erniep/Development/Peak/src/Seer/helloonefile/helloonefile.cpp",line="15",arch="i386:x86-64"},thread-id="1",stopped-threads="all",core="3" + + QString hwawpt_text = Seer::parseFirst(text, "hw-awpt=", '{', '}', false); + QString number_text = Seer::parseFirst(hwawpt_text, "number=", '"', '"', false); + QString exp_text = Seer::parseFirst(hwawpt_text, "exp=", '"', '"', false); + QString value_text = Seer::parseFirst(text, "value=", '{', '}', false); + QString old_text = Seer::parseFirst(value_text, "old=", '"', '"', false); + QString new_text = Seer::parseFirst(value_text, "new=", '"', '"', false); + QString frame_text = Seer::parseFirst(text, "frame=", '{', '}', false); + QString file_text = Seer::parseFirst(frame_text, "file=", '"', '"', false); + QString fullname_text = Seer::parseFirst(frame_text, "fullname=", '"', '"', false); + QString line_text = Seer::parseFirst(frame_text, "line=", '"', '"', false); + + // Find watchpoint number in the tree + QList matches = watchpointsTreeWidget->findItems(number_text, Qt::MatchExactly, 0); + if (matches.size() > 0) { + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + QTreeWidgetItem* item = matches.first(); + item->setText(6, file_text); + item->setText(7, fullname_text); + item->setText(8, line_text); + item->setText(12, old_text); + item->setText(13, new_text); + } + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + // Ignore. + + }else{ + // Ignore others. + } + + watchpointsTreeWidget->resizeColumnToContents(0); + watchpointsTreeWidget->resizeColumnToContents(1); + watchpointsTreeWidget->resizeColumnToContents(2); + watchpointsTreeWidget->resizeColumnToContents(3); + watchpointsTreeWidget->resizeColumnToContents(4); + //watchpointsTreeWidget->resizeColumnToContents(5); + watchpointsTreeWidget->resizeColumnToContents(6); + //watchpointsTreeWidget->resizeColumnToContents(7); + watchpointsTreeWidget->resizeColumnToContents(8); + watchpointsTreeWidget->resizeColumnToContents(9); + watchpointsTreeWidget->resizeColumnToContents(10); + watchpointsTreeWidget->resizeColumnToContents(11); + watchpointsTreeWidget->resizeColumnToContents(12); + watchpointsTreeWidget->resizeColumnToContents(13); + watchpointsTreeWidget->resizeColumnToContents(14); + + QApplication::restoreOverrideCursor(); +} + +void SeerWatchpointsBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + emit refreshWatchpointsList(); +} + +void SeerWatchpointsBrowserWidget::handleItemDoubleClicked (QTreeWidgetItem* item, int column) { + + Q_UNUSED(column); + + int lineno = item->text(8).toInt(); + + emit selectedFile(item->text(6), item->text(7), lineno); +} + +void SeerWatchpointsBrowserWidget::handleRefreshToolButton () { + + emit refreshWatchpointsList(); +} + +void SeerWatchpointsBrowserWidget::handleAddToolButton () { + + SeerWatchpointCreateDialog dlg(this); + + int ret = dlg.exec(); + + if (ret == 0) { + return; + } + + // Build a watchpoint specification. + QString watchpointParameters = dlg.watchpointText(); + + // If nothing, just return. + if (watchpointParameters == "") { + return; + } + + // Otherwise send the command to create the watchpoint. + emit insertWatchpoint(watchpointParameters); +} + +void SeerWatchpointsBrowserWidget::handleDeleteToolButton () { + + // Get selected tree items. + QList items = watchpointsTreeWidget->selectedItems(); + + // Build a string that is a list of watchpoints. + QString watchpoints; + + QList::iterator i; + for (i = items.begin(); i != items.end(); ++i) { + if (i != items.begin()) { + watchpoints += " "; + } + watchpoints += (*i)->text(0); + } + + // Don't do anything if the list of watchpoints is empty. + if (watchpoints == "") { + return; + } + + // Send the signal. + emit deleteWatchpoints(watchpoints); +} + +void SeerWatchpointsBrowserWidget::handleEnableToolButton () { + + // Get selected tree items. + QList items = watchpointsTreeWidget->selectedItems(); + + // Build a string that is a list of watchpoints. + QString watchpoints; + + QList::iterator i; + for (i = items.begin(); i != items.end(); ++i) { + + if (i != items.begin()) { + watchpoints += " "; + } + + watchpoints += (*i)->text(0); + } + + // Don't do anything if the list of watchpoints is empty. + if (watchpoints == "") { + return; + } + + // Send the signal. + emit enableWatchpoints(watchpoints); +} + +void SeerWatchpointsBrowserWidget::handleDisableToolButton () { + + // Get selected tree items. + QList items = watchpointsTreeWidget->selectedItems(); + + // Build a string that is a list of watchpoints. + QString watchpoints; + + QList::iterator i; + for (i = items.begin(); i != items.end(); ++i) { + + if (i != items.begin()) { + watchpoints += " "; + } + + watchpoints += (*i)->text(0); + } + + // Don't do anything if the list of watchpoints is empty. + if (watchpoints == "") { + return; + } + + // Send the signal. + emit disableWatchpoints(watchpoints); +} + +void SeerWatchpointsBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + emit refreshWatchpointsList(); +} + diff --git a/SeerWatchpointsBrowserWidget.h b/SeerWatchpointsBrowserWidget.h new file mode 100644 index 0000000..a4ec70a --- /dev/null +++ b/SeerWatchpointsBrowserWidget.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include "ui_SeerWatchpointsBrowserWidget.h" + +class SeerWatchpointsBrowserWidget : public QWidget, protected Ui::SeerWatchpointsBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerWatchpointsBrowserWidget (QWidget* parent = 0); + ~SeerWatchpointsBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleStoppingPointReached (); + + private slots: + void handleItemDoubleClicked (QTreeWidgetItem* item, int column); + void handleRefreshToolButton (); + void handleAddToolButton (); + void handleDeleteToolButton (); + void handleEnableToolButton (); + void handleDisableToolButton (); + + signals: + void refreshWatchpointsList (); + void deleteWatchpoints (QString watchpoints); + void enableWatchpoints (QString watchpoints); + void disableWatchpoints (QString watchpoints); + void insertWatchpoint (QString watchpoint); + void selectedFile (QString file, QString fullname, int lineno); + + protected: + void showEvent (QShowEvent* event); + + private: +}; + diff --git a/SeerWatchpointsBrowserWidget.ui b/SeerWatchpointsBrowserWidget.ui new file mode 100644 index 0000000..4feb8b0 --- /dev/null +++ b/SeerWatchpointsBrowserWidget.ui @@ -0,0 +1,190 @@ + + + SeerWatchpointsBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + 15 + + + + Number + + + + + Type + + + + + Disposition + + + + + Enabled + + + + + Address + + + + + Function + + + + + File + + + + + Fullname + + + + + Line + + + + + Thread Groups + + + + + Times + + + + + Expression + + + + + Value + + + + + New Value + + + + + Used + + + + + + + + + + Refresh the list of watchpoints. + + + + + + + .. + + + + + + + Add a new watchpoint. + + + ... + + + + .. + + + + + + + Delete selected watchpoints. + + + ... + + + + .. + + + + + + + Enable selected watchpoints. + + + ... + + + + .. + + + + + + + Disable selected watchpoints. + + + ... + + + + .. + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/test_utl.cpp b/test_utl.cpp new file mode 100644 index 0000000..00fe8f5 --- /dev/null +++ b/test_utl.cpp @@ -0,0 +1,47 @@ +#include "SeerUtl.h" +#include +#include +#include +#include +#include + +int main (int argc, char** argv) { + + Q_UNUSED(argc); + Q_UNUSED(argv); + + //QString test = "\"r0\",\"\",\"\",\"r4\""; + QString test = "\"\",\"r0\",\"\",\"r4\""; + QString newtest = Seer::filterEscapes(test); + + std::cout << test.toStdString() << std::endl; + std::cout << newtest.toStdString() << std::endl; + + QStringList list = Seer::parse(test, "", '"', '"', false); + + for ( const auto& name : list ) { + std::cout << name.toStdString() << std::endl; + } + + + QString text = "1^done,value=\"0\""; + + if (text.contains(QRegExp("^([0-9]+)\\^"))) { + qDebug() << text << "matches"; + }else{ + qDebug() << text << "doesn't match"; + } + + QString text2 = "1^done,value=\"\\\"Hello, World!\\\"\""; + + qDebug() << text2; + + QStringList list2 = Seer::parse(text2, "value=", '"', '"', false); + + for ( const auto& name : list2 ) { + std::cout << name.toStdString() << std::endl; + } + + return 0; +} +