Files
pcsx2/pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.cpp
T

242 lines
7.2 KiB
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "BreakpointDialog.h"
#include "AsyncDialogs.h"
#include "QtUtils.h"
#include "QtHost.h"
#include "Debugger/DebuggerWindow.h"
#include "DebugTools/Breakpoints.h"
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakpointModel& model)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(PURPOSE::CREATE)
, m_bpModel(model)
{
m_ui.setupUi(this);
m_ui.grpType->setEnabled(true);
m_ui.txtAddress->setEnabled(true);
connect(m_ui.rdoExecute, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
}
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakpointModel& model, BreakpointMemcheck bp_mc, int rowIndex)
: QDialog(parent)
, m_cpu(cpu)
, m_purpose(PURPOSE::EDIT)
, m_bpModel(model)
, m_bp_mc(bp_mc)
, m_rowIndex(rowIndex)
{
m_ui.setupUi(this);
connect(m_ui.rdoExecute, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
connect(m_ui.rdoMemory, &QRadioButton::toggled, this, &BreakpointDialog::onRdoButtonToggled);
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.txtDescription->setText(QString::fromStdString(bp->description));
m_ui.spnMaxHitCount->setValue(static_cast<int>(bp->maxHits));
m_ui.grpLogWhenHit->setChecked(bp->instrumentationEnabled);
m_ui.txtLogFormat->setText(QString::fromStdString(bp->logFormat));
m_ui.chkContinueOnHit->setChecked(bp->continueOnHit);
if (bp->hasCond)
m_ui.txtCondition->setText(QString::fromStdString(bp->cond.expressionString));
}
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
{
m_ui.rdoMemory->setChecked(true);
m_ui.txtAddress->setText(QtUtils::FilledQStringFromValue(mc->start, 16));
m_ui.txtSize->setText(QtUtils::FilledQStringFromValue(mc->end - mc->start, 16));
m_ui.txtDescription->setText(QString::fromStdString(mc->description));
m_ui.chkRead->setChecked(mc->memCond & MEMCHECK_READ);
m_ui.chkWrite->setChecked(mc->memCond & MEMCHECK_WRITE);
m_ui.chkChange->setChecked(mc->memCond & MEMCHECK_WRITE_ONCHANGE);
m_ui.chkEnable->setChecked(mc->result & MEMCHECK_BREAK);
m_ui.spnMaxHitCount->setValue(static_cast<int>(mc->maxHits));
m_ui.grpLogWhenHit->setChecked(mc->instrumentationEnabled);
m_ui.txtLogFormat->setText(QString::fromStdString(mc->logFormat));
m_ui.chkContinueOnHit->setChecked(mc->continueOnHit);
if (mc->hasCond)
m_ui.txtCondition->setText(QString::fromStdString(mc->cond.expressionString));
}
}
BreakpointDialog::~BreakpointDialog()
{
}
void BreakpointDialog::onRdoButtonToggled()
{
const bool isExecute = m_ui.rdoExecute->isChecked();
m_ui.grpMemory->setEnabled(!isExecute);
}
void BreakpointDialog::accept()
{
std::string error;
if (m_purpose == PURPOSE::CREATE)
{
if (m_ui.rdoExecute->isChecked())
m_bp_mc = BreakPoint();
else if (m_ui.rdoMemory->isChecked())
m_bp_mc = MemCheck();
}
if (auto* bp = std::get_if<BreakPoint>(&m_bp_mc))
{
PostfixExpression expr;
u64 address;
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), address, error))
{
AsyncDialogs::warning(g_debugger_window, tr("Invalid Address"), QString::fromStdString(error));
return;
}
// Reset totalHits if the address of the BP changes.
if (static_cast<u32>(address) != bp->addr)
bp->totalHits = 0;
bp->addr = address;
bp->description = m_ui.txtDescription->text().toStdString();
bp->maxHits = static_cast<u32>(m_ui.spnMaxHitCount->value());
bp->enabled = m_ui.chkEnable->isChecked();
const bool instrumentationEnabled = m_ui.grpLogWhenHit->isChecked();
const std::string logFormat = m_ui.txtLogFormat->text().toStdString();
if (instrumentationEnabled && !logFormat.empty() && !ValidateInstrumentationLogFormat(*m_cpu, logFormat, error))
{
AsyncDialogs::warning(g_debugger_window, tr("Invalid Log Format"), QString::fromStdString(error));
return;
}
bp->instrumentationEnabled = instrumentationEnabled;
bp->logFormat = logFormat;
bp->continueOnHit = m_ui.chkContinueOnHit->isChecked();
if (!m_ui.txtCondition->text().isEmpty())
{
bp->hasCond = true;
bp->cond.debug = m_cpu;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error))
{
AsyncDialogs::warning(g_debugger_window, tr("Invalid Condition"), QString::fromStdString(error));
return;
}
bp->cond.expression = expr;
bp->cond.expressionString = m_ui.txtCondition->text().toStdString();
}
else
{
bp->hasCond = false;
bp->cond = {};
}
}
if (auto* mc = std::get_if<MemCheck>(&m_bp_mc))
{
u64 startAddress;
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), startAddress, error))
{
AsyncDialogs::warning(g_debugger_window, tr("Invalid Address"), QString::fromStdString(error));
return;
}
u64 size;
if (!m_cpu->evaluateExpression(m_ui.txtSize->text().toStdString().c_str(), size, error) || !size)
{
AsyncDialogs::warning(g_debugger_window, tr("Invalid Size"), QString::fromStdString(error));
return;
}
// Reset totalHits if the address range of the MC changes.
if (static_cast<u32>(startAddress) != mc->start || static_cast<u32>(startAddress + size) != mc->end)
mc->totalHits = 0;
mc->start = startAddress;
mc->end = startAddress + size;
mc->maxHits = static_cast<u32>(m_ui.spnMaxHitCount->value());
mc->description = m_ui.txtDescription->text().toStdString();
const bool instrumentationEnabled = m_ui.grpLogWhenHit->isChecked();
const std::string logFormat = m_ui.txtLogFormat->text().toStdString();
if (instrumentationEnabled && !logFormat.empty() && !ValidateInstrumentationLogFormat(*m_cpu, logFormat, error))
{
AsyncDialogs::warning(g_debugger_window, tr("Invalid Log Format"), QString::fromStdString(error));
return;
}
mc->instrumentationEnabled = instrumentationEnabled;
mc->logFormat = logFormat;
mc->continueOnHit = m_ui.chkContinueOnHit->isChecked();
if (!m_ui.txtCondition->text().isEmpty())
{
mc->hasCond = true;
mc->cond.debug = m_cpu;
PostfixExpression expr;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error))
{
AsyncDialogs::warning(g_debugger_window, tr("Invalid Condition"), QString::fromStdString(error));
return;
}
mc->cond.expression = expr;
mc->cond.expressionString = m_ui.txtCondition->text().toStdString();
}
else
{
mc->hasCond = false;
mc->cond = {};
}
int condition = 0;
if (m_ui.chkRead->isChecked())
condition |= MEMCHECK_READ;
if (m_ui.chkWrite->isChecked())
condition |= MEMCHECK_WRITE;
if (m_ui.chkChange->isChecked())
condition |= MEMCHECK_WRITE_ONCHANGE;
mc->memCond = static_cast<MemCheckCondition>(condition);
int result = 0;
if (m_ui.chkEnable->isChecked())
result |= MEMCHECK_BREAK;
mc->result = static_cast<MemCheckResult>(result);
}
if (m_purpose == PURPOSE::EDIT)
{
m_bpModel.removeRows(m_rowIndex, 1);
}
m_bpModel.insertBreakpointRows(0, 1, {m_bp_mc});
QDialog::accept();
}
#include "moc_BreakpointDialog.cpp"