Debugger: Fix breakpoints and saved addresses lists

This commit is contained in:
chaoticgd 2025-03-06 22:22:39 +00:00 committed by Ty
parent 92baf77509
commit 6195f4b40e
9 changed files with 135 additions and 102 deletions

View File

@ -3,21 +3,48 @@
#include "BreakpointModel.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "Debugger/DebuggerSettingsManager.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "DebugTools/DisassemblyManager.h"
#include "common/Console.h"
#include "QtHost.h"
#include "QtUtils.h"
#include <QtWidgets/QMessageBox>
#include <algorithm>
std::map<BreakPointCpu, BreakpointModel*> BreakpointModel::s_instances;
BreakpointModel::BreakpointModel(DebugInterface& cpu, QObject* parent)
: QAbstractTableModel(parent)
, m_cpu(cpu)
{
if (m_cpu.getCpuType() == BREAKPOINT_EE)
{
connect(g_emu_thread, &EmuThread::onGameChanged, this, [this](const QString& title) {
if (title.isEmpty())
return;
if (rowCount() == 0)
DebuggerSettingsManager::loadGameSettings(this);
});
DebuggerSettingsManager::loadGameSettings(this);
}
connect(this, &BreakpointModel::dataChanged, this, &BreakpointModel::refreshData);
}
BreakpointModel* BreakpointModel::getInstance(DebugInterface& cpu)
{
auto iterator = s_instances.find(cpu.getCpuType());
if (iterator == s_instances.end())
iterator = s_instances.emplace(cpu.getCpuType(), new BreakpointModel(cpu)).first;
return iterator->second;
}
int BreakpointModel::rowCount(const QModelIndex&) const
@ -33,10 +60,10 @@ int BreakpointModel::columnCount(const QModelIndex&) const
QVariant BreakpointModel::data(const QModelIndex& index, int role) const
{
size_t row = static_cast<size_t>(index.row());
if (row >= m_breakpoints.size())
if (!index.isValid() || row >= m_breakpoints.size())
return QVariant();
BreakpointMemcheck bp_mc = m_breakpoints[row];
const BreakpointMemcheck& bp_mc = m_breakpoints[row];
if (role == Qt::DisplayRole)
{
@ -271,12 +298,14 @@ Qt::ItemFlags BreakpointModel::flags(const QModelIndex& index) const
bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
std::string error;
size_t row = static_cast<size_t>(index.row());
if (!index.isValid() || row >= m_breakpoints.size())
return false;
const BreakpointMemcheck& bp_mc = m_breakpoints[row];
if (role == Qt::CheckStateRole && index.column() == BreakpointColumns::ENABLED)
{
auto bp_mc = m_breakpoints.at(index.row());
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
{
Host::RunOnCPUThread([cpu = this->m_cpu.getCpuType(), bp = *bp, enabled = value.toBool()] {
@ -295,8 +324,6 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i
}
else if (role == Qt::EditRole && index.column() == BreakpointColumns::CONDITION)
{
auto bp_mc = m_breakpoints.at(index.row());
if (auto* bp = std::get_if<BreakPoint>(&bp_mc))
{
const QString condValue = value.toString();
@ -314,6 +341,7 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i
{
PostfixExpression expr;
std::string error;
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error))
{
QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error));
@ -347,6 +375,7 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i
{
PostfixExpression expr;
std::string error;
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error))
{
QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error));

View File

@ -34,8 +34,7 @@ public:
ExportRole = Qt::UserRole + 1,
};
static constexpr QHeaderView::ResizeMode HeaderResizeModes[BreakpointColumns::COLUMN_COUNT] =
{
static constexpr QHeaderView::ResizeMode HeaderResizeModes[BreakpointColumns::COLUMN_COUNT] = {
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::ResizeToContents,
@ -45,7 +44,7 @@ public:
QHeaderView::ResizeMode::ResizeToContents,
};
explicit BreakpointModel(DebugInterface& cpu, QObject* parent = nullptr);
static BreakpointModel* getInstance(DebugInterface& cpu);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
@ -63,6 +62,10 @@ public:
void clear();
private:
BreakpointModel(DebugInterface& cpu, QObject* parent = nullptr);
DebugInterface& m_cpu;
std::vector<BreakpointMemcheck> m_breakpoints;
static std::map<BreakPointCpu, BreakpointModel*> s_instances;
};

View File

@ -12,23 +12,10 @@
BreakpointWidget::BreakpointWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters, DISALLOW_MULTIPLE_INSTANCES)
, m_model(new BreakpointModel(cpu()))
, m_model(BreakpointModel::getInstance(cpu()))
{
m_ui.setupUi(this);
if (cpu().getCpuType() == BREAKPOINT_EE)
{
connect(g_emu_thread, &EmuThread::onGameChanged, this, [this](const QString& title) {
if (title.isEmpty())
return;
if (m_model->rowCount() == 0)
DebuggerSettingsManager::loadGameSettings(m_model);
});
DebuggerSettingsManager::loadGameSettings(m_model);
}
m_ui.breakpointList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.breakpointList, &QTableView::customContextMenuRequested, this, &BreakpointWidget::openContextMenu);
connect(m_ui.breakpointList, &QTableView::doubleClicked, this, &BreakpointWidget::onDoubleClicked);
@ -39,13 +26,6 @@ BreakpointWidget::BreakpointWidget(const DebuggerWidgetParameters& parameters)
m_ui.breakpointList->horizontalHeader()->setSectionResizeMode(i, mode);
i++;
}
connect(m_model, &BreakpointModel::dataChanged, m_model, &BreakpointModel::refreshData);
receiveEvent<DebuggerEvents::BreakpointsChanged>([this](const DebuggerEvents::BreakpointsChanged& event) -> bool {
m_model->refreshData();
return true;
});
}
void BreakpointWidget::onDoubleClicked(const QModelIndex& index)

View File

@ -47,10 +47,6 @@ namespace DebuggerEvents
{
};
struct BreakpointsChanged : Event
{
};
// Add the address to the saved addresses list and switch to that tab.
struct AddToSavedAddresses : Event
{

View File

@ -3,6 +3,7 @@
#include "DisassemblyWidget.h"
#include "Debugger/Breakpoints/BreakpointModel.h"
#include "Debugger/JsonValueWrapper.h"
#include "DebugTools/DebugInterface.h"
@ -12,6 +13,7 @@
#include "QtUtils.h"
#include "QtHost.h"
#include <QtCore/QPointer>
#include <QtGui/QMouseEvent>
#include <QtWidgets/QMenu>
#include <QtGui/QClipboard>
@ -185,22 +187,7 @@ void DisassemblyWidget::contextJumpToCursor()
void DisassemblyWidget::contextToggleBreakpoint()
{
if (!cpu().isAlive())
return;
const u32 selectedAddressStart = m_selectedAddressStart;
const BreakPointCpu cpuType = cpu().getCpuType();
if (CBreakPoints::IsAddressBreakPoint(cpuType, selectedAddressStart))
{
Host::RunOnCPUThread([cpuType, selectedAddressStart] { CBreakPoints::RemoveBreakPoint(cpuType, selectedAddressStart); });
}
else
{
Host::RunOnCPUThread([cpuType, selectedAddressStart] { CBreakPoints::AddBreakPoint(cpuType, selectedAddressStart); });
}
broadcastEvent(DebuggerEvents::BreakpointsChanged());
this->repaint();
toggleBreakpoint(m_selectedAddressStart);
}
void DisassemblyWidget::contextFollowBranch()
@ -559,21 +546,7 @@ void DisassemblyWidget::mousePressEvent(QMouseEvent* event)
void DisassemblyWidget::mouseDoubleClickEvent(QMouseEvent* event)
{
if (!cpu().isAlive())
return;
const u32 selectedAddress = (static_cast<int>(event->position().y()) / m_rowHeight * 4) + m_visibleStart;
const BreakPointCpu cpuType = cpu().getCpuType();
if (CBreakPoints::IsAddressBreakPoint(cpuType, selectedAddress))
{
Host::RunOnCPUThread([cpuType, selectedAddress] { CBreakPoints::RemoveBreakPoint(cpuType, selectedAddress); });
}
else
{
Host::RunOnCPUThread([cpuType, selectedAddress] { CBreakPoints::AddBreakPoint(cpuType, selectedAddress); });
}
broadcastEvent(DebuggerEvents::BreakpointsChanged());
this->repaint();
toggleBreakpoint((static_cast<int>(event->position().y()) / m_rowHeight * 4) + m_visibleStart);
}
void DisassemblyWidget::wheelEvent(QWheelEvent* event)
@ -916,6 +889,27 @@ void DisassemblyWidget::gotoAddress(u32 address, bool should_set_focus)
this->setFocus();
}
void DisassemblyWidget::toggleBreakpoint(u32 address)
{
if (!cpu().isAlive())
return;
QPointer<DisassemblyWidget> disassembly_widget(this);
Host::RunOnCPUThread([cpu = &cpu(), address, disassembly_widget] {
if (!CBreakPoints::IsAddressBreakPoint(cpu->getCpuType(), address))
CBreakPoints::AddBreakPoint(cpu->getCpuType(), address);
else
CBreakPoints::RemoveBreakPoint(cpu->getCpuType(), address);
QtHost::RunOnUIThread([cpu, disassembly_widget]() {
BreakpointModel::getInstance(*cpu)->refreshData();
if (disassembly_widget)
disassembly_widget->repaint();
});
});
}
bool DisassemblyWidget::AddressCanRestore(u32 start, u32 end)
{
for (u32 i = start; i <= end; i += 4)

View File

@ -62,6 +62,8 @@ public slots:
void gotoProgramCounterOnPause();
void gotoAddress(u32 address, bool should_set_focus);
void toggleBreakpoint(u32 address);
private:
Ui::DisassemblyWidget m_ui;

View File

@ -6,93 +6,112 @@
#include "common/Console.h"
std::map<BreakPointCpu, SavedAddressesModel*> SavedAddressesModel::s_instances;
SavedAddressesModel::SavedAddressesModel(DebugInterface& cpu, QObject* parent)
: QAbstractTableModel(parent)
, m_cpu(cpu)
{
}
SavedAddressesModel* SavedAddressesModel::getInstance(DebugInterface& cpu)
{
auto iterator = s_instances.find(cpu.getCpuType());
if (iterator == s_instances.end())
iterator = s_instances.emplace(cpu.getCpuType(), new SavedAddressesModel(cpu)).first;
return iterator->second;
}
QVariant SavedAddressesModel::data(const QModelIndex& index, int role) const
{
size_t row = static_cast<size_t>(index.row());
if (!index.isValid() || row >= m_savedAddresses.size())
return false;
const SavedAddress& entry = m_savedAddresses[row];
if (role == Qt::CheckStateRole)
{
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
SavedAddress savedAddress = m_savedAddresses.at(index.row());
switch (index.column())
{
case HeaderColumns::ADDRESS:
return QString::number(savedAddress.address, 16).toUpper();
return QString::number(entry.address, 16).toUpper();
case HeaderColumns::LABEL:
return savedAddress.label;
return entry.label;
case HeaderColumns::DESCRIPTION:
return savedAddress.description;
return entry.description;
}
}
if (role == Qt::UserRole)
{
SavedAddress savedAddress = m_savedAddresses.at(index.row());
switch (index.column())
{
case HeaderColumns::ADDRESS:
return savedAddress.address;
return entry.address;
case HeaderColumns::LABEL:
return savedAddress.label;
return entry.label;
case HeaderColumns::DESCRIPTION:
return savedAddress.description;
return entry.description;
}
}
return QVariant();
}
bool SavedAddressesModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (role == Qt::CheckStateRole)
{
size_t row = static_cast<size_t>(index.row());
if (!index.isValid() || row >= m_savedAddresses.size())
return false;
SavedAddress& entry = m_savedAddresses[row];
if (role == Qt::CheckStateRole)
return false;
}
if (role == Qt::EditRole)
{
SavedAddress addressToEdit = m_savedAddresses.at(index.row());
if (index.column() == HeaderColumns::ADDRESS)
{
bool ok = false;
const u32 address = value.toString().toUInt(&ok, 16);
if (ok)
addressToEdit.address = address;
entry.address = address;
else
return false;
}
if (index.column() == HeaderColumns::DESCRIPTION)
addressToEdit.description = value.toString();
entry.description = value.toString();
if (index.column() == HeaderColumns::LABEL)
addressToEdit.label = value.toString();
m_savedAddresses.at(index.row()) = addressToEdit;
entry.label = value.toString();
emit dataChanged(index, index, QList<int>(role));
return true;
}
else if (role == Qt::UserRole)
{
SavedAddress addressToEdit = m_savedAddresses.at(index.row());
if (index.column() == HeaderColumns::ADDRESS)
{
const u32 address = value.toUInt();
addressToEdit.address = address;
entry.address = address;
}
if (index.column() == HeaderColumns::DESCRIPTION)
addressToEdit.description = value.toString();
entry.description = value.toString();
if (index.column() == HeaderColumns::LABEL)
addressToEdit.label = value.toString();
m_savedAddresses.at(index.row()) = addressToEdit;
entry.label = value.toString();
emit dataChanged(index, index, QList<int>(role));
return true;
}
return false;
}
@ -155,6 +174,7 @@ bool SavedAddressesModel::removeRows(int row, int count, const QModelIndex& pare
{
if (row < 0 || count < 1 || static_cast<size_t>(row + count) > m_savedAddresses.size())
return false;
beginRemoveRows(parent, row, row + count - 1);
m_savedAddresses.erase(m_savedAddresses.begin() + row, m_savedAddresses.begin() + row + count);
endRemoveRows();

View File

@ -20,7 +20,7 @@ public:
QString description;
};
enum HeaderColumns: int
enum HeaderColumns : int
{
ADDRESS = 0,
LABEL,
@ -28,14 +28,14 @@ public:
COLUMN_COUNT
};
static constexpr QHeaderView::ResizeMode HeaderResizeModes[HeaderColumns::COLUMN_COUNT] =
{
static constexpr QHeaderView::ResizeMode HeaderResizeModes[HeaderColumns::COLUMN_COUNT] = {
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::Stretch,
};
explicit SavedAddressesModel(DebugInterface& cpu, QObject* parent = nullptr);
static SavedAddressesModel* getInstance(DebugInterface& cpu);
QVariant data(const QModelIndex& index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@ -49,6 +49,10 @@ public:
void clear();
private:
SavedAddressesModel(DebugInterface& cpu, QObject* parent = nullptr);
DebugInterface& m_cpu;
std::vector<SavedAddress> m_savedAddresses;
static std::map<BreakPointCpu, SavedAddressesModel*> s_instances;
};

View File

@ -11,7 +11,7 @@
SavedAddressesWidget::SavedAddressesWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters, DISALLOW_MULTIPLE_INSTANCES)
, m_model(new SavedAddressesModel(cpu(), this))
, m_model(SavedAddressesModel::getInstance(cpu()))
{
m_ui.setupUi(this);
@ -37,7 +37,7 @@ SavedAddressesWidget::SavedAddressesWidget(const DebuggerWidgetParameters& param
}
QTableView* savedAddressesTableView = m_ui.savedAddressesList;
connect(m_model, &QAbstractItemModel::dataChanged, [savedAddressesTableView](const QModelIndex& topLeft) {
connect(m_model, &QAbstractItemModel::dataChanged, this, [savedAddressesTableView](const QModelIndex& topLeft) {
savedAddressesTableView->resizeColumnToContents(topLeft.column());
});
@ -141,18 +141,23 @@ void SavedAddressesWidget::contextPasteCSV()
void SavedAddressesWidget::contextNew()
{
qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow();
m_model->addRow();
const u32 row_count = m_model->rowCount();
m_ui.savedAddressesList->edit(m_model->index(row_count - 1, 0));
}
void SavedAddressesWidget::addAddress(u32 address)
{
qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow();
const u32 row_count = m_model->rowCount();
const QModelIndex address_index = m_model->index(row_count - 1, 0);
m_model->addRow();
u32 row_count = m_model->rowCount();
QModelIndex address_index = m_model->index(row_count - 1, SavedAddressesModel::ADDRESS);
m_model->setData(address_index, address, Qt::UserRole);
m_ui.savedAddressesList->edit(m_model->index(row_count - 1, 1));
QModelIndex label_index = m_model->index(row_count - 1, SavedAddressesModel::LABEL);
if (label_index.isValid())
m_ui.savedAddressesList->edit(label_index);
}
void SavedAddressesWidget::saveToDebuggerSettings()