diff --git a/CHANGELOG.md b/CHANGELOG.md index d01df6d..98f4650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ the Debug dialog for the Run mode. Some apps use deferred loading of code with dlopen(). * Fixed bug when restoring from project file with 'start' mode. -* Improve visualizers for Ada arrays. "(system.address) 0x7fffffffcf10" +* ADA: Improve visualizers for Ada arrays. "(system.address) 0x7fffffffcf10" +* ADA: Add task browser. * Add a Shortcut (CTRL-I) to interrupt the running program. ## [1.15] - 2023-03-04 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e5cf31..6be31d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,6 +72,7 @@ set(HEADER_FILES SeerThreadFramesBrowserWidget.h SeerThreadIdsBrowserWidget.h SeerThreadGroupsBrowserWidget.h + SeerAdaTasksBrowserWidget.h SeerThreadManagerWidget.h SeerGdbLogWidget.h SeerTildeLogWidget.h @@ -158,6 +159,7 @@ set(SOURCE_FILES SeerThreadFramesBrowserWidget.cpp SeerThreadIdsBrowserWidget.cpp SeerThreadGroupsBrowserWidget.cpp + SeerAdaTasksBrowserWidget.cpp SeerThreadManagerWidget.cpp SeerGdbLogWidget.cpp SeerTildeLogWidget.cpp diff --git a/src/SeerAdaTasksBrowserWidget.cpp b/src/SeerAdaTasksBrowserWidget.cpp new file mode 100644 index 0000000..e6fb258 --- /dev/null +++ b/src/SeerAdaTasksBrowserWidget.cpp @@ -0,0 +1,185 @@ +#include "SeerAdaTasksBrowserWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include +#include + +SeerAdaTasksBrowserWidget::SeerAdaTasksBrowserWidget (QWidget* parent) : QWidget(parent) { + + // Construct the UI. + setupUi(this); + + // Setup the widgets + adaTaskTreeWidget->setMouseTracking(true); + adaTaskTreeWidget->setSortingEnabled(false); + adaTaskTreeWidget->resizeColumnToContents(0); // current + adaTaskTreeWidget->resizeColumnToContents(1); // id + adaTaskTreeWidget->resizeColumnToContents(2); // taskid + adaTaskTreeWidget->resizeColumnToContents(3); // threadid + adaTaskTreeWidget->resizeColumnToContents(4); // parentid + adaTaskTreeWidget->resizeColumnToContents(5); // priority + adaTaskTreeWidget->resizeColumnToContents(6); // state + adaTaskTreeWidget->resizeColumnToContents(7); // name + + adaTaskTreeWidget->clear(); + + // Connect things. + QObject::connect(adaTaskTreeWidget, &QTreeWidget::itemClicked, this, &SeerAdaTasksBrowserWidget::handleItemClicked); +} + +SeerAdaTasksBrowserWidget::~SeerAdaTasksBrowserWidget () { +} + +void SeerAdaTasksBrowserWidget::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,tasks={")) { + + // -ada-task-info [ task-id ] + + // ^done,tasks={ + // nr_rows="3", + // nr_cols="8", + // hdr=[ + // { + // width="1",alignment="-1",col_name="current",colhdr="" + // }, + // { + // width="3",alignment="1",col_name="id",colhdr="ID" + // }, + // { + // width="9",alignment="1",col_name="task-id",colhdr="TID" + // }, + // { + // width="4",alignment="1",col_name="thread-id",colhdr="" + // }, + // { + // width="4",alignment="1",col_name="parent-id",colhdr="P-ID" + // }, + // { + // width="3",alignment="1",col_name="priority",colhdr="Pri" + // }, + // { + // width="22",alignment="-1",col_name="state",colhdr="State" + // }, + // { + // width="1",alignment="2",col_name="name",colhdr="Name" + // } + // ], + // body=[ + // { + // id="1",task-id="45d030",thread-id="1",priority="48",state="Waiting on RV with 2 ",name="main_task" + // }, + // { + // current="*",id="2",task-id="45de30",thread-id="2",parent-id="1",priority="48",state="Runnable",name="task_a" + // }, + // { + // id="3",task-id="461460",thread-id="3",parent-id="1",priority="48",state="Delay Sleep",name="task_b" + // } + // ] + // } + + + //qDebug() << text; + + adaTaskTreeWidget->clear(); + + QString body_text = Seer::parseFirst(text, "body=", '[', ']', false); + + //qDebug() << threads_text; + + if (body_text != "") { + + QStringList tasks_list = Seer::parse(body_text, "", '{', '}', false); + + for ( const auto& task_text : tasks_list ) { + + QString current_text = Seer::parseFirst(task_text, "current=", '"', '"', false); + QString id_text = Seer::parseFirst(task_text, "id=", '"', '"', false); + QString taskid_text = Seer::parseFirst(task_text, "task-id=", '"', '"', false); + QString threadid_text = Seer::parseFirst(task_text, "thread-id=", '"', '"', false); + QString parentid_text = Seer::parseFirst(task_text, "parent-id=", '"', '"', false); + QString priority_text = Seer::parseFirst(task_text, "priority=", '"', '"', false); + QString state_text = Seer::parseFirst(task_text, "state=", '"', '"', false); + QString name_text = Seer::parseFirst(task_text, "name=", '"', '"', false); + + // Create the item. + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, current_text); + item->setText(1, id_text); + item->setText(2, taskid_text); + item->setText(3, threadid_text); + item->setText(4, parentid_text); + item->setText(5, priority_text); + item->setText(6, state_text); + item->setText(7, name_text); + + // Add the frame to the tree. + adaTaskTreeWidget->addTopLevelItem(item); + } + } + + // Clear the selection and select the one for the current thread-id. + adaTaskTreeWidget->clearSelection(); + + QList matches = adaTaskTreeWidget->findItems("*", Qt::MatchExactly, 0); + if (matches.size() > 0) { + adaTaskTreeWidget->setCurrentItem(matches.first()); + } + + }else if (text.startsWith("^error,msg=\"No registers.\"")) { + adaTaskTreeWidget->clear(); + + }else{ + // Ignore others. + } + + adaTaskTreeWidget->resizeColumnToContents(0); + adaTaskTreeWidget->resizeColumnToContents(1); + adaTaskTreeWidget->resizeColumnToContents(2); + adaTaskTreeWidget->resizeColumnToContents(3); + adaTaskTreeWidget->resizeColumnToContents(4); + adaTaskTreeWidget->resizeColumnToContents(5); + adaTaskTreeWidget->resizeColumnToContents(6); + adaTaskTreeWidget->resizeColumnToContents(7); + + QApplication::restoreOverrideCursor(); +} + +void SeerAdaTasksBrowserWidget::handleStoppingPointReached () { + + // Don't do any work if the widget is hidden. + if (isHidden()) { + return; + } + + refresh(); +} + +void SeerAdaTasksBrowserWidget::handleItemClicked (QTreeWidgetItem* item, int column) { + + Q_UNUSED(column); + + emit selectedThread(item->text(3).toInt()); +} + +void SeerAdaTasksBrowserWidget::refresh () { + + emit refreshAdaTasks(); +} + +void SeerAdaTasksBrowserWidget::showEvent (QShowEvent* event) { + + QWidget::showEvent(event); + + refresh(); +} + diff --git a/src/SeerAdaTasksBrowserWidget.h b/src/SeerAdaTasksBrowserWidget.h new file mode 100644 index 0000000..e469da2 --- /dev/null +++ b/src/SeerAdaTasksBrowserWidget.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include "ui_SeerAdaTasksBrowserWidget.h" + +class SeerAdaTasksBrowserWidget : public QWidget, protected Ui::SeerAdaTasksBrowserWidgetForm { + + Q_OBJECT + + public: + explicit SeerAdaTasksBrowserWidget (QWidget* parent = 0); + ~SeerAdaTasksBrowserWidget (); + + public slots: + void handleText (const QString& text); + void handleStoppingPointReached (); + void refresh (); + + protected slots: + void handleItemClicked (QTreeWidgetItem* item, int column); + + signals: + void refreshAdaTasks (); + void selectedThread (int threadid); + + protected: + void showEvent (QShowEvent* event); + + private: +}; + diff --git a/src/SeerAdaTasksBrowserWidget.ui b/src/SeerAdaTasksBrowserWidget.ui new file mode 100644 index 0000000..5352945 --- /dev/null +++ b/src/SeerAdaTasksBrowserWidget.ui @@ -0,0 +1,73 @@ + + + SeerAdaTasksBrowserWidgetForm + + + + 0 + 0 + 794 + 528 + + + + Form + + + + + + QAbstractItemView::SingleSelection + + + 8 + + + + Active + + + + + Id + + + + + Task Id + + + + + Thread Id + + + + + Parent Id + + + + + Priority + + + + + State + + + + + Name + + + + + + + + + + + diff --git a/src/SeerGdbWidget.cpp b/src/SeerGdbWidget.cpp index c7531e7..5f62714 100644 --- a/src/SeerGdbWidget.cpp +++ b/src/SeerGdbWidget.cpp @@ -172,6 +172,7 @@ SeerGdbWidget::SeerGdbWidget (QWidget* parent) : QWidget(parent) { QObject::connect(_gdbMonitor, &GdbMonitor::caretTextOutput, threadManagerWidget->threadIdsBrowserWidget(), &SeerThreadIdsBrowserWidget::handleText); QObject::connect(_gdbMonitor, &GdbMonitor::caretTextOutput, threadManagerWidget->threadGroupsBrowserWidget(), &SeerThreadGroupsBrowserWidget::handleText); QObject::connect(_gdbMonitor, &GdbMonitor::equalTextOutput, threadManagerWidget->threadGroupsBrowserWidget(), &SeerThreadGroupsBrowserWidget::handleText); + QObject::connect(_gdbMonitor, &GdbMonitor::caretTextOutput, threadManagerWidget->adaTasksBrowserWidget(), &SeerAdaTasksBrowserWidget::handleText); QObject::connect(editorManagerWidget, &SeerEditorManagerWidget::refreshBreakpointsList, this, &SeerGdbWidget::handleGdbGenericpointList); QObject::connect(editorManagerWidget, &SeerEditorManagerWidget::refreshStackFrames, this, &SeerGdbWidget::handleGdbStackListFrames); @@ -256,6 +257,10 @@ SeerGdbWidget::SeerGdbWidget (QWidget* parent) : QWidget(parent) { QObject::connect(threadManagerWidget->threadGroupsBrowserWidget(), &SeerThreadGroupsBrowserWidget::startThreadGroup, this, &SeerGdbWidget::handleGdbStartThreadGroup); QObject::connect(threadManagerWidget->threadGroupsBrowserWidget(), &SeerThreadGroupsBrowserWidget::continueThreadGroup, this, &SeerGdbWidget::handleGdbContinueThreadGroup); QObject::connect(threadManagerWidget->threadGroupsBrowserWidget(), &SeerThreadGroupsBrowserWidget::interruptThreadGroup, this, &SeerGdbWidget::handleGdbInterruptThreadGroup); + + QObject::connect(threadManagerWidget->adaTasksBrowserWidget(), &SeerAdaTasksBrowserWidget::refreshAdaTasks, this, &SeerGdbWidget::handleGdbAdaListTasks); + QObject::connect(threadManagerWidget->adaTasksBrowserWidget(), &SeerAdaTasksBrowserWidget::selectedThread, this, &SeerGdbWidget::handleGdbThreadSelectId); + QObject::connect(threadManagerWidget, &SeerThreadManagerWidget::schedulerLockingModeChanged, this, &SeerGdbWidget::handleGdbSchedulerLockingMode); QObject::connect(threadManagerWidget, &SeerThreadManagerWidget::scheduleMultipleModeChanged, this, &SeerGdbWidget::handleGdbScheduleMultipleMode); QObject::connect(threadManagerWidget, &SeerThreadManagerWidget::forkFollowsModeChanged, this, &SeerGdbWidget::handleGdbForkFollowMode); @@ -290,8 +295,9 @@ SeerGdbWidget::SeerGdbWidget (QWidget* parent) : QWidget(parent) { QObject::connect(this, &SeerGdbWidget::stoppingPointReached, stackManagerWidget->stackFramesBrowserWidget(), &SeerStackFramesBrowserWidget::handleStoppingPointReached); QObject::connect(this, &SeerGdbWidget::stoppingPointReached, stackManagerWidget->stackLocalsBrowserWidget(), &SeerStackLocalsBrowserWidget::handleStoppingPointReached); QObject::connect(this, &SeerGdbWidget::stoppingPointReached, stackManagerWidget->stackArgumentsBrowserWidget(), &SeerStackArgumentsBrowserWidget::handleStoppingPointReached); - QObject::connect(this, &SeerGdbWidget::stoppingPointReached, threadManagerWidget->threadIdsBrowserWidget(), &SeerThreadIdsBrowserWidget::handleStoppingPointReached); QObject::connect(this, &SeerGdbWidget::stoppingPointReached, threadManagerWidget->threadFramesBrowserWidget(), &SeerThreadFramesBrowserWidget::handleStoppingPointReached); + QObject::connect(this, &SeerGdbWidget::stoppingPointReached, threadManagerWidget->threadIdsBrowserWidget(), &SeerThreadIdsBrowserWidget::handleStoppingPointReached); + QObject::connect(this, &SeerGdbWidget::stoppingPointReached, threadManagerWidget->adaTasksBrowserWidget(), &SeerAdaTasksBrowserWidget::handleStoppingPointReached); QObject::connect(this, &SeerGdbWidget::stoppingPointReached, variableManagerWidget->registerValuesBrowserWidget(), &SeerRegisterValuesBrowserWidget::handleStoppingPointReached); QObject::connect(this, &SeerGdbWidget::stoppingPointReached, variableManagerWidget->variableTrackerBrowserWidget(), &SeerVariableTrackerBrowserWidget::handleStoppingPointReached); QObject::connect(this, &SeerGdbWidget::stoppingPointReached, _breakpointsBrowserWidget, &SeerBreakpointsBrowserWidget::handleStoppingPointReached); @@ -1878,6 +1884,15 @@ void SeerGdbWidget::handleGdbThreadSelectId (int threadid) { emit stoppingPointReached(); } +void SeerGdbWidget::handleGdbAdaListTasks () { + + if (executableLaunchMode() == "") { + return; + } + + handleGdbCommand("-ada-task-info "); +} + void SeerGdbWidget::handleGdbRegisterListNames () { if (executableLaunchMode() == "") { diff --git a/src/SeerGdbWidget.h b/src/SeerGdbWidget.h index 7d5ae2d..63c32b2 100644 --- a/src/SeerGdbWidget.h +++ b/src/SeerGdbWidget.h @@ -264,6 +264,7 @@ class SeerGdbWidget : public QWidget, protected Ui::SeerGdbWidgetForm { void handleGdbThreadListIds (); void handleGdbThreadListGroups (); void handleGdbThreadSelectId (int threadid); + void handleGdbAdaListTasks (); void handleGdbRegisterListNames (); void handleGdbRegisterListValues (QString fmt); void handleGdbRegisterSetValue (QString fmt, QString name, QString value); diff --git a/src/SeerMainWindow.cpp b/src/SeerMainWindow.cpp index 5370a64..037a764 100644 --- a/src/SeerMainWindow.cpp +++ b/src/SeerMainWindow.cpp @@ -738,6 +738,10 @@ void SeerMainWindow::handleText (const QString& text) { return; } + if (newtext == "^error,msg=\"Cannot inspect Ada tasks when program is not running\"") { + return; + } + // Display the error message. QString msg_text = Seer::parseFirst(text, "msg=", false); diff --git a/src/SeerThreadManagerWidget.cpp b/src/SeerThreadManagerWidget.cpp index 746b290..7e9a993 100644 --- a/src/SeerThreadManagerWidget.cpp +++ b/src/SeerThreadManagerWidget.cpp @@ -22,10 +22,12 @@ SeerThreadManagerWidget::SeerThreadManagerWidget (QWidget* parent) : QWidget(par _threadFramesBrowserWidget = new SeerThreadFramesBrowserWidget(this); _threadIdsBrowserWidget = new SeerThreadIdsBrowserWidget(this); _threadGroupsBrowserWidget = new SeerThreadGroupsBrowserWidget(this); + _adaTasksBrowserWidget = new SeerAdaTasksBrowserWidget(this); tabWidget->addTab(_threadFramesBrowserWidget, "Frames"); tabWidget->addTab(_threadIdsBrowserWidget, "Ids"); tabWidget->addTab(_threadGroupsBrowserWidget, "Groups"); + tabWidget->addTab(_adaTasksBrowserWidget, "AdaTasks"); QToolButton* refreshToolButton = new QToolButton(tabWidget); refreshToolButton->setIcon(QIcon(":/seer/resources/RelaxLightIcons/view-refresh.svg")); @@ -70,6 +72,10 @@ SeerThreadGroupsBrowserWidget* SeerThreadManagerWidget::threadGroupsBrowserWidge return _threadGroupsBrowserWidget; } +SeerAdaTasksBrowserWidget* SeerThreadManagerWidget::adaTasksBrowserWidget () { + return _adaTasksBrowserWidget; +} + void SeerThreadManagerWidget::setSchedulerLockingMode (const QString& mode) { schedulerLockingComboBox->setCurrentText(mode); @@ -105,6 +111,7 @@ void SeerThreadManagerWidget::handleRefreshToolButtonClicked () { threadFramesBrowserWidget()->refresh(); threadIdsBrowserWidget()->refresh(); threadGroupsBrowserWidget()->refresh(); + adaTasksBrowserWidget()->refresh(); } void SeerThreadManagerWidget::handleHelpToolButtonClicked () { diff --git a/src/SeerThreadManagerWidget.h b/src/SeerThreadManagerWidget.h index 52a1722..1ab451f 100644 --- a/src/SeerThreadManagerWidget.h +++ b/src/SeerThreadManagerWidget.h @@ -3,6 +3,7 @@ #include "SeerThreadFramesBrowserWidget.h" #include "SeerThreadIdsBrowserWidget.h" #include "SeerThreadGroupsBrowserWidget.h" +#include "SeerAdaTasksBrowserWidget.h" #include @@ -19,6 +20,7 @@ class SeerThreadManagerWidget : public QWidget, protected Ui::SeerThreadManagerW SeerThreadFramesBrowserWidget* threadFramesBrowserWidget (); SeerThreadIdsBrowserWidget* threadIdsBrowserWidget (); SeerThreadGroupsBrowserWidget* threadGroupsBrowserWidget (); + SeerAdaTasksBrowserWidget* adaTasksBrowserWidget (); signals: void schedulerLockingModeChanged (const QString& mode); @@ -52,5 +54,6 @@ class SeerThreadManagerWidget : public QWidget, protected Ui::SeerThreadManagerW SeerThreadFramesBrowserWidget* _threadFramesBrowserWidget; SeerThreadIdsBrowserWidget* _threadIdsBrowserWidget; SeerThreadGroupsBrowserWidget* _threadGroupsBrowserWidget; + SeerAdaTasksBrowserWidget* _adaTasksBrowserWidget; }; diff --git a/src/resources/help/ThreadProcessInfoBrowser.md b/src/resources/help/ThreadProcessInfoBrowser.md index 5fbfe65..0ca163e 100644 --- a/src/resources/help/ThreadProcessInfoBrowser.md +++ b/src/resources/help/ThreadProcessInfoBrowser.md @@ -33,7 +33,8 @@ This information is shown for each Thread Id: Clicking on a Thread Id will cause Seer to make that Thread Id the active thread. This will in turn cause the Stack Info Browser to refer to that Thread Id. ### Ids -Ids is a simplified list ofThread Ids, with no other information. Clicking on a Thread Id will cause Seer to make that Thread Id the active thread. This will in turn cause the Stack Info Browser to refer to that Thread Id. +Ids is a simplified list of Thread Ids, with no other information. Clicking on a Thread Id will cause Seer to make that Thread Id the active thread. +This will in turn cause the Stack Info Browser to refer to that Thread Id. ### Groups Groups is a list of Thread Groups. Basically, inferior processes. Most programs are just one inferior, even if they use threads. Program's that use fork and exec will have multiple inferiors. @@ -48,6 +49,25 @@ This information is shown for each Thread Id: Executable The name and path of the process executable Cores List of cpu cores used by the process ``` +### AdaTasks +When debugging an Ada program, this view will show the state of all Ada tasks. Clicking on a Task Id will cause that Task Id to be the active task. +This will in turn cause the Stack Info Browser to refer to the thread for that Task Id. + +This information is shown for each Task Id: + +``` + Column Description + --------------- ---------------------------------------------- + Active The current task that is active + Id The identifier that GDB uses to refer to the Ada task + Task Id The identifier that the target uses to refer to the Ada task + Thread Id The global thread identifier of the thread corresponding to the Ada task + Parent Id This field exists only when the task was created by another task. + In this case, it provides the ID of the parent task + Priority The base priority of the task + State The current state of the task + Name The name of the task +``` ### Scheduler Locking mode * Record. Behaves like 'off' in 'record' mode and like 'on' in 'replay' mode. This is the default. @@ -76,5 +96,6 @@ Consult these gdb references 1. [Link](https://sourceware.org/gdb/onlinedocs/gdb/Threads.html#thread-ID-lists) Listing thread information. 2. [Link](https://sourceware.org/gdb/onlinedocs/gdb/All_002dStop-Mode.html#All_002dStop-Mode) GDB all-stop mode. 3. [Link](https://sourceware.org/gdb/onlinedocs/gdb/Forks.html) GDB follow-fork mode. -3. [Link](https://sourceware.org/gdb/onlinedocs/gdb/Process-Record-and-Replay.html) GDB Record or Replay mode. +4. [Link](https://sourceware.org/gdb/onlinedocs/gdb/Process-Record-and-Replay.html) GDB Record or Replay mode. +5. [Link](https://sourceware.org/gdb/download/onlinedocs/gdb/Ada-Tasks.html) Ada tasks.