mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-06-13 00:58:29 +02:00
MemoryView auto updateDebugger. Implement base codetrace logic. Add register breakpoints. Add CodeViewWidget autostepping to track a value.Debugger
This commit is contained in:
@ -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();
|
||||
|
Reference in New Issue
Block a user