mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 22:29:39 -06:00
Merge pull request #11015 from TryTwo/Conditional_Breakpoints
Conditional breakpoints
This commit is contained in:
@ -15,6 +15,7 @@
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/PowerPC/BreakPoints.h"
|
||||
#include "Core/PowerPC/Expression.h"
|
||||
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
@ -86,7 +87,7 @@ void BreakpointWidget::CreateWidgets()
|
||||
m_table = new QTableWidget;
|
||||
m_table->setTabKeyNavigation(false);
|
||||
m_table->setContentsMargins(0, 0, 0, 0);
|
||||
m_table->setColumnCount(5);
|
||||
m_table->setColumnCount(6);
|
||||
m_table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
@ -160,7 +161,7 @@ void BreakpointWidget::Update()
|
||||
m_table->clear();
|
||||
|
||||
m_table->setHorizontalHeaderLabels(
|
||||
{tr("Active"), tr("Type"), tr("Function"), tr("Address"), tr("Flags")});
|
||||
{tr("Active"), tr("Type"), tr("Function"), tr("Address"), tr("Flags"), tr("Condition")});
|
||||
|
||||
int i = 0;
|
||||
m_table->setRowCount(i);
|
||||
@ -203,6 +204,13 @@ void BreakpointWidget::Update()
|
||||
|
||||
m_table->setItem(i, 4, create_item(flags));
|
||||
|
||||
QString condition;
|
||||
|
||||
if (bp.condition)
|
||||
condition = QString::fromStdString(bp.condition->GetText());
|
||||
|
||||
m_table->setItem(i, 5, create_item(condition));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
@ -387,12 +395,15 @@ void BreakpointWidget::OnContextMenu()
|
||||
|
||||
void BreakpointWidget::AddBP(u32 addr)
|
||||
{
|
||||
AddBP(addr, false, true, true);
|
||||
AddBP(addr, false, true, true, {});
|
||||
}
|
||||
|
||||
void BreakpointWidget::AddBP(u32 addr, bool temp, bool break_on_hit, bool log_on_hit)
|
||||
void BreakpointWidget::AddBP(u32 addr, bool temp, bool break_on_hit, bool log_on_hit,
|
||||
const QString& condition)
|
||||
{
|
||||
PowerPC::breakpoints.Add(addr, temp, break_on_hit, log_on_hit);
|
||||
PowerPC::breakpoints.Add(
|
||||
addr, temp, break_on_hit, log_on_hit,
|
||||
!condition.isEmpty() ? Expression::TryParse(condition.toUtf8().constData()) : std::nullopt);
|
||||
|
||||
emit BreakpointsChanged();
|
||||
Update();
|
||||
|
@ -21,7 +21,7 @@ public:
|
||||
~BreakpointWidget();
|
||||
|
||||
void AddBP(u32 addr);
|
||||
void AddBP(u32 addr, bool temp, bool break_on_hit, bool log_on_hit);
|
||||
void AddBP(u32 addr, bool temp, bool break_on_hit, bool log_on_hit, const QString& condition);
|
||||
void AddAddressMBP(u32 addr, bool on_read = true, bool on_write = true, bool do_log = true,
|
||||
bool do_break = true);
|
||||
void AddRangedMBP(u32 from, u32 to, bool do_read = true, bool do_write = true, bool do_log = true,
|
||||
|
@ -11,9 +11,11 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Core/PowerPC/Expression.h"
|
||||
#include "DolphinQt/Debugger/BreakpointWidget.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
|
||||
@ -35,16 +37,25 @@ void NewBreakpointDialog::CreateWidgets()
|
||||
auto* type_group = new QButtonGroup(this);
|
||||
|
||||
// Instruction BP
|
||||
auto* top_layout = new QHBoxLayout;
|
||||
m_instruction_bp = new QRadioButton(tr("Instruction Breakpoint"));
|
||||
m_instruction_bp->setChecked(true);
|
||||
type_group->addButton(m_instruction_bp);
|
||||
m_instruction_box = new QGroupBox;
|
||||
m_instruction_address = new QLineEdit;
|
||||
m_instruction_condition = new QLineEdit;
|
||||
m_cond_help_btn = new QPushButton(tr("Help"));
|
||||
|
||||
auto* instruction_layout = new QHBoxLayout;
|
||||
top_layout->addWidget(m_instruction_bp);
|
||||
top_layout->addStretch();
|
||||
top_layout->addWidget(m_cond_help_btn);
|
||||
|
||||
auto* instruction_layout = new QGridLayout;
|
||||
m_instruction_box->setLayout(instruction_layout);
|
||||
instruction_layout->addWidget(new QLabel(tr("Address:")));
|
||||
instruction_layout->addWidget(m_instruction_address);
|
||||
instruction_layout->addWidget(new QLabel(tr("Address:")), 0, 0);
|
||||
instruction_layout->addWidget(m_instruction_address, 0, 1);
|
||||
instruction_layout->addWidget(new QLabel(tr("Condition:")), 1, 0);
|
||||
instruction_layout->addWidget(m_instruction_condition, 1, 1);
|
||||
|
||||
// Memory BP
|
||||
m_memory_bp = new QRadioButton(tr("Memory Breakpoint"));
|
||||
@ -102,7 +113,7 @@ void NewBreakpointDialog::CreateWidgets()
|
||||
|
||||
auto* layout = new QVBoxLayout;
|
||||
|
||||
layout->addWidget(m_instruction_bp);
|
||||
layout->addLayout(top_layout);
|
||||
layout->addWidget(m_instruction_box);
|
||||
layout->addWidget(m_memory_bp);
|
||||
layout->addWidget(m_memory_box);
|
||||
@ -119,6 +130,8 @@ void NewBreakpointDialog::ConnectWidgets()
|
||||
connect(m_buttons, &QDialogButtonBox::accepted, this, &NewBreakpointDialog::accept);
|
||||
connect(m_buttons, &QDialogButtonBox::rejected, this, &NewBreakpointDialog::reject);
|
||||
|
||||
connect(m_cond_help_btn, &QPushButton::clicked, this, &NewBreakpointDialog::ShowConditionHelp);
|
||||
|
||||
connect(m_instruction_bp, &QRadioButton::toggled, this, &NewBreakpointDialog::OnBPTypeChanged);
|
||||
connect(m_memory_bp, &QRadioButton::toggled, this, &NewBreakpointDialog::OnBPTypeChanged);
|
||||
|
||||
@ -174,7 +187,15 @@ void NewBreakpointDialog::accept()
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent->AddBP(address, false, do_break, do_log);
|
||||
const QString condition = m_instruction_condition->text().trimmed();
|
||||
|
||||
if (!condition.isEmpty() && !Expression::TryParse(condition.toUtf8().constData()))
|
||||
{
|
||||
invalid_input(tr("Condition"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_parent->AddBP(address, false, do_break, do_log, condition);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -205,3 +226,46 @@ void NewBreakpointDialog::accept()
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void NewBreakpointDialog::ShowConditionHelp()
|
||||
{
|
||||
const auto message = QStringLiteral(
|
||||
"Set a code breakpoint for when an instruction is executed. Use with the code widget.\n"
|
||||
"\n"
|
||||
"Conditions:\n"
|
||||
"Sets an expression that is evaluated when a breakpoint is hit. If the expression is false "
|
||||
"or 0, the breakpoint is ignored until hit again. Statements should be separated by a comma. "
|
||||
"Only the last statement will be used to determine what to do.\n"
|
||||
"\n"
|
||||
"Registers that can be referenced:\n"
|
||||
"GPRs : r0..r31\n"
|
||||
"FPRs : f0..f31\n LR, CTR, PC\n"
|
||||
"\n"
|
||||
"Functions:\n"
|
||||
"Set a register: r1 = 8\n"
|
||||
"Casts: s8(0xff). Available: s8, u8, s16, u16, s32, u32\n"
|
||||
"Read Memory: read_u32(0x80000000). Available: u8, s8, u16, s16, u32, s32, f32, f64\n"
|
||||
"Write Memory: write_u32(r3, 0x80000000). Available: u8, u16, u32, f32, f64\n"
|
||||
"*currently writing will always be triggered\n"
|
||||
"\n"
|
||||
"Operations:\n"
|
||||
"Unary: -u, !u, ~u\n"
|
||||
"Math: * / + -, power: **, remainder: %, shift: <<, >>\n"
|
||||
"Compare: <, <=, >, >=, ==, !=, &&, ||\n"
|
||||
"Bitwise: &, |, ^\n"
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
"r4 == 1\n"
|
||||
"f0 == 1.0 && f2 < 10.0\n"
|
||||
"r26 <= r0 && ((r5 + 3) & -4) * ((r6 + 3) & -4)* 4 > r0\n"
|
||||
"p = r3 + 0x8, p == 0x8003510 && read_u32(p) != 0\n"
|
||||
"Write and break: r4 = 8, 1\n"
|
||||
"Write and continue: f3 = f1 + f2, 0\n"
|
||||
"The condition must always be last\n\n"
|
||||
"All variables will be printed in the Memory Interface log, if there's a hit or a NaN "
|
||||
"result. To check for issues, assign a variable to your equation, so it can be printed.\n\n"
|
||||
"Note: All values are internally converted to Doubles for calculations. It's possible for "
|
||||
"them to go out of range or to become NaN. A warning will be given if NaN is returned, and "
|
||||
"the var that became NaN will be logged.");
|
||||
ModalMessageBox::information(this, tr("Conditional help"), message);
|
||||
}
|
||||
|
@ -29,11 +29,14 @@ private:
|
||||
|
||||
void OnBPTypeChanged();
|
||||
void OnAddressTypeChanged();
|
||||
void ShowConditionHelp();
|
||||
|
||||
// Instruction BPs
|
||||
QRadioButton* m_instruction_bp;
|
||||
QGroupBox* m_instruction_box;
|
||||
QLineEdit* m_instruction_address;
|
||||
QLineEdit* m_instruction_condition;
|
||||
QPushButton* m_cond_help_btn;
|
||||
|
||||
// Memory BPs
|
||||
QRadioButton* m_memory_bp;
|
||||
|
Reference in New Issue
Block a user