From 81a09d3bb4c4acee91519e92fd05a4a929d1628b Mon Sep 17 00:00:00 2001 From: Ernie Pasveer Date: Sun, 30 Mar 2025 14:29:20 -0500 Subject: [PATCH] Fixed a bunch of things. --- src/CMakeLists.txt | 2 + src/SeerGdbWidget.cpp | 22 ++++ src/SeerGdbWidget.h | 1 + src/SeerMemoryVisualizerWidget.cpp | 5 +- src/SeerSkipBrowserWidget.cpp | 29 ++++- src/SeerSkipBrowserWidget.h | 2 + src/SeerSkipBrowserWidget.ui | 17 --- src/SeerSkipCreateDialog.cpp | 92 +++++++++++++ src/SeerSkipCreateDialog.h | 26 ++++ src/SeerSkipCreateDialog.ui | 200 +++++++++++++++++++++++++++++ src/resource.qrc | 1 + src/resources/help/Skips.md | 68 ++++++++++ tests/helloskip/MISkip.py | 15 ++- 13 files changed, 451 insertions(+), 29 deletions(-) create mode 100644 src/SeerSkipCreateDialog.cpp create mode 100644 src/SeerSkipCreateDialog.h create mode 100644 src/SeerSkipCreateDialog.ui create mode 100644 src/resources/help/Skips.md diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cabf40e..18d2b03 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -99,6 +99,7 @@ set(HEADER_FILES SeerTypeBrowserWidget.h SeerStaticBrowserWidget.h SeerSkipBrowserWidget.h + SeerSkipCreateDialog.h SeerSourceSymbolLibraryManagerWidget.h SeerStackArgumentsBrowserWidget.h SeerStackFramesBrowserWidget.h @@ -199,6 +200,7 @@ set(SOURCE_FILES SeerTypeBrowserWidget.cpp SeerStaticBrowserWidget.cpp SeerSkipBrowserWidget.cpp + SeerSkipCreateDialog.cpp SeerSourceSymbolLibraryManagerWidget.cpp SeerStackArgumentsBrowserWidget.cpp SeerStackFramesBrowserWidget.cpp diff --git a/src/SeerGdbWidget.cpp b/src/SeerGdbWidget.cpp index 3cf3644..6fe819a 100644 --- a/src/SeerGdbWidget.cpp +++ b/src/SeerGdbWidget.cpp @@ -231,6 +231,7 @@ SeerGdbWidget::SeerGdbWidget (QWidget* parent) : QWidget(parent) { QObject::connect(sourceLibraryManagerWidget->adaExceptionsBrowserWidget(), &SeerAdaExceptionsBrowserWidget::refreshAdaExceptions, this, &SeerGdbWidget::handleGdbAdaListExceptions); QObject::connect(sourceLibraryManagerWidget->adaExceptionsBrowserWidget(), &SeerAdaExceptionsBrowserWidget::insertCatchpoint, this, &SeerGdbWidget::handleGdbCatchpointInsert); QObject::connect(sourceLibraryManagerWidget->skipBrowserWidget(), &SeerSkipBrowserWidget::refreshSkipList, this, &SeerGdbWidget::handleGdbListSkips); + QObject::connect(sourceLibraryManagerWidget->skipBrowserWidget(), &SeerSkipBrowserWidget::addSkip, this, &SeerGdbWidget::handleGdbAddSkip); QObject::connect(sourceLibraryManagerWidget->skipBrowserWidget(), &SeerSkipBrowserWidget::deleteSkips, this, &SeerGdbWidget::handleGdbDeleteSkips); QObject::connect(sourceLibraryManagerWidget->skipBrowserWidget(), &SeerSkipBrowserWidget::enableSkips, this, &SeerGdbWidget::handleGdbEnableSkips); QObject::connect(sourceLibraryManagerWidget->skipBrowserWidget(), &SeerSkipBrowserWidget::disableSkips, this, &SeerGdbWidget::handleGdbDisableSkips); @@ -2310,6 +2311,27 @@ void SeerGdbWidget::handleGdbListSkips () { handleGdbCommand("-skip-list"); } +void SeerGdbWidget::handleGdbAddSkip (QString skipmode, QString skipparameters) { + + if (executableLaunchMode() == "") { + return; + } + + if (skipmode == "file") { + handleGdbCommand("-skip-create-file " + skipparameters); + }else if (skipmode == "gfile") { + handleGdbCommand("-skip-create-gfile " + skipparameters); + }else if (skipmode == "function") { + handleGdbCommand("-skip-create-function " + skipparameters); + }else if (skipmode == "rfunction") { + handleGdbCommand("-skip-create-rfunction " + skipparameters); + }else{ + return; + } + + handleGdbListSkips(); +} + void SeerGdbWidget::handleGdbDeleteSkips (QString skipids) { if (executableLaunchMode() == "") { diff --git a/src/SeerGdbWidget.h b/src/SeerGdbWidget.h index a097772..d629786 100644 --- a/src/SeerGdbWidget.h +++ b/src/SeerGdbWidget.h @@ -300,6 +300,7 @@ class SeerGdbWidget : public QWidget, protected Ui::SeerGdbWidgetForm { void handleGdbAdaListTasks (); void handleGdbAdaListExceptions (); void handleGdbListSkips (); + void handleGdbAddSkip (QString skipmode, QString skipparameters); void handleGdbDeleteSkips (QString skipids); void handleGdbEnableSkips (QString skipids); void handleGdbDisableSkips (QString skipids); diff --git a/src/SeerMemoryVisualizerWidget.cpp b/src/SeerMemoryVisualizerWidget.cpp index 3ccc658..f78b61f 100644 --- a/src/SeerMemoryVisualizerWidget.cpp +++ b/src/SeerMemoryVisualizerWidget.cpp @@ -408,13 +408,12 @@ void SeerMemoryVisualizerWidget::handlePrintButton () { QPrinter printer; - QPrintDialog* dlg = new QPrintDialog(&printer, this); + QPrintDialog dialog(&printer, this); - if (dlg->exec() != QDialog::Accepted) { + if (dialog.exec() != QDialog::Accepted) { return; } - // Make a copy so we can temporarily add a header. QTextDocument* clone = 0; diff --git a/src/SeerSkipBrowserWidget.cpp b/src/SeerSkipBrowserWidget.cpp index 6bb534e..8e5d718 100644 --- a/src/SeerSkipBrowserWidget.cpp +++ b/src/SeerSkipBrowserWidget.cpp @@ -1,12 +1,8 @@ #include "SeerSkipBrowserWidget.h" +#include "SeerSkipCreateDialog.h" #include "SeerUtl.h" #include #include -#include -#include -#include -#include -#include #include SeerSkipBrowserWidget::SeerSkipBrowserWidget (QWidget* parent) : QWidget(parent) { @@ -26,6 +22,7 @@ SeerSkipBrowserWidget::SeerSkipBrowserWidget (QWidget* parent) : QWidget(parent) skipTreeWidget->setSortingEnabled(false); // Connect things. + QObject::connect(skipAddToolButton, &QToolButton::clicked, this, &SeerSkipBrowserWidget::handleAddToolButton); QObject::connect(skipDeleteToolButton, &QToolButton::clicked, this, &SeerSkipBrowserWidget::handleDeleteToolButton); QObject::connect(skipEnableToolButton, &QToolButton::clicked, this, &SeerSkipBrowserWidget::handleEnableToolButton); QObject::connect(skipDisableToolButton, &QToolButton::clicked, this, &SeerSkipBrowserWidget::handleDisableToolButton); @@ -89,6 +86,28 @@ void SeerSkipBrowserWidget::handleText (const QString& text) { QApplication::restoreOverrideCursor(); } +void SeerSkipBrowserWidget::handleAddToolButton () { + + // Create the dialog. + SeerSkipCreateDialog dialog(this); + + // Execute it. + if (dialog.exec() != QDialog::Accepted) { + return; + } + + // Get result. + QString mode = dialog.skipMode(); + QString parameters = dialog.skipParameters(); + + if (mode == "" || parameters == "") { + return; + } + + // Send the 'add skip' command. + emit addSkip(mode, parameters); +} + void SeerSkipBrowserWidget::handleDeleteToolButton () { // Get selected tree items. diff --git a/src/SeerSkipBrowserWidget.h b/src/SeerSkipBrowserWidget.h index 7c2c167..a4d0b14 100644 --- a/src/SeerSkipBrowserWidget.h +++ b/src/SeerSkipBrowserWidget.h @@ -17,12 +17,14 @@ class SeerSkipBrowserWidget : public QWidget, protected Ui::SeerSkipBrowserWidge void refresh (); protected slots: + void handleAddToolButton (); void handleDeleteToolButton (); void handleEnableToolButton (); void handleDisableToolButton (); signals: void refreshSkipList (); + void addSkip (const QString& skipMode, const QString& skipParameters); void deleteSkips (const QString& skips); void enableSkips (const QString& skips); void disableSkips (const QString& skips); diff --git a/src/SeerSkipBrowserWidget.ui b/src/SeerSkipBrowserWidget.ui index 329f01f..3e17d7e 100644 --- a/src/SeerSkipBrowserWidget.ui +++ b/src/SeerSkipBrowserWidget.ui @@ -91,23 +91,6 @@ - - - - true - - - Help on skips. - - - - - - - :/seer/resources/RelaxLightIcons/help-about.svg:/seer/resources/RelaxLightIcons/help-about.svg - - - diff --git a/src/SeerSkipCreateDialog.cpp b/src/SeerSkipCreateDialog.cpp new file mode 100644 index 0000000..89efe34 --- /dev/null +++ b/src/SeerSkipCreateDialog.cpp @@ -0,0 +1,92 @@ +#include "SeerSkipCreateDialog.h" +#include "SeerHelpPageDialog.h" +#include + +SeerSkipCreateDialog::SeerSkipCreateDialog (QWidget* parent) : QDialog(parent) { + + // Set up the UI. + setupUi(this); + + // Setup the widgets + fileRadioButton->setChecked(true); + fileRadioButton->setFocus(); + + // Connect things. + QObject::connect(modeButtonGroup, QOverload::of(&QButtonGroup::idClicked), this, &SeerSkipCreateDialog::handleModeButtonGroup); + QObject::connect(helpToolButton, &QToolButton::clicked, this, &SeerSkipCreateDialog::handleHelpToolButton); + + handleModeButtonGroup(); +} + +SeerSkipCreateDialog::~SeerSkipCreateDialog () { +} + +void SeerSkipCreateDialog::handleModeButtonGroup () { + + // Disable all text fields. We'll enable one later. + fileLineEdit->setEnabled(false); + fileGlobLineEdit->setEnabled(false); + functionLineEdit->setEnabled(false); + functionRegexLineEdit->setEnabled(false); + + // Enable the one that is selected. + QAbstractButton* button = modeButtonGroup->checkedButton(); + if (button == dynamic_cast(fileRadioButton)) { + fileLineEdit->setEnabled(true); + } else if (button == dynamic_cast(fileGlobRadioButton)) { + fileGlobLineEdit->setEnabled(true); + } else if (button == dynamic_cast(functionRadioButton)) { + functionLineEdit->setEnabled(true); + } else if (button == dynamic_cast(functionRegexRadioButton)) { + functionRegexLineEdit->setEnabled(true); + } +} + +void SeerSkipCreateDialog::handleHelpToolButton () { + + SeerHelpPageDialog* help = new SeerHelpPageDialog(this); + help->loadFile(":/seer/resources/help/Skips.md"); + help->show(); + help->raise(); +} + +QString SeerSkipCreateDialog::skipMode () const { + + // Build a catchpoint specification. + QString skipMode; + + if (fileRadioButton->isChecked()) { + skipMode = "file"; + }else if (fileGlobRadioButton->isChecked()) { + skipMode = "gfile"; + }else if (functionRadioButton->isChecked()) { + skipMode = "function"; + }else if (functionRegexRadioButton->isChecked()) { + skipMode = "rfunction"; + } + + qDebug() << skipMode; + + return skipMode; +} + +QString SeerSkipCreateDialog::skipParameters () const { + + // Build a catchpoint specification. + QString skipParameters; + + if (fileRadioButton->isChecked()) { + skipParameters = fileLineEdit->text(); + }else if (fileGlobRadioButton->isChecked()) { + skipParameters = fileGlobLineEdit->text(); + }else if (functionRadioButton->isChecked()) { + skipParameters = functionLineEdit->text(); + }else if (functionRegexRadioButton->isChecked()) { + skipParameters = functionRegexLineEdit->text(); + } + + qDebug() << skipParameters; + + return skipParameters; +} + diff --git a/src/SeerSkipCreateDialog.h b/src/SeerSkipCreateDialog.h new file mode 100644 index 0000000..f0ae194 --- /dev/null +++ b/src/SeerSkipCreateDialog.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "ui_SeerSkipCreateDialog.h" + +class SeerSkipCreateDialog : public QDialog, protected Ui::SeerSkipCreateDialogForm { + + Q_OBJECT + + public: + explicit SeerSkipCreateDialog (QWidget* parent = 0); + ~SeerSkipCreateDialog (); + + QString skipMode () const; + QString skipParameters () const; + + public slots: + private slots: + void handleModeButtonGroup (); + void handleHelpToolButton (); + + private: +}; + diff --git a/src/SeerSkipCreateDialog.ui b/src/SeerSkipCreateDialog.ui new file mode 100644 index 0000000..5d5e3e6 --- /dev/null +++ b/src/SeerSkipCreateDialog.ui @@ -0,0 +1,200 @@ + + + SeerSkipCreateDialogForm + + + + 0 + 0 + 471 + 253 + + + + Create a Skip + + + + + + + + Specify Skip details. + + + + + + + Qt::Horizontal + + + + 348 + 20 + + + + + + + + + + + + :/seer/resources/RelaxLightIcons/help-about.svg:/seer/resources/RelaxLightIcons/help-about.svg + + + + + + + + + + + Filename + + + modeButtonGroup + + + + + + + file + + + + + + + Filename (glob) + + + modeButtonGroup + + + + + + + file-glob-pattern + + + + + + + Function + + + modeButtonGroup + + + + + + + linespec + + + + + + + Function (regex) + + + modeButtonGroup + + + + + + + regex + + + + + + + Qt::Vertical + + + + 20 + 17 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + fileRadioButton + fileGlobRadioButton + functionRadioButton + functionRegexRadioButton + fileLineEdit + fileGlobLineEdit + functionLineEdit + functionRegexLineEdit + helpToolButton + + + + + + + buttonBox + accepted() + SeerSkipCreateDialogForm + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SeerSkipCreateDialogForm + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + + diff --git a/src/resource.qrc b/src/resource.qrc index 61fd2af..1472288 100644 --- a/src/resource.qrc +++ b/src/resource.qrc @@ -78,6 +78,7 @@ resources/help/RRDebugMode.md resources/help/CorefileDebugMode.md resources/help/Printpoints.md + resources/help/Skips.md diff --git a/src/resources/help/Skips.md b/src/resources/help/Skips.md new file mode 100644 index 0000000..cce43f8 --- /dev/null +++ b/src/resources/help/Skips.md @@ -0,0 +1,68 @@ +## Skips + +### Introduction + +Skips are gdb's way of ignoring non-important arguments that are functions when stepping into a function to debug. + +For example, consider this code: +``` + 101 int func() + 102 { + 103 foo(boring()); + 104 bar(boring()); + 105 } +``` +` ` +Suppose you wish to step into the functions ```foo``` and ```bar```, but you are not interested in stepping through ```boring```. +If you run ```step``` at line 103, you’ll enter ```boring()```, but if you run ```next```, you’ll step over both ```foo``` and ```boring```! + +One solution is to ```step``` into ```boring``` and use the ```finish``` command to immediately exit it. But this can become tedious if ```boring``` is called from many places. + +A more flexible solution is to tell gdb to execute ```boring``` with out stepping into it. The ```skip``` command does this. + +### Skip types + +There are 4 types of skips. + +#### 'file' skips + +This skip takes a single source file. All functions descriped in the file will be skipped when stepping. + +#### 'file glob-pattern' skips + +Functions in files matching file-glob-pattern will be skipped over when stepping. + +#### 'function' skips + +This skip takes a single function specification (see the Location-Specifications link below). +This function will be skipped when stepping. + +#### 'function regex' skips + +Functions whose name matches regexp will be skipped over when stepping. + +This form is useful for complex function names. For example, there is generally no need to step into C++ std::string constructors or destructors. +Plus with C++ templates it can be hard to write out the full name of the function, and often it doesn’t matter what the template arguments are. + +Specifying the function to be skipped as a regular expression makes this easier. +``` + ^std::(allocator|basic_string)<.*>::~?\1 *\( +``` +` ` +If you want to skip every templated C++ constructor and destructor in the std namespace you can do: +``` + ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\( +``` +` ` +### References + +Here is gdb's reference for the ```skip``` command. + +https://sourceware.org/gdb/current/onlinedocs/gdb.html/Skipping-Over-Functions-and-Files.html +https://sourceware.org/gdb/current/onlinedocs/gdb.html/Location-Specifications.html#Location-Specifications + +Here's a good article from "MaskRay" where he describes skips in an easy to understand way. + +https://maskray.me/blog/2024-12-30-skipping-boring-functions-in-debuggers + +` ` diff --git a/tests/helloskip/MISkip.py b/tests/helloskip/MISkip.py index 5761965..1d8867e 100644 --- a/tests/helloskip/MISkip.py +++ b/tests/helloskip/MISkip.py @@ -8,6 +8,7 @@ class MISkip(gdb.MICommand): -skip-delete Delete a list of skip id's. -skip-enable Enable a list of skip id's. -skip-disable Disable a list of skip id's. + -skip-create Functions described in manual syntax will be skipped over when stepping. -skip-create-file Functions in file will be skipped over when stepping. -skip-create-gfile Functions in files matching file-glob-pattern will be skipped over when stepping. -skip-create-function Functions named by linespec or the function containing the line named by linespec will be skipped over when stepping. @@ -36,6 +37,8 @@ class MISkip(gdb.MICommand): if columns: if (columns.group(1) == "Num"): continue + if (columns.group(1) == "Not"): + continue skipmeta = {} skipmeta["number"] = columns.group(1) skipmeta["enable"] = columns.group(2) @@ -57,17 +60,20 @@ class MISkip(gdb.MICommand): elif self._mode == "disable": gdb.execute ("skip disable " + " ".join(argv), to_string=True) return None + elif self._mode == "create": + gdb.execute ("skip \"" + " ".join(argv) + "\"", to_string=True) + return None elif self._mode == "createfile": - gdb.execute ("skip -file " + " ".join(argv), to_string=True) + gdb.execute ("skip -file \"" + " ".join(argv) + "\"", to_string=True) return None elif self._mode == "creategfile": - gdb.execute ("skip -gfile " + " ".join(argv), to_string=True) + gdb.execute ("skip -gfile \"" + " ".join(argv) + "\"", to_string=True) return None elif self._mode == "createfunction": - gdb.execute ("skip -function " + " ".join(argv), to_string=True) + gdb.execute ("skip -function \"" + " ".join(argv) + "\"", to_string=True) return None elif self._mode == "createrfunction": - gdb.execute ("skip -ffunction " + " ".join(argv), to_string=True) + gdb.execute ("skip -ffunction \"" + " ".join(argv) + "\"", to_string=True) return None else: raise gdb.GdbError("skips: Invalid parameter: %s" % self._mode) @@ -76,6 +82,7 @@ MISkip("-skip-list", "list") MISkip("-skip-delete", "delete") MISkip("-skip-enable", "enable") MISkip("-skip-disable", "disable") +MISkip("-skip-create", "create") MISkip("-skip-create-file", "createfile") MISkip("-skip-create-gfile", "creategfile") MISkip("-skip-create-function", "createfunction")