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; +} +