Debugger Threads: (Refactor) Use model based widgets

This commit is contained in:
Ty Lamontagne 2023-01-04 02:11:28 -05:00 committed by lightningterror
parent 37540d1c68
commit c00caa886e
11 changed files with 268 additions and 175 deletions

View File

@ -136,6 +136,8 @@ target_sources(pcsx2-qt PRIVATE
Debugger/BreakpointDialog.ui
Debugger/Models/BreakpointModel.cpp
Debugger/Models/BreakpointModel.h
Debugger/Models/ThreadModel.cpp
Debugger/Models/ThreadModel.h
Tools/InputRecording/NewInputRecordingDlg.cpp
Tools/InputRecording/NewInputRecordingDlg.h
Tools/InputRecording/NewInputRecordingDlg.ui

View File

@ -20,6 +20,7 @@
#include "DisassemblyWidget.h"
#include "BreakpointDialog.h"
#include "Models/BreakpointModel.h"
#include "Models/ThreadModel.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
@ -41,6 +42,7 @@ using namespace MipsStackWalk;
CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
: m_cpu(cpu)
, m_bpModel(cpu)
, m_threadModel(cpu)
{
m_ui.setupUi(this);
@ -59,13 +61,14 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
connect(m_ui.breakpointList, &QTableView::customContextMenuRequested, this, &CpuWidget::onBPListContextMenu);
connect(m_ui.breakpointList, &QTableView::doubleClicked, this, &CpuWidget::onBPListDoubleClicked);
m_ui.breakpointList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_ui.breakpointList->setModel(&m_bpModel);
connect(m_ui.threadList, &QTableWidget::customContextMenuRequested, this, &CpuWidget::onThreadListContextMenu);
connect(m_ui.threadList, &QTableWidget::cellDoubleClicked, this, &CpuWidget::onThreadListDoubleClick);
connect(m_ui.threadList, &QTableView::customContextMenuRequested, this, &CpuWidget::onThreadListContextMenu);
connect(m_ui.threadList, &QTableView::doubleClicked, this, &CpuWidget::onThreadListDoubleClick);
connect(m_ui.threadList, &QTableWidget::customContextMenuRequested, this, &CpuWidget::onThreadListContextMenu);
connect(m_ui.threadList, &QTableWidget::cellDoubleClicked, this, &CpuWidget::onThreadListDoubleClick);
m_ui.threadList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_ui.threadList->setModel(&m_threadModel);
connect(m_ui.stackframeList, &QTableWidget::customContextMenuRequested, this, &CpuWidget::onStackListContextMenu);
connect(m_ui.stackframeList, &QTableWidget::cellDoubleClicked, this, &CpuWidget::onStackListDoubleClick);
@ -247,14 +250,11 @@ void CpuWidget::onBPListContextMenu(QPoint pos)
if (!m_cpu.isAlive())
return;
if (m_bplistContextMenu)
delete m_bplistContextMenu;
m_bplistContextMenu = new QMenu(m_ui.breakpointList);
QMenu* contextMenu = new QMenu(tr("Breakpoint List Context Menu"), m_ui.breakpointList);
QAction* newAction = new QAction(tr("New"), m_ui.breakpointList);
connect(newAction, &QAction::triggered, this, &CpuWidget::contextBPListNew);
m_bplistContextMenu->addAction(newAction);
contextMenu->addAction(newAction);
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
@ -262,21 +262,21 @@ void CpuWidget::onBPListContextMenu(QPoint pos)
{
QAction* editAction = new QAction(tr("Edit"), m_ui.breakpointList);
connect(editAction, &QAction::triggered, this, &CpuWidget::contextBPListEdit);
m_bplistContextMenu->addAction(editAction);
contextMenu->addAction(editAction);
if (selModel->selectedIndexes().count() == 1)
{
QAction* copyAction = new QAction(tr("Copy"), m_ui.breakpointList);
connect(copyAction, &QAction::triggered, this, &CpuWidget::contextBPListCopy);
m_bplistContextMenu->addAction(copyAction);
contextMenu->addAction(copyAction);
}
QAction* deleteAction = new QAction(tr("Delete"), m_ui.breakpointList);
connect(deleteAction, &QAction::triggered, this, &CpuWidget::contextBPListDelete);
m_bplistContextMenu->addAction(deleteAction);
contextMenu->addAction(deleteAction);
}
m_bplistContextMenu->popup(m_ui.breakpointList->mapToGlobal(pos));
contextMenu->popup(m_ui.breakpointList->mapToGlobal(pos));
}
void CpuWidget::contextBPListCopy()
@ -334,8 +334,6 @@ void CpuWidget::updateFunctionList(bool whenEmpty)
if (!m_cpu.isAlive())
return;
m_bpModel.refreshData();
if (whenEmpty && m_ui.listFunctions->count())
return;
@ -371,127 +369,41 @@ void CpuWidget::updateFunctionList(bool whenEmpty)
void CpuWidget::updateThreads()
{
m_ui.threadList->setRowCount(0);
if (m_cpu.getCpuType() == BREAKPOINT_EE)
m_threadlistObjects = getEEThreads();
for (size_t i = 0; i < m_threadlistObjects.size(); i++)
{
m_ui.threadList->insertRow(i);
const auto& thread = m_threadlistObjects[i];
if (thread.data.status == THS_RUN)
m_activeThread = thread;
QTableWidgetItem* idItem = new QTableWidgetItem();
idItem->setText(QString::number(thread.tid));
idItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.threadList->setItem(i, 0, idItem);
QTableWidgetItem* pcItem = new QTableWidgetItem();
if (thread.data.status == THS_RUN)
pcItem->setText(FilledQStringFromValue(m_cpu.getPC(), 16));
else
pcItem->setText(FilledQStringFromValue(thread.data.entry, 16));
pcItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.threadList->setItem(i, 1, pcItem);
QTableWidgetItem* entryItem = new QTableWidgetItem();
entryItem->setText(FilledQStringFromValue(thread.data.entry_init, 16));
entryItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.threadList->setItem(i, 2, entryItem);
QTableWidgetItem* priorityItem = new QTableWidgetItem();
priorityItem->setText(QString::number(thread.data.currentPriority));
priorityItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.threadList->setItem(i, 3, priorityItem);
QString statusString;
switch (thread.data.status)
{
case THS_BAD:
statusString = tr("Bad");
break;
case THS_RUN:
statusString = tr("Running");
break;
case THS_READY:
statusString = tr("Ready");
break;
case THS_WAIT:
statusString = tr("Waiting");
break;
case THS_SUSPEND:
statusString = tr("Suspended");
break;
case THS_WAIT_SUSPEND:
statusString = tr("Waiting/Suspended");
break;
case THS_DORMANT:
statusString = tr("Dormant");
break;
}
QTableWidgetItem* statusItem = new QTableWidgetItem();
statusItem->setText(statusString);
statusItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.threadList->setItem(i, 4, statusItem);
QString waitTypeString;
switch (thread.data.waitType)
{
case WAIT_NONE:
waitTypeString = tr("None");
break;
case WAIT_WAKEUP_REQ:
waitTypeString = tr("Wakeup request");
break;
case WAIT_SEMA:
waitTypeString = tr("Semaphore");
break;
}
QTableWidgetItem* waitItem = new QTableWidgetItem();
waitItem->setText(waitTypeString);
waitItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.threadList->setItem(i, 5, waitItem);
}
m_threadModel.refreshData();
}
void CpuWidget::onThreadListContextMenu(QPoint pos)
{
if (!m_threadlistContextMenu)
{
m_threadlistContextMenu = new QMenu(m_ui.threadList);
if (!m_ui.threadList->selectionModel()->hasSelection())
return;
QAction* copyAction = new QAction(tr("Copy"), m_ui.threadList);
connect(copyAction, &QAction::triggered, [this] {
const auto& items = m_ui.threadList->selectedItems();
if (!items.size())
return;
QApplication::clipboard()->setText(items.first()->text());
});
m_threadlistContextMenu->addAction(copyAction);
}
QMenu* contextMenu = new QMenu(tr("Thread List Context Menu"), m_ui.threadList);
m_threadlistContextMenu->exec(m_ui.threadList->mapToGlobal(pos));
QAction* actionCopy = new QAction(tr("Copy"), m_ui.threadList);
connect(actionCopy, &QAction::triggered, [this]() {
const auto* selModel = m_ui.threadList->selectionModel();
if (!selModel->hasSelection())
return;
QGuiApplication::clipboard()->setText(m_ui.threadList->model()->data(selModel->currentIndex()).toString());
});
contextMenu->addAction(actionCopy);
contextMenu->popup(m_ui.threadList->mapToGlobal(pos));
}
void CpuWidget::onThreadListDoubleClick(int row, int column)
void CpuWidget::onThreadListDoubleClick(const QModelIndex& index)
{
const auto& entry = m_threadlistObjects.at(row);
if (column == 1) // PC
switch (index.column())
{
if (entry.data.status == THS_RUN)
m_ui.disassemblyWidget->gotoAddress(m_cpu.getPC());
else
m_ui.disassemblyWidget->gotoAddress(entry.data.entry);
}
else if (column == 2) // Entry Point
{
m_ui.disassemblyWidget->gotoAddress(entry.data.entry_init);
case ThreadModel::ThreadColumns::ENTRY:
m_ui.memoryviewWidget->gotoAddress(m_ui.threadList->model()->data(index, Qt::UserRole).toUInt());
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
break;
default: // Default to PC
m_ui.disassemblyWidget->gotoAddress(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
break;
}
}

View File

@ -23,6 +23,7 @@
#include "DebugTools/MipsStackWalk.h"
#include "Models/BreakpointModel.h"
#include "Models/ThreadModel.h"
#include "QtHost.h"
#include <QtWidgets/QWidget>
@ -50,7 +51,6 @@ public slots:
void onVMPaused();
void updateBreakpoints();
void onBPListDoubleClicked(const QModelIndex& index);
void onBPListContextMenu(QPoint pos);
@ -60,8 +60,8 @@ public slots:
void contextBPListEdit();
void updateThreads();
void onThreadListDoubleClick(const QModelIndex& index);
void onThreadListContextMenu(QPoint pos);
void onThreadListDoubleClick(int row, int column);
void updateStackFrames();
void onStackListContextMenu(QPoint pos);
@ -93,8 +93,6 @@ public slots:
private:
std::vector<QTableWidget*> m_registerTableViews;
QMenu* m_bplistContextMenu = 0;
QMenu* m_threadlistContextMenu = 0;
QMenu* m_stacklistContextMenu = 0;
QMenu* m_funclistContextMenu = 0;
@ -103,6 +101,7 @@ private:
DebugInterface& m_cpu;
BreakpointModel m_bpModel;
ThreadModel m_threadModel;
std::vector<EEThread> m_threadlistObjects;
EEThread m_activeThread;

View File

@ -322,7 +322,7 @@
<enum>QTabWidget::South</enum>
</property>
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="tab_memory">
<property name="sizePolicy">
@ -438,7 +438,7 @@
<number>0</number>
</property>
<item>
<widget class="QTableWidget" name="threadList">
<widget class="QTableView" name="threadList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@ -451,48 +451,6 @@
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<property name="rowCount">
<number>0</number>
</property>
<property name="columnCount">
<number>6</number>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>ID</string>
</property>
</column>
<column>
<property name="text">
<string>PC</string>
</property>
</column>
<column>
<property name="text">
<string>Entry Point</string>
</property>
</column>
<column>
<property name="text">
<string>Priority</string>
</property>
</column>
<column>
<property name="text">
<string>State</string>
</property>
</column>
<column>
<property name="text">
<string>Wait Type</string>
</property>
</column>
</widget>
</item>
</layout>

View File

@ -734,4 +734,5 @@ void DisassemblyWidget::gotoAddress(u32 address)
m_selectedAddressEnd = destAddress;
this->repaint();
this->setFocus();
}

View File

@ -500,4 +500,5 @@ void MemoryViewWidget::gotoAddress(u32 address)
m_table.UpdateStartAddress(address & ~0xF);
m_table.selectedAddress = address;
this->repaint();
this->setFocus();
}

View File

@ -50,7 +50,7 @@ public:
bool insertRows(int row, int count, std::vector<BreakpointMemcheck> breakpoints, const QModelIndex& index = QModelIndex());
BreakpointMemcheck at(int row) const { return m_breakpoints.at(row); };
public slots:
void refreshData();
private:

View File

@ -0,0 +1,142 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "ThreadModel.h"
#include "QtUtils.h"
ThreadModel::ThreadModel(DebugInterface& cpu, QObject* parent)
: QAbstractTableModel(parent)
, m_cpu(cpu)
{
}
int ThreadModel::rowCount(const QModelIndex&) const
{
if (m_cpu.getCpuType() == BREAKPOINT_EE)
{
Console.Warning("%d", getEEThreads().size());
return getEEThreads().size();
}
else
return 0;
}
int ThreadModel::columnCount(const QModelIndex&) const
{
return ThreadModel::COLUMN_COUNT;
}
QVariant ThreadModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole)
{
const auto thread = getEEThreads().at(index.row());
switch (index.column())
{
case ThreadModel::ID:
return thread.tid;
case ThreadModel::PC:
{
if (thread.data.status == THS_RUN)
return QtUtils::FilledQStringFromValue(m_cpu.getPC(), 16);
else
return QtUtils::FilledQStringFromValue(thread.data.entry, 16);
}
case ThreadModel::ENTRY:
return QtUtils::FilledQStringFromValue(thread.data.entry_init, 16);
case ThreadModel::PRIORITY:
return QString::number(thread.data.currentPriority);
case ThreadModel::STATE:
{
const auto& state = ThreadStateStrings.find(thread.data.status);
if (state != ThreadStateStrings.end())
return state->second;
else
return tr("INVALID");
}
case ThreadModel::WAIT_TYPE:
{
const auto& waitType = ThreadWaitStrings.find(thread.data.waitType);
if (waitType != ThreadWaitStrings.end())
return waitType->second;
else
return tr("INVALID");
}
}
}
else if (role == Qt::UserRole)
{
const auto& thread = getEEThreads().at(index.row());
switch (index.column())
{
case ThreadModel::ID:
return thread.tid;
case ThreadModel::PC:
{
if (thread.data.status == THS_RUN)
return m_cpu.getPC();
else
return thread.data.entry;
}
case ThreadModel::ENTRY:
return thread.data.entry_init;
case ThreadModel::PRIORITY:
return thread.data.currentPriority;
case ThreadModel::STATE:
return thread.data.status;
case ThreadModel::WAIT_TYPE:
return thread.data.waitType;
default:
return QVariant();
}
}
return QVariant();
}
QVariant ThreadModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case ThreadColumns::ID:
return tr("ID");
case ThreadColumns::PC:
return tr("PC");
case ThreadColumns::ENTRY:
return tr("ENTRY");
case ThreadColumns::PRIORITY:
return tr("PRIORITY");
case ThreadColumns::STATE:
return tr("STATE");
case ThreadColumns::WAIT_TYPE:
return tr("WAIT TYPE");
default:
return QVariant();
}
}
return QVariant();
}
void ThreadModel::refreshData()
{
beginResetModel();
endResetModel();
}

View File

@ -0,0 +1,66 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QtCore/QAbstractTableModel>
#include "DebugTools/DebugInterface.h"
#include "DebugTools/BiosDebugData.h"
#include <map>
class ThreadModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum ThreadColumns : int
{
ID = 0,
PC,
ENTRY,
PRIORITY,
STATE,
WAIT_TYPE,
COLUMN_COUNT
};
explicit ThreadModel(DebugInterface& cpu, QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void refreshData();
private:
const std::map<int, QString> ThreadStateStrings{
{THS_BAD, tr("BAD")},
{THS_RUN, tr("RUN")},
{THS_READY, tr("READY")},
{THS_WAIT, tr("WAIT")},
{THS_SUSPEND, tr("SUSPEND")},
{THS_WAIT_SUSPEND, tr("WAIT SUSPEND")},
{THS_DORMANT, tr("DORMANT")}};
const std::map<int, QString> ThreadWaitStrings{
{WAIT_NONE, tr("NONE")},
{WAIT_WAKEUP_REQ, tr("WAKEUP REQUEST")},
{WAIT_SEMA, tr("SEMAPHORE")}};
DebugInterface& m_cpu;
};

View File

@ -149,6 +149,7 @@
<ClCompile Include="Debugger\RegisterWidget.cpp" />
<ClCompile Include="Debugger\BreakpointDialog.cpp" />
<ClCompile Include="Debugger\Models\BreakpointModel.cpp" />
<ClCompile Include="Debugger\Models\ThreadModel.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
<ClCompile Include="Settings\ControllerGlobalSettingsWidget.cpp" />
@ -230,6 +231,7 @@
<QtMoc Include="Debugger\RegisterWidget.h" />
<QtMoc Include="Debugger\BreakpointDialog.h" />
<QtMoc Include="Debugger\Models\BreakpointModel.h" />
<QtMoc Include="Debugger\Models\ThreadModel.h" />
<QtMoc Include="Settings\ControllerBindingWidgets.h" />
<QtMoc Include="Settings\ControllerGlobalSettingsWidget.h" />
<ClInclude Include="Settings\MemoryCardConvertWorker.h" />
@ -281,6 +283,7 @@
<ClCompile Include="$(IntDir)Debugger\moc_MemoryViewWidget.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_BreakpointDialog.cpp" />
<ClCompile Include="$(IntDir)Debugger\Models\moc_BreakpointModel.cpp" />
<ClCompile Include="$(IntDir)Debugger\Models\moc_ThreadModel.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListModel.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListRefreshThread.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListWidget.cpp" />

View File

@ -280,6 +280,9 @@
<ClCompile Include="Debugger\Models\BreakpointModel.cpp">
<Filter>Debugger\Models</Filter>
</ClCompile>
<ClCompile Include="Debugger\Models\ThreadModel.cpp">
<Filter>Debugger\Models</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
@ -301,6 +304,9 @@
<ClCompile Include="$(IntDir)Debugger\Models\moc_BreakpointModel.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\Models\moc_ThreadModel.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -441,6 +447,9 @@
<QtMoc Include="Debugger\Models\BreakpointModel.h">
<Filter>Debugger\Models</Filter>
</QtMoc>
<QtMoc Include="Debugger\Models\ThreadModel.h">
<Filter>Debugger\Models</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">