summaryrefslogtreecommitdiff
path: root/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp')
-rw-r--r--Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp101
1 files changed, 101 insertions, 0 deletions
diff --git a/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp b/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
index 76e43d50ad..886b04da64 100644
--- a/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
+++ b/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
@@ -12,6 +12,7 @@
#include <QInputDialog>
#include <QKeyEvent>
#include <QMenu>
+#include <QMessageBox>
#include <QMouseEvent>
#include <QPainter>
#include <QResizeEvent>
@@ -21,6 +22,7 @@
#include <QWheelEvent>
#include "Common/Assert.h"
+#include "Common/Debug/CodeTrace.h"
#include "Common/GekkoDisassembler.h"
#include "Common/StringUtil.h"
#include "Core/Core.h"
@@ -564,6 +566,26 @@ void CodeViewWidget::OnContextMenu()
auto* restore_action =
menu->addAction(tr("Restore instruction"), this, &CodeViewWidget::OnRestoreInstruction);
+ QString target;
+ if (addr == PC && running && Core::GetState() == Core::State::Paused)
+ {
+ const std::string line = PowerPC::debug_interface.Disassemble(PC);
+ const auto target_it = std::find(line.begin(), line.end(), '\t');
+ const auto target_end = std::find(target_it, line.end(), ',');
+
+ if (target_it != line.end() && target_end != line.end())
+ target = QString::fromStdString(std::string{target_it + 1, target_end});
+ }
+
+ auto* run_until_menu = menu->addMenu(tr("Run until (ignoring breakpoints)"));
+ run_until_menu->addAction(tr("%1's value is hit").arg(target), this,
+ [this] { AutoStep(CodeTrace::AutoStop::Always); });
+ run_until_menu->addAction(tr("%1's value is used").arg(target), this,
+ [this] { AutoStep(CodeTrace::AutoStop::Used); });
+ run_until_menu->addAction(tr("%1's value is changed").arg(target),
+ [this] { AutoStep(CodeTrace::AutoStop::Changed); });
+
+ run_until_menu->setEnabled(!target.isEmpty());
follow_branch_action->setEnabled(running && GetBranchFromAddress(addr));
for (auto* action : {copy_address_action, copy_line_action, copy_hex_action, function_action,
@@ -587,6 +609,85 @@ void CodeViewWidget::OnContextMenu()
Update();
}
+void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
+{
+ // Autosteps and follows value in the target (left-most) register. The Used and Changed options
+ // silently follows target through reshuffles in memory and registers and stops on use or update.
+
+ CodeTrace code_trace;
+ bool repeat = false;
+
+ QMessageBox msgbox(QMessageBox::NoIcon, tr("Run until"), {}, QMessageBox::Cancel);
+ QPushButton* run_button = msgbox.addButton(tr("Keep Running"), QMessageBox::AcceptRole);
+ // Not sure if we want default to be cancel. Spacebar can let you quickly continue autostepping if
+ // Yes.
+
+ do
+ {
+ // Run autostep then update codeview
+ const AutoStepResults results = code_trace.AutoStepping(repeat, option);
+ emit Host::GetInstance()->UpdateDisasmDialog();
+ repeat = true;
+
+ // Invalid instruction, 0 means no step executed.
+ if (results.count == 0)
+ return;
+
+ // Status report
+ if (results.reg_tracked.empty() && results.mem_tracked.empty())
+ {
+ QMessageBox::warning(
+ this, tr("Overwritten"),
+ tr("Target value was overwritten by current instruction.\nInstructions executed: %1")
+ .arg(QString::number(results.count)),
+ QMessageBox::Cancel);
+ return;
+ }
+ else if (results.timed_out)
+ {
+ // Can keep running and try again after a time out.
+ msgbox.setText(
+ tr("<font color='#ff0000'>AutoStepping timed out. Current instruction is irrelevant."));
+ }
+ else
+ {
+ msgbox.setText(tr("Value tracked to current instruction."));
+ }
+
+ // Mem_tracked needs to track each byte individually, so a tracked word-sized value would have
+ // four entries. The displayed memory list needs to be shortened so it's not a huge list of
+ // bytes. Assumes adjacent bytes represent a word or half-word and removes the redundant bytes.
+ std::set<u32> mem_out;
+ auto iter = results.mem_tracked.begin();
+
+ while (iter != results.mem_tracked.end())
+ {
+ const u32 address = *iter;
+ mem_out.insert(address);
+
+ for (u32 i = 1; i <= 3; i++)
+ {
+ if (results.mem_tracked.count(address + i))
+ iter++;
+ else
+ break;
+ }
+
+ iter++;
+ }
+
+ const QString msgtext =
+ tr("Instructions executed: %1\nValue contained in:\nRegisters: %2\nMemory: %3")
+ .arg(QString::number(results.count))
+ .arg(QString::fromStdString(fmt::format("{}", fmt::join(results.reg_tracked, ", "))))
+ .arg(QString::fromStdString(fmt::format("{:#x}", fmt::join(mem_out, ", "))));
+
+ msgbox.setInformativeText(msgtext);
+ msgbox.exec();
+
+ } while (msgbox.clickedButton() == (QAbstractButton*)run_button);
+}
+
void CodeViewWidget::OnCopyAddress()
{
const u32 addr = GetContextAddress();