Debugger Stack frames: (Refactor) Use model based widgets

This commit is contained in:
Ty Lamontagne 2023-01-06 21:35:57 -05:00 committed by lightningterror
parent c00caa886e
commit b6125e97e9
10 changed files with 235 additions and 110 deletions

View File

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

View File

@ -43,6 +43,7 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
: m_cpu(cpu)
, m_bpModel(cpu)
, m_threadModel(cpu)
, m_stackModel(cpu)
{
m_ui.setupUi(this);
@ -70,8 +71,11 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
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);
connect(m_ui.stackList, &QTableView::customContextMenuRequested, this, &CpuWidget::onStackListContextMenu);
connect(m_ui.stackList, &QTableView::doubleClicked, this, &CpuWidget::onStackListDoubleClick);
m_ui.stackList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_ui.stackList->setModel(&m_stackModel);
connect(m_ui.tabWidgetRegFunc, &QTabWidget::currentChanged, [this](int i) {if(i == 1){updateFunctionList(true);} });
connect(m_ui.listFunctions, &QListWidget::customContextMenuRequested, this, &CpuWidget::onFuncListContextMenu);
@ -471,72 +475,46 @@ void CpuWidget::onFuncListDoubleClick(QListWidgetItem* item)
void CpuWidget::updateStackFrames()
{
m_ui.stackframeList->setRowCount(0);
m_stacklistObjects = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29),
m_activeThread.data.entry_init, m_activeThread.data.stack);
for (size_t i = 0; i < m_stacklistObjects.size(); i++)
{
m_ui.stackframeList->insertRow(i);
const auto& stackFrame = m_stacklistObjects.at(i);
QTableWidgetItem* entryItem = new QTableWidgetItem();
entryItem->setText(FilledQStringFromValue(stackFrame.entry, 16));
entryItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.stackframeList->setItem(i, 0, entryItem);
QTableWidgetItem* entryName = new QTableWidgetItem();
entryName->setText(m_cpu.GetSymbolMap().GetLabelString(stackFrame.entry).c_str());
entryName->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.stackframeList->setItem(i, 1, entryName);
QTableWidgetItem* entryPC = new QTableWidgetItem();
entryPC->setText(FilledQStringFromValue(stackFrame.pc, 16));
entryPC->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.stackframeList->setItem(i, 2, entryPC);
QTableWidgetItem* entryOpcode = new QTableWidgetItem();
entryOpcode->setText(m_ui.disassemblyWidget->GetLineDisasm(stackFrame.pc));
entryOpcode->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.stackframeList->setItem(i, 3, entryOpcode);
QTableWidgetItem* entrySP = new QTableWidgetItem();
entrySP->setText(FilledQStringFromValue(stackFrame.sp, 16));
entrySP->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.stackframeList->setItem(i, 4, entrySP);
QTableWidgetItem* entryStackSize = new QTableWidgetItem();
entryStackSize->setText(QString::number(stackFrame.stackSize));
entryStackSize->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled);
m_ui.stackframeList->setItem(i, 5, entryStackSize);
}
m_stackModel.refreshData();
}
void CpuWidget::onStackListContextMenu(QPoint pos)
{
if (!m_stacklistContextMenu)
{
m_stacklistContextMenu = new QMenu(m_ui.stackframeList);
if (!m_ui.stackList->selectionModel()->hasSelection())
return;
QAction* copyAction = new QAction(tr("Copy"), m_ui.stackframeList);
connect(copyAction, &QAction::triggered, [this] {
const auto& items = m_ui.stackframeList->selectedItems();
if (!items.size())
return;
QApplication::clipboard()->setText(items.first()->text());
});
m_stacklistContextMenu->addAction(copyAction);
}
QMenu* contextMenu = new QMenu(tr("Stack List Context Menu"), m_ui.stackList);
m_stacklistContextMenu->exec(m_ui.stackframeList->mapToGlobal(pos));
QAction* actionCopy = new QAction(tr("Copy"), m_ui.stackList);
connect(actionCopy, &QAction::triggered, [this]() {
const auto* selModel = m_ui.stackList->selectionModel();
if (!selModel->hasSelection())
return;
QGuiApplication::clipboard()->setText(m_ui.stackList->model()->data(selModel->currentIndex()).toString());
});
contextMenu->addAction(actionCopy);
contextMenu->popup(m_ui.stackList->mapToGlobal(pos));
}
void CpuWidget::onStackListDoubleClick(int row, int column)
void CpuWidget::onStackListDoubleClick(const QModelIndex& index)
{
const auto& entry = m_stacklistObjects.at(row);
m_ui.disassemblyWidget->gotoAddress(entry.pc);
switch (index.column())
{
case StackModel::StackModel::ENTRY:
case StackModel::StackModel::ENTRY_LABEL:
m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
break;
case StackModel::StackModel::SP:
m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->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.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
break;
}
}
template <typename T>

View File

@ -24,6 +24,7 @@
#include "Models/BreakpointModel.h"
#include "Models/ThreadModel.h"
#include "Models/StackModel.h"
#include "QtHost.h"
#include <QtWidgets/QWidget>
@ -65,7 +66,7 @@ public slots:
void updateStackFrames();
void onStackListContextMenu(QPoint pos);
void onStackListDoubleClick(int row, int column);
void onStackListDoubleClick(const QModelIndex& index);
void updateFunctionList(bool whenEmpty = false);
void onFuncListContextMenu(QPoint pos);
@ -102,9 +103,9 @@ private:
BreakpointModel m_bpModel;
ThreadModel m_threadModel;
StackModel m_stackModel;
std::vector<EEThread> m_threadlistObjects;
EEThread m_activeThread;
std::vector<StackFrame> m_stacklistObjects;
bool m_demangleFunctions = true;

View File

@ -457,7 +457,7 @@
</widget>
<widget class="QWidget" name="tab_callstack">
<attribute name="title">
<string>Call Stack</string>
<string>Active Call Stack</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
@ -476,7 +476,7 @@
<number>0</number>
</property>
<item>
<widget class="QTableWidget" name="stackframeList">
<widget class="QTableView" name="stackList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@ -495,48 +495,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>Entry</string>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>PC</string>
</property>
</column>
<column>
<property name="text">
<string>Opcode</string>
</property>
</column>
<column>
<property name="text">
<string>SP</string>
</property>
</column>
<column>
<property name="text">
<string>Frame Size</string>
</property>
</column>
</widget>
</item>
</layout>

View File

@ -0,0 +1,126 @@
/* 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 "StackModel.h"
#include "DebugTools/MipsStackWalk.h"
#include "DebugTools/BiosDebugData.h"
#include "QtUtils.h"
StackModel::StackModel(DebugInterface& cpu, QObject* parent)
: QAbstractTableModel(parent)
, m_cpu(cpu)
{
}
int StackModel::rowCount(const QModelIndex&) const
{
return m_stackFrames.size();
}
int StackModel::columnCount(const QModelIndex&) const
{
return StackModel::COLUMN_COUNT;
}
QVariant StackModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole)
{
const auto& stackFrame = m_stackFrames.at(index.row());
switch (index.column())
{
case StackModel::ENTRY:
return QtUtils::FilledQStringFromValue(stackFrame.entry, 16);
case StackModel::ENTRY_LABEL:
return m_cpu.GetSymbolMap().GetLabelString(stackFrame.entry).c_str();
case StackModel::PC:
return QtUtils::FilledQStringFromValue(stackFrame.pc, 16);
case StackModel::PC_OPCODE:
return m_cpu.disasm(stackFrame.pc, true).c_str();
case StackModel::SP:
return QtUtils::FilledQStringFromValue(stackFrame.sp, 16);
case StackModel::SIZE:
return QString::number(stackFrame.stackSize);
}
}
else if (role == Qt::UserRole)
{
const auto& stackFrame = m_stackFrames.at(index.row());
switch (index.column())
{
case StackModel::ENTRY:
return stackFrame.entry;
case StackModel::ENTRY_LABEL:
return m_cpu.GetSymbolMap().GetLabelString(stackFrame.entry).c_str();
case StackModel::PC:
return stackFrame.pc;
case StackModel::PC_OPCODE:
return m_cpu.disasm(stackFrame.pc, true).c_str();
case StackModel::SP:
return stackFrame.sp;
case StackModel::SIZE:
return stackFrame.stackSize;
}
}
return QVariant();
}
QVariant StackModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case StackColumns::ENTRY:
return tr("ENTRY");
case StackColumns::ENTRY_LABEL:
return tr("LABEL");
case StackColumns::PC:
return tr("PC");
case StackColumns::PC_OPCODE:
return tr("INSTRUCTION");
case StackColumns::SP:
return tr("STACK POINTER");
case StackColumns::SIZE:
return tr("SIZE");
default:
return QVariant();
}
}
return QVariant();
}
void StackModel::refreshData()
{
if (m_cpu.getCpuType() == BREAKPOINT_IOP)
return;
// Hopefully in the near future we can get a stack frame for
// each thread
beginResetModel();
for (const auto& thread : getEEThreads())
{
if (thread.data.status == THS_RUN)
{
m_stackFrames = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29),
thread.data.entry_init, thread.data.stack);
break;
}
}
endResetModel();
}

View File

@ -0,0 +1,51 @@
/* 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/MipsStackWalk.h"
class StackModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum StackColumns : int
{
ENTRY = 0,
ENTRY_LABEL,
PC,
PC_OPCODE,
SP,
SIZE,
COLUMN_COUNT
};
explicit StackModel(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:
DebugInterface& m_cpu;
std::vector<MipsStackWalk::StackFrame> m_stackFrames;
};

View File

@ -28,10 +28,7 @@ ThreadModel::ThreadModel(DebugInterface& cpu, QObject* parent)
int ThreadModel::rowCount(const QModelIndex&) const
{
if (m_cpu.getCpuType() == BREAKPOINT_EE)
{
Console.Warning("%d", getEEThreads().size());
return getEEThreads().size();
}
else
return 0;
}
@ -139,4 +136,4 @@ void ThreadModel::refreshData()
{
beginResetModel();
endResetModel();
}
}

View File

@ -63,4 +63,4 @@ private:
{WAIT_SEMA, tr("SEMAPHORE")}};
DebugInterface& m_cpu;
};
};

View File

@ -150,6 +150,7 @@
<ClCompile Include="Debugger\BreakpointDialog.cpp" />
<ClCompile Include="Debugger\Models\BreakpointModel.cpp" />
<ClCompile Include="Debugger\Models\ThreadModel.cpp" />
<ClCompile Include="Debugger\Models\StackModel.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
<ClCompile Include="Settings\ControllerGlobalSettingsWidget.cpp" />
@ -232,6 +233,7 @@
<QtMoc Include="Debugger\BreakpointDialog.h" />
<QtMoc Include="Debugger\Models\BreakpointModel.h" />
<QtMoc Include="Debugger\Models\ThreadModel.h" />
<QtMoc Include="Debugger\Models\StackModel.h" />
<QtMoc Include="Settings\ControllerBindingWidgets.h" />
<QtMoc Include="Settings\ControllerGlobalSettingsWidget.h" />
<ClInclude Include="Settings\MemoryCardConvertWorker.h" />
@ -284,6 +286,7 @@
<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)Debugger\Models\moc_StackModel.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListModel.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListRefreshThread.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListWidget.cpp" />

View File

@ -283,6 +283,9 @@
<ClCompile Include="Debugger\Models\ThreadModel.cpp">
<Filter>Debugger\Models</Filter>
</ClCompile>
<ClCompile Include="Debugger\Models\StackModel.cpp">
<Filter>Debugger\Models</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
@ -307,6 +310,9 @@
<ClCompile Include="$(IntDir)Debugger\Models\moc_ThreadModel.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\Models\moc_StackModel.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -450,6 +456,9 @@
<QtMoc Include="Debugger\Models\ThreadModel.h">
<Filter>Debugger\Models</Filter>
</QtMoc>
<QtMoc Include="Debugger\Models\StackModel.h">
<Filter>Debugger\Models</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">