Debugger: Hook up breakpoints and stepping again

This commit is contained in:
chaoticgd 2025-02-17 08:17:55 +00:00 committed by Ty
parent eb83cb70ea
commit e4d7d22e78
4 changed files with 165 additions and 30 deletions

View File

@ -7,8 +7,9 @@
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "DebugTools/MIPSAnalyst.h"
#include "DebugTools/MipsStackWalk.h"
#include "DebugTools/SymbolImporter.h"
#include "VMManager.h"
#include "QtHost.h"
#include "MainWindow.h"
#include "AnalysisOptionsDialog.h"
@ -111,14 +112,6 @@ void DebuggerWindow::clearToolBarState()
restoreState(m_default_toolbar_state);
}
// There is no straightforward way to set the tab text to bold in Qt
// Sorry colour blind people, but this is the best we can do for now
void DebuggerWindow::setTabActiveStyle(BreakPointCpu enabledCpu)
{
//m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r5900), (enabledCpu == BREAKPOINT_EE) ? Qt::red : this->palette().text().color());
//m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r3000), (enabledCpu == BREAKPOINT_IOP) ? Qt::red : this->palette().text().color());
}
void DebuggerWindow::onVMStateChanged()
{
if (!QtHost::IsVMPaused())
@ -128,7 +121,6 @@ void DebuggerWindow::onVMStateChanged()
m_ui.actionStepInto->setEnabled(false);
m_ui.actionStepOver->setEnabled(false);
m_ui.actionStepOut->setEnabled(false);
setTabActiveStyle(BREAKPOINT_IOP_AND_EE);
}
else
{
@ -142,18 +134,7 @@ void DebuggerWindow::onVMStateChanged()
if (CBreakPoints::GetBreakpointTriggered())
{
const BreakPointCpu triggeredCpu = CBreakPoints::GetBreakpointTriggeredCpu();
setTabActiveStyle(triggeredCpu);
switch (triggeredCpu)
{
case BREAKPOINT_EE:
//m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r5900);
break;
case BREAKPOINT_IOP:
//m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r3000);
break;
default:
break;
}
m_dock_manager->switchToLayoutWithCPU(triggeredCpu);
Host::RunOnCPUThread([] {
CBreakPoints::ClearTemporaryBreakPoints();
CBreakPoints::SetBreakpointTriggered(false, BREAKPOINT_IOP_AND_EE);
@ -174,20 +155,140 @@ void DebuggerWindow::onRunPause()
void DebuggerWindow::onStepInto()
{
//CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
//currentCpu->onStepInto();
DebugInterface* cpu = currentCPU();
if (!cpu)
return;
if (!cpu->isAlive() || !cpu->isCpuPaused())
return;
// Allow the cpu to skip this pc if it is a breakpoint
CBreakPoints::SetSkipFirst(cpu->getCpuType(), cpu->getPC());
const u32 pc = cpu->getPC();
const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu, pc);
u32 bpAddr = pc + 0x4; // Default to the next instruction
if (info.isBranch)
{
if (!info.isConditional)
{
bpAddr = info.branchTarget;
}
else
{
if (info.conditionMet)
{
bpAddr = info.branchTarget;
}
else
{
bpAddr = pc + (2 * 4); // Skip branch delay slot
}
}
}
if (info.isSyscall)
bpAddr = info.branchTarget; // Syscalls are always taken
Host::RunOnCPUThread([cpu, bpAddr] {
CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true);
cpu->resumeCpu();
});
repaint();
}
void DebuggerWindow::onStepOver()
{
//CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
//currentCpu->onStepOver();
DebugInterface* cpu = currentCPU();
if (!cpu)
return;
if (!cpu->isAlive() || !cpu->isCpuPaused())
return;
const u32 pc = cpu->getPC();
const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu, pc);
u32 bpAddr = pc + 0x4; // Default to the next instruction
if (info.isBranch)
{
if (!info.isConditional)
{
if (info.isLinkedBranch) // jal, jalr
{
// it's a function call with a delay slot - skip that too
bpAddr += 4;
}
else // j, ...
{
// in case of absolute branches, set the breakpoint at the branch target
bpAddr = info.branchTarget;
}
}
else // beq, ...
{
if (info.conditionMet)
{
bpAddr = info.branchTarget;
}
else
{
bpAddr = pc + (2 * 4); // Skip branch delay slot
}
}
}
Host::RunOnCPUThread([cpu, bpAddr] {
CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true);
cpu->resumeCpu();
});
this->repaint();
}
void DebuggerWindow::onStepOut()
{
//CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
//currentCpu->onStepOut();
DebugInterface* cpu = currentCPU();
if (!cpu)
return;
if (!cpu->isAlive() || !cpu->isCpuPaused())
return;
// Allow the cpu to skip this pc if it is a breakpoint
CBreakPoints::SetSkipFirst(cpu->getCpuType(), cpu->getPC());
std::vector<MipsStackWalk::StackFrame> stack_frames;
for (const auto& thread : cpu->GetThreadList())
{
if (thread->Status() == ThreadStatus::THS_RUN)
{
stack_frames = MipsStackWalk::Walk(
cpu,
cpu->getPC(),
cpu->getRegister(0, 31),
cpu->getRegister(0, 29),
thread->EntryPoint(),
thread->StackTop());
break;
}
}
if (stack_frames.size() < 2)
return;
u32 breakpoint_pc = stack_frames.at(1).pc;
Host::RunOnCPUThread([cpu, breakpoint_pc] {
CBreakPoints::AddBreakPoint(cpu->getCpuType(), breakpoint_pc, true);
cpu->resumeCpu();
});
this->repaint();
}
void DebuggerWindow::onAnalyse()
@ -210,6 +311,15 @@ void DebuggerWindow::closeEvent(QCloseEvent* event)
deleteLater();
}
DebugInterface* DebuggerWindow::currentCPU()
{
std::optional<BreakPointCpu> maybe_cpu = m_dock_manager->cpu();
if (!maybe_cpu.has_value())
return nullptr;
return &DebugInterface::get(*maybe_cpu);
}
void DebuggerWindow::setupDefaultToolBarState()
{
// Hiding all the toolbars lets us save the default state of the window with

View File

@ -38,6 +38,8 @@ protected:
void closeEvent(QCloseEvent* event);
private:
DebugInterface* currentCPU();
void setupDefaultToolBarState();
Ui::DebuggerWindow m_ui;
@ -49,8 +51,6 @@ private:
DockManager* m_dock_manager;
QByteArray m_default_toolbar_state;
void setTabActiveStyle(BreakPointCpu toggledCPU);
};
extern DebuggerWindow* g_debugger_window;

View File

@ -118,6 +118,20 @@ void DockManager::switchToLayout(DockLayout::Index layout_index)
}
}
bool DockManager::switchToLayoutWithCPU(BreakPointCpu cpu)
{
for (DockLayout::Index i = 0; i < m_layouts.size(); i++)
{
if (m_layouts[i].cpu() == cpu)
{
switchToLayout(i);
return true;
}
}
return false;
}
void DockManager::loadLayouts()
{
m_layouts.clear();
@ -601,6 +615,14 @@ void DockManager::updateToolBarLockState()
toolbar->setMovable(!m_layout_locked || toolbar->isFloating());
}
std::optional<BreakPointCpu> DockManager::cpu()
{
if (m_current_layout == DockLayout::INVALID_INDEX)
return std::nullopt;
return m_layouts.at(m_current_layout).cpu();
}
KDDockWidgets::Core::DockWidget* DockManager::dockWidgetFactory(const QString& name)
{
if (!g_debugger_window)

View File

@ -51,6 +51,7 @@ public:
bool deleteLayout(DockLayout::Index layout_index);
void switchToLayout(DockLayout::Index layout_index);
bool switchToLayoutWithCPU(BreakPointCpu cpu);
void loadLayouts();
bool saveLayouts();
@ -79,6 +80,8 @@ public:
void setLayoutLocked(bool locked);
void updateToolBarLockState();
std::optional<BreakPointCpu> cpu();
private:
static KDDockWidgets::Core::DockWidget* dockWidgetFactory(const QString& name);
static bool dragAboutToStart(KDDockWidgets::Core::Draggable* draggable);