Skip to content

Commit

Permalink
Debugger: Implement function/instruction nop restore
Browse files Browse the repository at this point in the history
  • Loading branch information
F0bes committed Oct 10, 2023
1 parent 7fa8d05 commit bf6f6bc
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 49 deletions.
159 changes: 113 additions & 46 deletions pcsx2-qt/Debugger/DisassemblyWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,56 +37,11 @@ DisassemblyWidget::DisassemblyWidget(QWidget* parent)
{
ui.setupUi(this);

CreateCustomContextMenu();
connect(this, &DisassemblyWidget::customContextMenuRequested, this, &DisassemblyWidget::customMenuRequested);
}

DisassemblyWidget::~DisassemblyWidget() = default;

void DisassemblyWidget::CreateCustomContextMenu()
{
if (m_contextMenu)
return; // ???
m_contextMenu = new QMenu(this);

QAction* action = 0;
m_contextMenu->addAction(action = new QAction(tr("Copy Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyAddress);
m_contextMenu->addAction(action = new QAction(tr("Copy Instruction Hex"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionHex);
m_contextMenu->addAction(action = new QAction(tr("Copy Instruction Text"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
// TODO: Disassemble to file. Do people use that?
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Assemble new Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAssembleInstruction);
m_contextMenu->addAction(action = new QAction(tr("NOP Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextNoopInstruction);
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Run to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRunToCursor);
m_contextMenu->addAction(action = new QAction(tr("Jump to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextJumpToCursor);
m_contextMenu->addAction(action = new QAction(tr("Toggle Breakpoint"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextToggleBreakpoint);
m_contextMenu->addAction(action = new QAction(tr("Follow Branch"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextFollowBranch);
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Go to Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextGoToAddress);
m_contextMenu->addAction(action = new QAction(tr("Go to in Memory View"), this));
connect(action, &QAction::triggered, this, [this]() { gotoInMemory(m_selectedAddressStart); });
m_contextMenu->addSeparator();
m_contextMenu->addAction(action = new QAction(tr("Add Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAddFunction);
m_contextMenu->addAction(action = new QAction(tr("Rename Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRenameFunction);
m_contextMenu->addAction(action = new QAction(tr("Remove Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRemoveFunction);
m_contextMenu->addAction(action = new QAction(tr("Stub (NOP) Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextStubFunction);
}

void DisassemblyWidget::contextCopyAddress()
{
QGuiApplication::clipboard()->setText(FetchSelectionInfo(SelectionInfo::ADDRESS));
Expand Down Expand Up @@ -133,6 +88,7 @@ void DisassemblyWidget::contextAssembleInstruction()
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu, val = encodedInstruction] {
for (u32 i = start; i <= end; i += 4)
{
this->m_nopedInstructions.insert({i, cpu->read32(i)});
cpu->write32(i, val);
}
QtHost::RunOnUIThread([this] { VMUpdate(); });
Expand All @@ -145,12 +101,28 @@ void DisassemblyWidget::contextNoopInstruction()
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] {
for (u32 i = start; i <= end; i += 4)
{
this->m_nopedInstructions.insert({i, cpu->read32(i)});
cpu->write32(i, 0x00);
}
QtHost::RunOnUIThread([this] { VMUpdate(); });
});
}

void DisassemblyWidget::contextRestoreInstruction()
{
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] {
for (u32 i = start; i <= end; i += 4)
{
if (this->m_nopedInstructions.find(i) != this->m_nopedInstructions.end())
{
cpu->write32(i, this->m_nopedInstructions[i]);
this->m_nopedInstructions.erase(i);
}
}
QtHost::RunOnUIThread([this] { VMUpdate(); });
});
}

void DisassemblyWidget::contextRunToCursor()
{
Host::RunOnCPUThread([&] {
Expand Down Expand Up @@ -317,6 +289,7 @@ void DisassemblyWidget::contextStubFunction()
if (curFuncAddress != SymbolMap::INVALID_ADDRESS)
{
Host::RunOnCPUThread([this, curFuncAddress, cpu = m_cpu] {
this->m_stubbedFunctions.insert({curFuncAddress, {cpu->read32(curFuncAddress), cpu->read32(curFuncAddress + 4)}});
cpu->write32(curFuncAddress, 0x03E00008); // jr $ra
cpu->write32(curFuncAddress + 4, 0x00000000); // nop
QtHost::RunOnUIThread([this] { VMUpdate(); });
Expand All @@ -328,6 +301,23 @@ void DisassemblyWidget::contextStubFunction()
}
}

void DisassemblyWidget::contextRestoreFunction()
{
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
if (curFuncAddress != SymbolMap::INVALID_ADDRESS && m_stubbedFunctions.find(curFuncAddress) != m_stubbedFunctions.end())
{
Host::RunOnCPUThread([this, curFuncAddress, cpu = m_cpu] {
cpu->write32(curFuncAddress, std::get<0>(this->m_stubbedFunctions[curFuncAddress]));
cpu->write32(curFuncAddress + 4, std::get<1>(this->m_stubbedFunctions[curFuncAddress]));
this->m_stubbedFunctions.erase(curFuncAddress);
QtHost::RunOnUIThread([this] { VMUpdate(); });
});
}
else
{
QMessageBox::warning(this, tr("Restore Function Error"), tr("No function / symbol is currently selected."));
}
}
void DisassemblyWidget::SetCpu(DebugInterface* cpu)
{
m_cpu = cpu;
Expand Down Expand Up @@ -639,7 +629,58 @@ void DisassemblyWidget::customMenuRequested(QPoint pos)
if (!m_cpu->isAlive())
return;

m_contextMenu->popup(this->mapToGlobal(pos));
QMenu* contextMenu = new QMenu(this);

QAction* action = 0;
contextMenu->addAction(action = new QAction(tr("Copy Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyAddress);
contextMenu->addAction(action = new QAction(tr("Copy Instruction Hex"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionHex);
contextMenu->addAction(action = new QAction(tr("Copy Instruction Text"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
contextMenu->addSeparator();
if (AddressCanRestore(m_selectedAddressStart, m_selectedAddressEnd))
{
contextMenu->addAction(action = new QAction(tr("Restore Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRestoreInstruction);
}
contextMenu->addAction(action = new QAction(tr("Assemble new Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAssembleInstruction);
contextMenu->addAction(action = new QAction(tr("NOP Instruction(s)"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextNoopInstruction);
contextMenu->addSeparator();
contextMenu->addAction(action = new QAction(tr("Run to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRunToCursor);
contextMenu->addAction(action = new QAction(tr("Jump to Cursor"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextJumpToCursor);
contextMenu->addAction(action = new QAction(tr("Toggle Breakpoint"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextToggleBreakpoint);
contextMenu->addAction(action = new QAction(tr("Follow Branch"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextFollowBranch);
contextMenu->addSeparator();
contextMenu->addAction(action = new QAction(tr("Go to Address"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextGoToAddress);
contextMenu->addAction(action = new QAction(tr("Go to in Memory View"), this));
connect(action, &QAction::triggered, this, [this]() { gotoInMemory(m_selectedAddressStart); });
contextMenu->addSeparator();
contextMenu->addAction(action = new QAction(tr("Add Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextAddFunction);
contextMenu->addAction(action = new QAction(tr("Rename Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRenameFunction);
contextMenu->addAction(action = new QAction(tr("Remove Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRemoveFunction);
if(FunctionCanRestore(m_selectedAddressStart))
{
contextMenu->addAction(action = new QAction(tr("Restore Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextRestoreFunction);
}
else
{
contextMenu->addAction(action = new QAction(tr("Stub (NOP) Function"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextStubFunction);
}
contextMenu->setAttribute(Qt::WA_DeleteOnClose);
contextMenu->popup(this->mapToGlobal(pos));
}

inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFont font, u32 pc)
Expand Down Expand Up @@ -756,3 +797,29 @@ void DisassemblyWidget::gotoAddress(u32 address)
this->repaint();
this->setFocus();
}

bool DisassemblyWidget::AddressCanRestore(u32 start, u32 end)
{
for (u32 i = start; i <= end; i += 4)
{
if (this->m_nopedInstructions.find(i) != this->m_nopedInstructions.end())
{
return true;
}
}
return false;
}

bool DisassemblyWidget::FunctionCanRestore(u32 address)
{
u32 funcStartAddress = m_cpu->GetSymbolMap().GetFunctionStart(address);

if (funcStartAddress != SymbolMap::INVALID_ADDRESS)
{
if(m_stubbedFunctions.find(funcStartAddress) != this->m_stubbedFunctions.end())
{
return true;
}
}
return false;
}
11 changes: 8 additions & 3 deletions pcsx2-qt/Debugger/DisassemblyWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public slots:
void contextCopyInstructionText();
void contextAssembleInstruction();
void contextNoopInstruction();
void contextRestoreInstruction();
void contextRunToCursor();
void contextJumpToCursor();
void contextToggleBreakpoint();
Expand All @@ -65,6 +66,7 @@ public slots:
void contextRenameFunction();
void contextRemoveFunction();
void contextStubFunction();
void contextRestoreFunction();

void gotoAddress(u32 address);

Expand All @@ -76,16 +78,16 @@ public slots:
private:
Ui::DisassemblyWidget ui;

QMenu* m_contextMenu = nullptr;
void CreateCustomContextMenu();

DebugInterface* m_cpu;
u32 m_visibleStart = 0x00336318; // The address of the first opcode shown(row 0)
u32 m_visibleRows;
u32 m_selectedAddressStart = 0;
u32 m_selectedAddressEnd = 0;
u32 m_rowHeight = 0;

std::map<u32, u32> m_nopedInstructions;
std::map<u32, std::tuple<u32, u32>> m_stubbedFunctions;

DisassemblyManager m_disassemblyManager;

inline QString DisassemblyStringFromAddress(u32 address, QFont font, u32 pc);
Expand All @@ -97,4 +99,7 @@ public slots:
INSTRUCTIONTEXT,
};
QString FetchSelectionInfo(SelectionInfo selInfo);

bool AddressCanRestore(u32 start, u32 end);
bool FunctionCanRestore(u32 address);
};

0 comments on commit bf6f6bc

Please sign in to comment.