Debugger Breakpoints: (Refactor) Use some reflection and item models

This commit is contained in:
Ty Lamontagne 2023-01-03 00:27:05 -05:00 committed by lightningterror
parent 8fca67f0d6
commit 37540d1c68
18 changed files with 579 additions and 478 deletions

View File

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

View File

@ -23,10 +23,11 @@
#include <QtWidgets/QDialog>
#include <QtWidgets/QMessageBox>
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu)
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakpointModel& model)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(BPDIALOG_PURPOSE::CREATE)
, m_purpose(PURPOSE::CREATE)
, m_bpModel(model)
{
m_ui.setupUi(this);
@ -37,43 +38,42 @@ BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu)
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
}
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakPoint* bp)
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakpointModel& model, BreakpointMemcheck bp_mc, int rowIndex)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(BPDIALOG_PURPOSE::EDIT_BP)
, m_bp(bp)
, m_purpose(PURPOSE::EDIT)
, m_bpModel(model)
, m_bp_mc(bp_mc)
, m_rowIndex(rowIndex)
{
m_ui.setupUi(this);
m_ui.txtAddress->setText(QtUtils::FilledQStringFromValue(m_bp->addr, 16));
if (m_bp->hasCond)
m_ui.txtCondition->setText(QString::fromLocal8Bit(&m_bp->cond.expressionString[0]));
m_ui.chkEnable->setChecked(m_bp->enabled);
connect(m_ui.rdoExecute, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
m_ui.rdoExecute->toggle();
}
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, MemCheck* mc)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(BPDIALOG_PURPOSE::EDIT_MC)
, m_mc(mc)
{
m_ui.setupUi(this);
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
{
m_ui.rdoExecute->setChecked(true);
m_ui.chkEnable->setChecked(bp->enabled);
m_ui.txtAddress->setText(QtUtils::FilledQStringFromValue(bp->addr, 16));
m_ui.txtAddress->setText(QtUtils::FilledQStringFromValue(m_mc->start, 16));
m_ui.txtSize->setText(QString::number(m_mc->end - m_mc->start, 16).toUpper());
m_ui.chkLog->setChecked(m_mc->result & MEMCHECK_LOG);
m_ui.chkEnable->setChecked(m_mc->result & MEMCHECK_BREAK);
m_ui.chkChange->setChecked(m_mc->cond & MEMCHECK_WRITE_ONCHANGE);
m_ui.chkRead->setChecked(m_mc->cond & MEMCHECK_READ);
m_ui.chkWrite->setChecked(m_mc->cond & MEMCHECK_WRITE);
if (bp->hasCond)
m_ui.txtCondition->setText(QString::fromLocal8Bit(bp->cond.expressionString));
}
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
{
m_ui.rdoMemory->setChecked(true);
connect(m_ui.rdoExecute, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
m_ui.rdoMemory->toggle();
m_ui.txtAddress->setText(QtUtils::FilledQStringFromValue(mc->start, 16));
m_ui.txtSize->setText(QtUtils::FilledQStringFromValue(mc->end - mc->start, 16));
m_ui.chkRead->setChecked(mc->cond & MEMCHECK_READ);
m_ui.chkWrite->setChecked(mc->cond & MEMCHECK_WRITE);
m_ui.chkChange->setChecked(mc->cond & MEMCHECK_WRITE_ONCHANGE);
m_ui.chkEnable->setChecked(mc->result & MEMCHECK_BREAK);
m_ui.chkLog->setChecked(mc->result & MEMCHECK_LOG);
}
}
BreakpointDialog::~BreakpointDialog()
@ -90,48 +90,67 @@ void BreakpointDialog::onRdoButtonToggled()
m_ui.chkLog->setEnabled(!isExecute);
}
// When we are creating a breakpoint, use m_bp or m_mc only as a place to store information for the new breakpoint
// When we are modifying a breakpoint, m_bp or m_mc is a pointer to the breakpoint we are updating
void BreakpointDialog::accept()
{
if (m_purpose == BPDIALOG_PURPOSE::CREATE)
if (m_purpose == PURPOSE::CREATE)
{
if (m_ui.rdoExecute->isChecked())
{
m_bp = new BreakPoint;
m_bp->cpu = m_cpu->getCpuType();
m_bp->cond.debug = m_cpu;
}
else
{
m_mc = new MemCheck;
m_mc->cpu = m_cpu->getCpuType();
}
m_bp_mc = BreakPoint();
else if (m_ui.rdoMemory->isChecked())
m_bp_mc = MemCheck();
}
PostfixExpression expression;
// Validate the address
u64 address;
if (!m_cpu->initExpression(m_ui.txtAddress->text().toLocal8Bit(), expression) || !m_cpu->parseExpression(expression, address))
if (auto* bp = std::get_if<BreakPoint>(&m_bp_mc))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid address \"%1\"").arg(m_ui.txtAddress->text()));
return;
PostfixExpression expr;
u64 address;
if (!m_cpu->initExpression(m_ui.txtAddress->text().toLocal8Bit().constData(), expr) ||
!m_cpu->parseExpression(expr, address))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid address \"%1\"").arg(m_ui.txtAddress->text()));
return;
}
bp->enabled = m_ui.chkEnable->isChecked();
if (!m_ui.txtCondition->text().isEmpty())
{
bp->hasCond = true;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toLocal8Bit().constData(), expr))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid condition \"%1\"").arg(getExpressionError()));
return;
}
bp->cond.expression = expr;
strncpy(&bp->cond.expressionString[0], m_ui.txtCondition->text().toLocal8Bit().constData(),
sizeof(bp->cond.expressionString));
}
}
u64 size;
if (m_ui.rdoMemory->isChecked())
if (auto* mc = std::get_if<MemCheck>(&m_bp_mc))
{
if (!m_cpu->initExpression(m_ui.txtSize->text().toLocal8Bit(), expression) || !m_cpu->parseExpression(expression, size) || !size)
PostfixExpression expr;
u64 startAddress;
if (!m_cpu->initExpression(m_ui.txtAddress->text().toLocal8Bit().constData(), expr) ||
!m_cpu->parseExpression(expr, startAddress))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid address \"%1\"").arg(m_ui.txtAddress->text()));
return;
}
u64 size;
if (!m_cpu->initExpression(m_ui.txtSize->text().toLocal8Bit(), expr) ||
!m_cpu->parseExpression(expr, size) || !size)
{
QMessageBox::warning(this, tr("Error"), tr("Invalid size \"%1\"").arg(m_ui.txtSize->text()));
return;
}
m_mc->start = address;
const bool changedSize = m_mc->end != (m_mc->start + size);
const u32 prevEnd = m_mc->end;
m_mc->end = m_mc->start + size;
mc->start = startAddress;
mc->end = startAddress + size;
int condition = 0;
if (m_ui.chkRead->isChecked())
@ -141,68 +160,24 @@ void BreakpointDialog::accept()
if (m_ui.chkChange->isChecked())
condition |= MEMCHECK_WRITE_ONCHANGE;
mc->cond = static_cast<MemCheckCondition>(condition);
int result = 0;
if (m_ui.chkEnable->isChecked())
result |= MEMCHECK_BREAK;
if (m_ui.chkLog->isChecked())
result |= MEMCHECK_LOG;
if (m_purpose == BPDIALOG_PURPOSE::CREATE)
{
Host::RunOnCPUThread([this, condition, result] {
CBreakPoints::AddMemCheck(m_cpu->getCpuType(), m_mc->start, m_mc->end, (MemCheckCondition)condition, (MemCheckResult)result);
delete m_mc;
});
}
else
{
Host::RunOnCPUThread([this, mc = *m_mc, condition, result, prevEnd, changedSize] {
if (changedSize)
{
CBreakPoints::RemoveMemCheck(m_cpu->getCpuType(), mc.start, prevEnd);
CBreakPoints::AddMemCheck(m_cpu->getCpuType(), mc.start, mc.end, static_cast<MemCheckCondition>(condition), static_cast<MemCheckResult>(result));
}
else
{
CBreakPoints::ChangeMemCheck(m_cpu->getCpuType(), mc.start, mc.end, static_cast<MemCheckCondition>(condition), static_cast<MemCheckResult>(result));
}
});
}
mc->result = static_cast<MemCheckResult>(result);
}
else
if (m_purpose == PURPOSE::EDIT)
{
if (m_purpose == BPDIALOG_PURPOSE::CREATE)
Host::RunOnCPUThread([this, address] {
CBreakPoints::AddBreakPoint(m_cpu->getCpuType(), address);
});
// Validate the condition
// TODO: Expression management in the core should be updated to make this prettier
if (!m_ui.txtCondition->text().isEmpty())
{
auto strData = m_ui.txtCondition->text().toLocal8Bit();
expression.clear();
if (!m_cpu->initExpression(strData.constData(), expression))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid condition \"%1\"").arg(strData));
return;
}
m_bp->cond.expression = expression;
strncpy(&m_bp->cond.expressionString[0], strData.constData(), sizeof(m_bp->cond.expressionString));
Host::RunOnCPUThread([this, address] {
CBreakPoints::ChangeBreakPointAddCond(m_cpu->getCpuType(), address, m_bp->cond);
});
}
else
{
Host::RunOnCPUThread([this, address] { CBreakPoints::ChangeBreakPointRemoveCond(m_cpu->getCpuType(), address); });
}
Host::RunOnCPUThread([this, address] { CBreakPoints::ChangeBreakPoint(m_cpu->getCpuType(), address, m_ui.chkEnable->isChecked()); });
m_bp->addr = address;
m_bpModel.removeRows(m_rowIndex, 1);
}
m_bpModel.insertRows(0, 1, {m_bp_mc});
QDialog::accept();
}

View File

@ -19,6 +19,8 @@
#include "DebugTools/Breakpoints.h"
#include "Models/BreakpointModel.h"
#include <QtWidgets/QDialog>
class BreakpointDialog final : public QDialog
@ -26,28 +28,26 @@ class BreakpointDialog final : public QDialog
Q_OBJECT
public:
BreakpointDialog(QWidget* parent, DebugInterface* cpu);
BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakPoint* bp);
BreakpointDialog(QWidget* parent, DebugInterface* cpu, MemCheck* mc);
BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakpointModel& model);
BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakpointModel& model, BreakpointMemcheck bpmc, int rowIndex);
~BreakpointDialog();
public slots:
void onRdoButtonToggled();
void accept() override;
private:
enum class BPDIALOG_PURPOSE
enum class PURPOSE
{
CREATE,
EDIT_BP,
EDIT_MC
EDIT
};
Ui::BreakpointDialog m_ui;
DebugInterface* m_cpu;
const BPDIALOG_PURPOSE m_purpose;
BreakPoint* m_bp = nullptr;
MemCheck* m_mc = nullptr;
};
const PURPOSE m_purpose;
BreakpointModel& m_bpModel;
BreakpointMemcheck m_bp_mc;
int m_rowIndex;
};

View File

@ -19,6 +19,7 @@
#include "DisassemblyWidget.h"
#include "BreakpointDialog.h"
#include "Models/BreakpointModel.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
@ -27,7 +28,6 @@
#include "common/BitCast.h"
#include "QtUtils.h"
#include <QtWidgets/QHeaderView>
#include <QtGui/QClipboard>
#include <QtWidgets/QMessageBox>
#include <QtConcurrent/QtConcurrent>
@ -40,6 +40,7 @@ using namespace MipsStackWalk;
CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
: m_cpu(cpu)
, m_bpModel(cpu)
{
m_ui.setupUi(this);
@ -55,9 +56,10 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
connect(m_ui.registerWidget, &RegisterWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets);
connect(m_ui.disassemblyWidget, &DisassemblyWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets);
connect(m_ui.tabWidget, &QTabWidget::currentChanged, [this] { fixBPListColumnSize(); });
connect(m_ui.breakpointList, &QTableWidget::customContextMenuRequested, this, &CpuWidget::onBPListContextMenu);
connect(m_ui.breakpointList, &QTableWidget::itemChanged, this, &CpuWidget::onBPListItemChange);
connect(m_ui.breakpointList, &QTableView::customContextMenuRequested, this, &CpuWidget::onBPListContextMenu);
connect(m_ui.breakpointList, &QTableView::doubleClicked, this, &CpuWidget::onBPListDoubleClicked);
m_ui.breakpointList->setModel(&m_bpModel);
connect(m_ui.threadList, &QTableWidget::customContextMenuRequested, this, &CpuWidget::onThreadListContextMenu);
connect(m_ui.threadList, &QTableWidget::cellDoubleClicked, this, &CpuWidget::onThreadListDoubleClick);
@ -86,9 +88,6 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
m_ui.registerWidget->SetCpu(&cpu);
m_ui.memoryviewWidget->SetCpu(&cpu);
if (m_cpu.getCpuType() == BREAKPOINT_EE)
CBreakPoints::SetUpdateHandler(std::bind(&CpuWidget::reloadCPUWidgets, this));
this->repaint();
}
@ -101,11 +100,6 @@ void CpuWidget::paintEvent(QPaintEvent* event)
m_ui.memoryviewWidget->update();
}
void CpuWidget::resizeEvent(QResizeEvent* event)
{
fixBPListColumnSize();
}
// The cpu shouldn't be alive when these are called
// But make sure it isn't just in case
void CpuWidget::onStepInto()
@ -234,145 +228,18 @@ void CpuWidget::onVMPaused()
void CpuWidget::updateBreakpoints()
{
m_ui.breakpointList->blockSignals(true);
m_ui.breakpointList->setRowCount(0);
m_bplistObjects.clear();
int iter = 0;
for (const auto& breakpoint : CBreakPoints::GetBreakpoints())
{
if (breakpoint.cpu != m_cpu.getCpuType())
continue;
if (breakpoint.temporary)
continue;
m_ui.breakpointList->insertRow(iter);
BreakpointObject obj;
obj.bp = std::make_shared<BreakPoint>(breakpoint);
m_bplistObjects.push_back(obj);
// Type (R/O)
QTableWidgetItem* typeItem = new QTableWidgetItem();
typeItem->setText(tr("Execute"));
typeItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 0, typeItem);
// Offset (R/O), possibly allow changing offset???
QTableWidgetItem* offsetItem = new QTableWidgetItem();
offsetItem->setText(FilledQStringFromValue(breakpoint.addr, 16));
offsetItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 1, offsetItem);
// Size & Label (R/O)
QTableWidgetItem* sizeLabelItem = new QTableWidgetItem();
sizeLabelItem->setText(m_cpu.GetSymbolMap().GetLabelString(breakpoint.addr).c_str());
sizeLabelItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 2, sizeLabelItem);
// Opcode (R/O)
QTableWidgetItem* opcodeItem = new QTableWidgetItem();
opcodeItem->setText(m_ui.disassemblyWidget->GetLineDisasm(breakpoint.addr));
opcodeItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 3, opcodeItem);
// Condition (R/W)
QTableWidgetItem* conditionItem = new QTableWidgetItem();
conditionItem->setText(breakpoint.hasCond ? QString::fromLocal8Bit(breakpoint.cond.expressionString) : "");
conditionItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEditable);
m_ui.breakpointList->setItem(iter, 4, conditionItem);
// Hits (R/O) (Disabled for execute bp)
QTableWidgetItem* hitsItem = new QTableWidgetItem();
hitsItem->setText("N/A");
hitsItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 5, hitsItem);
// Enabled (R/W)
QTableWidgetItem* enabledItem = new QTableWidgetItem();
enabledItem->setCheckState(breakpoint.enabled ? Qt::Checked : Qt::Unchecked);
enabledItem->setFlags(Qt::ItemFlag::ItemIsUserCheckable | Qt::ItemFlag::ItemIsEnabled);
m_ui.breakpointList->setItem(iter, 6, enabledItem);
iter++;
}
for (const auto& memcheck : CBreakPoints::GetMemChecks())
{
if (memcheck.cpu != m_cpu.getCpuType())
continue;
m_ui.breakpointList->insertRow(iter);
BreakpointObject obj;
obj.mc = std::make_shared<MemCheck>(memcheck);
m_bplistObjects.push_back(obj);
// Type (R/O)
QTableWidgetItem* typeItem = new QTableWidgetItem();
QString type("");
type += (memcheck.cond & MEMCHECK_READ) ? tr("Read") : "";
type += ((memcheck.cond & MEMCHECK_BOTH) == MEMCHECK_BOTH) ? ", " : " ";
type += (memcheck.cond & MEMCHECK_WRITE) ? (memcheck.cond & MEMCHECK_WRITE_ONCHANGE) ? tr("Write(C)") : tr("Write") : "";
typeItem->setText(type);
typeItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 0, typeItem);
// Offset (R/O), possibly allow changing offset?
QTableWidgetItem* offsetItem = new QTableWidgetItem();
offsetItem->setText(FilledQStringFromValue(memcheck.start, 16));
offsetItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 1, offsetItem);
// Size & Label (R/O)
QTableWidgetItem* sizeLabelItem = new QTableWidgetItem();
sizeLabelItem->setText(QString::number(memcheck.end - memcheck.start, 16));
sizeLabelItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEditable);
m_ui.breakpointList->setItem(iter, 2, sizeLabelItem);
// Opcode (R/O)
QTableWidgetItem* opcodeItem = new QTableWidgetItem();
opcodeItem->setText(m_ui.disassemblyWidget->GetLineDisasm(memcheck.start));
opcodeItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 3, opcodeItem);
// Condition (R/W) (Disabled for memchecks)
QTableWidgetItem* conditionItem = new QTableWidgetItem();
conditionItem->setText("N/A");
conditionItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 4, conditionItem);
// Hits (R/O)
QTableWidgetItem* hitsItem = new QTableWidgetItem();
hitsItem->setText(QString::number(memcheck.numHits));
hitsItem->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable);
m_ui.breakpointList->setItem(iter, 5, hitsItem);
// Enabled (R/W)
QTableWidgetItem* enabledItem = new QTableWidgetItem();
enabledItem->setCheckState((memcheck.result & MEMCHECK_BREAK) ? Qt::Checked : Qt::Unchecked);
enabledItem->setFlags(Qt::ItemFlag::ItemIsUserCheckable | Qt::ItemFlag::ItemIsEnabled);
m_ui.breakpointList->setItem(iter, 6, enabledItem);
iter++;
}
m_ui.breakpointList->blockSignals(false);
m_bpModel.refreshData();
}
void CpuWidget::fixBPListColumnSize()
void CpuWidget::onBPListDoubleClicked(const QModelIndex& index)
{
m_ui.breakpointList->horizontalHeader()->resizeSection(0, 90);
m_ui.breakpointList->horizontalHeader()->resizeSection(1, 65);
m_ui.breakpointList->horizontalHeader()->resizeSection(5, 40);
m_ui.breakpointList->horizontalHeader()->resizeSection(6, 60);
constexpr int currentWidthTotal = 90 + 65 + 40 + 60;
const int sectionWidth = (m_ui.breakpointList->width() - currentWidthTotal) / 3.0f;
m_ui.breakpointList->horizontalHeader()->resizeSection(2, sectionWidth);
m_ui.breakpointList->horizontalHeader()->resizeSection(3, sectionWidth);
m_ui.breakpointList->horizontalHeader()->resizeSection(4, sectionWidth);
if (index.isValid())
{
if (index.column() == BreakpointModel::OFFSET)
{
m_ui.disassemblyWidget->gotoAddress(m_bpModel.data(index).toString().toUInt(nullptr, 16));
}
}
}
void CpuWidget::onBPListContextMenu(QPoint pos)
@ -397,8 +264,6 @@ void CpuWidget::onBPListContextMenu(QPoint pos)
connect(editAction, &QAction::triggered, this, &CpuWidget::contextBPListEdit);
m_bplistContextMenu->addAction(editAction);
// Only copy when one column is selected
// Shouldn't be trivial to support cross column copy
if (selModel->selectedIndexes().count() == 1)
{
QAction* copyAction = new QAction(tr("Copy"), m_ui.breakpointList);
@ -414,81 +279,6 @@ void CpuWidget::onBPListContextMenu(QPoint pos)
m_bplistContextMenu->popup(m_ui.breakpointList->mapToGlobal(pos));
}
void CpuWidget::onBPListItemChange(QTableWidgetItem* item)
{
if (item->column() == 2 && m_bplistObjects.at(item->row()).mc) // Size / Label column. Size is editable for memchecks
{
const auto& mc = m_bplistObjects.at(item->row()).mc;
bool ok;
u32 val = item->text().toUInt(&ok, 16);
if (!ok)
{
QMessageBox::warning(this, tr("Error"), tr("Invalid size \"%1\"").arg(item->text()));
item->setText(QString::number((mc->end - mc->start), 16));
return;
}
if (val == (mc->end - mc->start))
{
return;
}
Host::RunOnCPUThread([this, val, mc] {
CBreakPoints::RemoveMemCheck(m_cpu.getCpuType(), mc->start, mc->end);
CBreakPoints::AddMemCheck(m_cpu.getCpuType(), mc->start, mc->start + val, mc->cond, mc->result);
});
updateBreakpoints();
}
else if (item->column() == 4 && m_bplistObjects.at(item->row()).bp) // Condition column. Only editable for breakpoints
{
const auto& bp = m_bplistObjects.at(item->row()).bp;
if (item->text().isEmpty() && bp->hasCond)
{
Host::RunOnCPUThread([this, bp] {
CBreakPoints::ChangeBreakPointRemoveCond(m_cpu.getCpuType(), bp->addr);
});
updateBreakpoints();
}
else if (item->text() != QString::fromLocal8Bit(&bp->cond.expressionString[0]))
{
PostfixExpression expression;
if (!m_cpu.initExpression(item->text().toLocal8Bit().constData(), expression))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid condition \"%1\"").arg(item->text()));
item->setText(QString::fromLocal8Bit(&bp->cond.expressionString[0]));
return;
}
BreakPointCond cond;
cond.debug = &m_cpu;
cond.expression = expression;
strcpy(&cond.expressionString[0], item->text().toLocal8Bit().constData());
Host::RunOnCPUThread([this, bp, cond] {
CBreakPoints::ChangeBreakPointAddCond(m_cpu.getCpuType(), bp->addr, cond);
});
updateBreakpoints();
}
}
else if (item->column() == 6)
{
auto bpmc = m_bplistObjects.at(item->row());
Host::RunOnCPUThread([this, bpmc, checked = item->checkState()] {
if (bpmc.bp)
{
CBreakPoints::ChangeBreakPoint(m_cpu.getCpuType(), bpmc.bp->addr, checked);
}
else
{
CBreakPoints::ChangeMemCheck(m_cpu.getCpuType(), bpmc.mc->start, bpmc.mc->end, bpmc.mc->cond, MemCheckResult(bpmc.mc->result ^ MEMCHECK_BREAK));
}
});
}
}
void CpuWidget::contextBPListCopy()
{
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
@ -496,7 +286,7 @@ void CpuWidget::contextBPListCopy()
if (!selModel->hasSelection())
return;
QGuiApplication::clipboard()->setText(m_ui.breakpointList->selectedItems().first()->text());
QGuiApplication::clipboard()->setText(m_bpModel.data(selModel->currentIndex()).toString());
}
void CpuWidget::contextBPListDelete()
@ -506,34 +296,21 @@ void CpuWidget::contextBPListDelete()
if (!selModel->hasSelection())
return;
int last_row = -1;
for (auto& index : selModel->selectedIndexes())
QModelIndexList rows = selModel->selectedIndexes();
std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) {
return a.row() > b.row();
});
for (const QModelIndex& index : rows)
{
if (index.row() == last_row) // If the next index is in the same row, don't delete that breakpoint twice!
continue;
auto& bpObject = m_bplistObjects.at(index.row());
Host::RunOnCPUThread([&] {
if (bpObject.bp)
{
CBreakPoints::RemoveBreakPoint(m_cpu.getCpuType(), bpObject.bp->addr);
}
else
{
CBreakPoints::RemoveMemCheck(m_cpu.getCpuType(), bpObject.mc->start, bpObject.mc->end);
}
});
last_row = index.row();
m_bpModel.removeRows(index.row(), 1);
}
updateBreakpoints();
}
void CpuWidget::contextBPListNew()
{
BreakpointDialog* bpDialog = new BreakpointDialog(this, &m_cpu);
connect(bpDialog, &BreakpointDialog::accepted, this, &CpuWidget::updateBreakpoints);
BreakpointDialog* bpDialog = new BreakpointDialog(this, &m_cpu, m_bpModel);
bpDialog->show();
}
@ -544,20 +321,11 @@ void CpuWidget::contextBPListEdit()
if (!selModel->hasSelection())
return;
auto& bpObject = m_bplistObjects.at(selModel->selectedIndexes().first().row());
const int selectedRow = selModel->selectedIndexes().first().row();
BreakpointDialog* bpDialog;
auto bpObject = m_bpModel.at(selectedRow);
if (bpObject.bp)
{
bpDialog = new BreakpointDialog(this, &m_cpu, bpObject.bp.get());
}
else
{
bpDialog = new BreakpointDialog(this, &m_cpu, bpObject.mc.get());
}
connect(bpDialog, &BreakpointDialog::accepted, this, &CpuWidget::updateBreakpoints);
BreakpointDialog* bpDialog = new BreakpointDialog(this, &m_cpu, m_bpModel, bpObject, selectedRow);
bpDialog->show();
}
@ -566,6 +334,8 @@ void CpuWidget::updateFunctionList(bool whenEmpty)
if (!m_cpu.isAlive())
return;
m_bpModel.refreshData();
if (whenEmpty && m_ui.listFunctions->count())
return;

View File

@ -22,6 +22,8 @@
#include "DebugTools/BiosDebugData.h"
#include "DebugTools/MipsStackWalk.h"
#include "Models/BreakpointModel.h"
#include "QtHost.h"
#include <QtWidgets/QWidget>
#include <QtWidgets/QTableWidget>
@ -39,7 +41,6 @@ public:
~CpuWidget();
public slots:
void resizeEvent(QResizeEvent* event);
void paintEvent(QPaintEvent* event);
void onStepInto();
@ -49,10 +50,9 @@ public slots:
void onVMPaused();
void updateBreakpoints();
void fixBPListColumnSize();
void onBPListDoubleClicked(const QModelIndex& index);
void onBPListContextMenu(QPoint pos);
void onBPListItemChange(QTableWidgetItem* item);
void contextBPListCopy();
void contextBPListDelete();
@ -82,7 +82,6 @@ public slots:
updateBreakpoints();
updateThreads();
updateStackFrames();
updateFunctionList();
m_ui.registerWidget->update();
m_ui.disassemblyWidget->update();
@ -103,15 +102,8 @@ private:
DebugInterface& m_cpu;
// Poor mans variant
// Allows us to map row index to breakpoint / memcheck objects
struct BreakpointObject
{
std::shared_ptr<BreakPoint> bp;
std::shared_ptr<MemCheck> mc;
};
BreakpointModel m_bpModel;
std::vector<BreakpointObject> m_bplistObjects;
std::vector<EEThread> m_threadlistObjects;
EEThread m_activeThread;
std::vector<StackFrame> m_stacklistObjects;

View File

@ -322,7 +322,7 @@
<enum>QTabWidget::South</enum>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="tab_memory">
<property name="sizePolicy">
@ -403,7 +403,7 @@
<number>0</number>
</property>
<item>
<widget class="QTableWidget" name="breakpointList">
<widget class="QTableView" name="breakpointList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@ -413,59 +413,6 @@
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="rowCount">
<number>0</number>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>75</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Offset</string>
</property>
</column>
<column>
<property name="text">
<string>Size / Label</string>
</property>
</column>
<column>
<property name="text">
<string>Opcode</string>
</property>
</column>
<column>
<property name="text">
<string>Condition</string>
</property>
</column>
<column>
<property name="text">
<string>Hits</string>
</property>
</column>
<column>
<property name="text">
<string>Enabled</string>
</property>
</column>
</widget>
</item>
</layout>

View File

@ -61,6 +61,9 @@ DebuggerWindow::DebuggerWindow(QWidget* parent)
m_ui.cpuTabs->addTab(m_cpuWidget_r5900, "R5900");
m_ui.cpuTabs->addTab(m_cpuWidget_r3000, "R3000");
CBreakPoints::SetUpdateHandler(std::bind(&DebuggerWindow::onBreakpointsChanged, this));
return;
}
@ -121,3 +124,9 @@ void DebuggerWindow::onStepOut()
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
currentCpu->onStepOut();
}
void DebuggerWindow::onBreakpointsChanged()
{
m_cpuWidget_r5900->reloadCPUWidgets();
m_cpuWidget_r3000->reloadCPUWidgets();
}

View File

@ -33,6 +33,7 @@ public slots:
void onStepInto();
void onStepOver();
void onStepOut();
void onBreakpointsChanged();
private:
Ui::DebuggerWindow m_ui;

View File

@ -0,0 +1,314 @@
/* 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 "BreakpointModel.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "DebugTools/DisassemblyManager.h"
#include "QtHost.h"
#include "QtUtils.h"
#include <QtWidgets/QMessageBox>
#include <algorithm>
BreakpointModel::BreakpointModel(DebugInterface& cpu, QObject* parent)
: QAbstractTableModel(parent)
, m_cpu(cpu)
{
}
int BreakpointModel::rowCount(const QModelIndex&) const
{
return m_breakpoints.size();
}
int BreakpointModel::columnCount(const QModelIndex&) const
{
return BreakpointColumns::COLUMN_COUNT;
}
QVariant BreakpointModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole)
{
auto bp_mc = m_breakpoints.at(index.row());
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
{
switch (index.column())
{
case BreakpointColumns::TYPE:
return tr("Execute");
case BreakpointColumns::OFFSET:
return QtUtils::FilledQStringFromValue(bp->addr, 16);
case BreakpointColumns::SIZE_LABEL:
return m_cpu.GetSymbolMap().GetLabelString(bp->addr).c_str();
case BreakpointColumns::OPCODE:
// Note: Fix up the disassemblymanager so we can use it here, instead of calling a function through the disassemblyview (yuck)
return m_cpu.disasm(bp->addr, true).c_str();
case BreakpointColumns::CONDITION:
return bp->hasCond ? QString::fromLocal8Bit(bp->cond.expressionString) : tr("No Condition");
case BreakpointColumns::HITS:
return tr("--");
case BreakpointColumns::ENABLED:
return bp->enabled ? tr("Enabled") : tr("Disabled");
}
}
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
{
switch (index.column())
{
case BreakpointColumns::TYPE:
{
QString type("");
type += (mc->cond & MEMCHECK_READ) ? tr("Read") : "";
type += ((mc->cond & MEMCHECK_BOTH) == MEMCHECK_BOTH) ? ", " : " ";
type += (mc->cond & MEMCHECK_WRITE) ? (mc->cond & MEMCHECK_WRITE_ONCHANGE) ? tr("Write(C)") : tr("Write") : "";
return type;
}
case BreakpointColumns::OFFSET:
return QtUtils::FilledQStringFromValue(mc->start, 16);
case BreakpointColumns::SIZE_LABEL:
return QString::number(mc->end - mc->start, 16);
case BreakpointColumns::OPCODE:
return tr("--"); // Our address is going to point to memory, no purpose in printing the op
case BreakpointColumns::CONDITION:
return tr("--"); // No condition on memchecks
case BreakpointColumns::HITS:
return QString::number(mc->numHits);
case BreakpointColumns::ENABLED:
return (mc->result & MEMCHECK_BREAK) ? tr("Enabled") : tr("Disabled");
}
}
}
else if (role == Qt::CheckStateRole)
{
if (index.column() == 6)
{
auto bp_mc = m_breakpoints.at(index.row());
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
{
return bp->enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked;
}
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
{
return (mc->result & MEMCHECK_BREAK) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked;
}
}
}
return QVariant();
}
QVariant BreakpointModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case BreakpointColumns::TYPE:
return tr("TYPE");
case BreakpointColumns::OFFSET:
return tr("OFFSET");
case BreakpointColumns::SIZE_LABEL:
return tr("SIZE / LABEL");
case BreakpointColumns::OPCODE:
return tr("INSTRUCTION");
case BreakpointColumns::CONDITION:
return tr("CONDITION");
case BreakpointColumns::HITS:
return tr("HITS");
case BreakpointColumns::ENABLED:
return tr("ENABLED");
default:
return QVariant();
}
}
return QVariant();
}
Qt::ItemFlags BreakpointModel::flags(const QModelIndex& index) const
{
volatile const int row = index.row();
bool is_breakpoint = std::holds_alternative<BreakPoint>(m_breakpoints.at(row));
switch (index.column())
{
case BreakpointColumns::CONDITION:
if (is_breakpoint)
return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEditable;
[[fallthrough]];
case BreakpointColumns::TYPE:
case BreakpointColumns::OPCODE:
case BreakpointColumns::HITS:
case BreakpointColumns::OFFSET:
case BreakpointColumns::SIZE_LABEL:
return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable;
case BreakpointColumns::ENABLED:
return Qt::ItemFlag::ItemIsUserCheckable | Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable;
}
return index.flags();
}
bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
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()] {
CBreakPoints::ChangeBreakPoint(cpu, bp.addr, enabled);
});
}
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
{
Host::RunOnCPUThread([cpu = this->m_cpu.getCpuType(), mc = *mc] {
CBreakPoints::ChangeMemCheck(cpu, mc.start, mc.end, mc.cond,
MemCheckResult(mc.result ^ MEMCHECK_BREAK));
});
}
return true;
}
else if (role == Qt::EditRole && index.column() == BreakpointColumns::CONDITION)
{
auto bp_mc = m_breakpoints.at(index.row());
if (std::holds_alternative<MemCheck>(bp_mc))
return false;
const auto bp = std::get<BreakPoint>(bp_mc);
const QString condValue = value.toString();
if (condValue.isEmpty())
{
if (bp.hasCond)
{
Host::RunOnCPUThread([cpu = m_cpu.getCpuType(), bp] {
CBreakPoints::ChangeBreakPointRemoveCond(cpu, bp.addr);
});
}
}
else
{
PostfixExpression expr;
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr))
{
QMessageBox::warning(nullptr, "Condition Error", QString(getExpressionError()));
return false;
}
BreakPointCond cond;
cond.debug = &m_cpu;
cond.expression = expr;
strcpy(&cond.expressionString[0], condValue.toLocal8Bit().constData());
Host::RunOnCPUThread([cpu = m_cpu.getCpuType(), bp, cond] {
CBreakPoints::ChangeBreakPointAddCond(cpu, bp.addr, cond);
});
return true;
}
}
return false;
}
bool BreakpointModel::removeRows(int row, int count, const QModelIndex& index)
{
beginRemoveRows(index, row, row + count);
for (int i = row; i < row + count; i++)
{
auto bp_mc = m_breakpoints.at(i);
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
{
Host::RunOnCPUThread([cpu = m_cpu.getCpuType(), addr = bp->addr] {
CBreakPoints::RemoveBreakPoint(cpu, addr);
});
}
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
{
Host::RunOnCPUThread([cpu = m_cpu.getCpuType(), start = mc->start, end = mc->end] {
CBreakPoints::RemoveMemCheck(cpu, start, end);
});
}
}
endRemoveRows();
return true;
}
bool BreakpointModel::insertRows(int row, int count, std::vector<BreakpointMemcheck> breakpoints, const QModelIndex& index)
{
if (breakpoints.size() != count)
return false;
beginInsertRows(index, row, row + count);
for (const auto& bp_mc : breakpoints)
{
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
{
Host::RunOnCPUThread([cpu = m_cpu.getCpuType(), bp = *bp] {
CBreakPoints::AddBreakPoint(cpu, bp.addr);
if (bp.hasCond)
{
CBreakPoints::ChangeBreakPointAddCond(cpu, bp.addr, bp.cond);
}
});
}
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
{
Host::RunOnCPUThread([cpu = m_cpu.getCpuType(), mc = *mc] {
CBreakPoints::AddMemCheck(cpu, mc.start, mc.end, mc.cond, mc.result);
});
}
}
endInsertRows();
return true;
}
void BreakpointModel::refreshData()
{
beginResetModel();
m_breakpoints.clear();
auto breakpoints = CBreakPoints::GetBreakpoints(m_cpu.getCpuType(), false);
for (const auto& bp : breakpoints)
{
m_breakpoints.push_back(bp);
}
auto memchecks = CBreakPoints::GetMemChecks(m_cpu.getCpuType());
for (const auto& mc : memchecks)
{
m_breakpoints.push_back(mc);
}
endResetModel();
}

View File

@ -0,0 +1,59 @@
/* 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/Breakpoints.h"
using BreakpointMemcheck = std::variant<BreakPoint, MemCheck>;
class BreakpointModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum BreakpointColumns : int
{
TYPE = 0,
OFFSET,
SIZE_LABEL,
OPCODE,
CONDITION,
HITS,
ENABLED,
COLUMN_COUNT
};
explicit BreakpointModel(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;
Qt::ItemFlags flags(const QModelIndex& index) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
bool removeRows(int row, int count, const QModelIndex& index = QModelIndex()) override;
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:
DebugInterface& m_cpu;
std::vector<BreakpointMemcheck> m_breakpoints;
};

View File

@ -48,7 +48,7 @@
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)pcsx2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<!-- Needed for moc pch -->
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\Tools\InputRecording;$(ProjectDir)\Debugger</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\Tools\InputRecording;$(ProjectDir)\Debugger;$(ProjectDir)\Debugger\Models</AdditionalIncludeDirectories>
<ExceptionHandling>Async</ExceptionHandling>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
@ -148,6 +148,7 @@
<ClCompile Include="Debugger\MemoryViewWidget.cpp" />
<ClCompile Include="Debugger\RegisterWidget.cpp" />
<ClCompile Include="Debugger\BreakpointDialog.cpp" />
<ClCompile Include="Debugger\Models\BreakpointModel.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
<ClCompile Include="Settings\ControllerGlobalSettingsWidget.cpp" />
@ -228,6 +229,7 @@
<QtMoc Include="Debugger\MemoryViewWidget.h" />
<QtMoc Include="Debugger\RegisterWidget.h" />
<QtMoc Include="Debugger\BreakpointDialog.h" />
<QtMoc Include="Debugger\Models\BreakpointModel.h" />
<QtMoc Include="Settings\ControllerBindingWidgets.h" />
<QtMoc Include="Settings\ControllerGlobalSettingsWidget.h" />
<ClInclude Include="Settings\MemoryCardConvertWorker.h" />
@ -278,6 +280,7 @@
<ClCompile Include="$(IntDir)Debugger\moc_RegisterWidget.cpp" />
<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)GameList\moc_GameListModel.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListRefreshThread.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListWidget.cpp" />

View File

@ -22,6 +22,9 @@
<Filter Include="Debugger">
<UniqueIdentifier>{ddb40cc4-9996-4ade-b647-eb549063553c}</UniqueIdentifier>
</Filter>
<Filter Include="Debugger\Models">
<UniqueIdentifier>{f4084ca0-d9d5-4584-b9d2-063db9f67de2}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\pcsx2\PCSX2.rc">
@ -274,6 +277,9 @@
<ClCompile Include="Debugger\BreakpointDialog.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\Models\BreakpointModel.cpp">
<Filter>Debugger\Models</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
@ -292,6 +298,9 @@
<ClCompile Include="$(IntDir)Debugger\moc_BreakpointDialog.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Debugger\Models\moc_BreakpointModel.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -429,6 +438,9 @@
<QtMoc Include="Debugger\BreakpointDialog.h">
<Filter>Debugger</Filter>
</QtMoc>
<QtMoc Include="Debugger\Models\BreakpointModel.h">
<Filter>Debugger\Models</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">

View File

@ -27,7 +27,7 @@ u64 CBreakPoints::breakSkipFirstTicksEE_ = 0;
u32 CBreakPoints::breakSkipFirstAtIop_ = 0;
u64 CBreakPoints::breakSkipFirstTicksIop_ = 0;
std::vector<MemCheck> CBreakPoints::memChecks_;
std::vector<MemCheck *> CBreakPoints::cleanupMemChecks_;
std::vector<MemCheck*> CBreakPoints::cleanupMemChecks_;
bool CBreakPoints::breakpointTriggered_ = false;
bool CBreakPoints::corePaused = false;
std::function<void()> CBreakPoints::cb_bpUpdated_;
@ -171,8 +171,10 @@ bool CBreakPoints::IsAddressBreakPoint(BreakPointCpu cpu, u32 addr)
bool CBreakPoints::IsAddressBreakPoint(BreakPointCpu cpu, u32 addr, bool* enabled)
{
size_t bp = FindBreakpoint(cpu, addr);
if (bp == INVALID_BREAKPOINT) return false;
if (enabled != NULL) *enabled = breakPoints_[bp].enabled;
if (bp == INVALID_BREAKPOINT)
return false;
if (enabled != NULL)
*enabled = breakPoints_[bp].enabled;
return true;
}
@ -244,7 +246,7 @@ void CBreakPoints::ClearTemporaryBreakPoints()
if (breakPoints_.empty())
return;
for (int i = (int)breakPoints_.size()-1; i >= 0; --i)
for (int i = (int)breakPoints_.size() - 1; i >= 0; --i)
{
if (breakPoints_[i].temporary)
{
@ -254,7 +256,7 @@ void CBreakPoints::ClearTemporaryBreakPoints()
}
}
void CBreakPoints::ChangeBreakPointAddCond(BreakPointCpu cpu, u32 addr, const BreakPointCond &cond)
void CBreakPoints::ChangeBreakPointAddCond(BreakPointCpu cpu, u32 addr, const BreakPointCond& cond)
{
size_t bp = FindBreakpoint(cpu, addr, true, false);
if (bp != INVALID_BREAKPOINT)
@ -275,7 +277,7 @@ void CBreakPoints::ChangeBreakPointRemoveCond(BreakPointCpu cpu, u32 addr)
}
}
BreakPointCond *CBreakPoints::GetBreakPointCondition(BreakPointCpu cpu, u32 addr)
BreakPointCond* CBreakPoints::GetBreakPointCondition(BreakPointCpu cpu, u32 addr)
{
size_t bp = FindBreakpoint(cpu, addr, true, true);
//temp breakpoints are unconditional
@ -389,14 +391,35 @@ const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()
return ranges;
}
const std::vector<MemCheck> CBreakPoints::GetMemChecks()
const std::vector<MemCheck> CBreakPoints::GetMemChecks(BreakPointCpu cpu)
{
return memChecks_;
std::vector<MemCheck> memChecks;
std::copy_if(memChecks_.begin(), memChecks_.end(), std::back_inserter(memChecks), [cpu](MemCheck& mc) { return mc.cpu == cpu; });
return memChecks;
}
const std::vector<BreakPoint> CBreakPoints::GetBreakpoints()
const std::vector<BreakPoint> CBreakPoints::GetBreakpoints(BreakPointCpu cpu, bool includeTemp)
{
return breakPoints_;
std::vector<BreakPoint> breakPoints;
std::copy_if(breakPoints_.begin(), breakPoints_.end(), std::back_inserter(breakPoints), [cpu, includeTemp](BreakPoint& bp) {
if (bp.cpu == cpu)
{
if (includeTemp)
return true;
else
return bp.temporary == false;
}
else
{
return false;
}
});
if (includeTemp)
return breakPoints_;
return breakPoints;
}
void CBreakPoints::Update(BreakPointCpu cpu, u32 addr)
@ -409,14 +432,11 @@ void CBreakPoints::Update(BreakPointCpu cpu, u32 addr)
resume = true;
}
// if (addr != 0)
// Cpu->Clear(addr-4,8);
// else
SysClearExecutionCache();
SysClearExecutionCache();
if (resume)
r5900Debug.resumeCpu();
if(cb_bpUpdated_)
if (cb_bpUpdated_)
cb_bpUpdated_();
}

View File

@ -141,8 +141,13 @@ public:
// Includes uncached addresses.
static const std::vector<MemCheck> GetMemCheckRanges();
static const std::vector<MemCheck> GetMemChecks();
static const std::vector<BreakPoint> GetBreakpoints();
static const std::vector<MemCheck> GetMemChecks(BreakPointCpu cpu);
static const std::vector<BreakPoint> GetBreakpoints(BreakPointCpu cpu, bool includeTemp);
// Returns count of all non-temporary breakpoints
static size_t GetNumBreakpoints()
{
return std::count_if(breakPoints_.begin(), breakPoints_.end(), [](BreakPoint& bp) { return !bp.temporary; });
}
static size_t GetNumMemchecks() { return memChecks_.size(); }
static void Update(BreakPointCpu cpu = BREAKPOINT_IOP_AND_EE, u32 addr = 0);

View File

@ -70,13 +70,11 @@ void intMemcheck(u32 op, u32 bits, bool store)
start = standardizeBreakpointAddress(start);
u32 end = start + bits/8;
auto checks = CBreakPoints::GetMemChecks();
auto checks = CBreakPoints::GetMemChecks(BREAKPOINT_EE);
for (size_t i = 0; i < checks.size(); i++)
{
auto& check = checks[i];
if (check.cpu != BREAKPOINT_EE)
continue;
if (check.result == 0)
continue;
if ((check.cond & MEMCHECK_WRITE) == 0 && store)

View File

@ -157,13 +157,11 @@ void psxMemcheck(u32 op, u32 bits, bool store)
u32 end = start + bits / 8;
auto checks = CBreakPoints::GetMemChecks();
auto checks = CBreakPoints::GetMemChecks(BREAKPOINT_IOP);
for (size_t i = 0; i < checks.size(); i++)
{
auto& check = checks[i];
if (check.cpu != BREAKPOINT_IOP)
continue;
if (check.result == 0)
continue;
if ((check.cond & MEMCHECK_WRITE) == 0 && store)

View File

@ -1364,11 +1364,9 @@ static void psxRecMemcheck(u32 op, u32 bits, bool store)
// ecx = access address
// edx = access address+size
auto checks = CBreakPoints::GetMemChecks();
auto checks = CBreakPoints::GetMemChecks(BREAKPOINT_IOP);
for (size_t i = 0; i < checks.size(); i++)
{
if (checks[i].cpu != BREAKPOINT_IOP)
continue;
if (checks[i].result == 0)
continue;
if ((checks[i].cond & MEMCHECK_WRITE) == 0 && store)

View File

@ -1613,11 +1613,9 @@ void recMemcheck(u32 op, u32 bits, bool store)
// ecx = access address
// edx = access address+size
auto checks = CBreakPoints::GetMemChecks();
auto checks = CBreakPoints::GetMemChecks(BREAKPOINT_EE);
for (size_t i = 0; i < checks.size(); i++)
{
if (checks[i].cpu != BREAKPOINT_EE)
continue;
if (checks[i].result == 0)
continue;
if ((checks[i].cond & MEMCHECK_WRITE) == 0 && store)