mirror of
https://github.com/fergalmoran/ladybird.git
synced 2026-02-08 00:34:19 +00:00
Debugger: Breakpoints now persist after being tripped
Previously, a breakpoint was removed after it was tripped. After a breakpoint trips, we have to undo the 'int3' patch from the instruction in order to continue the exceution. To make a breakpoint persist, we switch to "single step" mode, which stops the execution after a single instruction, and then we insert the breakpoint at the previous instruction. There is also some code that deals with an edge case where there are breakpoints in two consecutive instructions.
This commit is contained in:
@@ -132,18 +132,38 @@ bool DebugSession::insert_breakpoint(void* address)
|
||||
if (!original_bytes.has_value())
|
||||
return false;
|
||||
|
||||
if (!poke(reinterpret_cast<u32*>(address), (original_bytes.value() & ~(uint32_t)0xff) | BREAKPOINT_INSTRUCTION))
|
||||
return false;
|
||||
BreakPoint breakpoint { address, original_bytes.value(), BreakPointState::Disabled };
|
||||
|
||||
m_breakpoints.set(address, breakpoint);
|
||||
|
||||
enable_breakpoint(breakpoint);
|
||||
|
||||
m_breakpoints.set(address, { address, original_bytes.value() });
|
||||
return true;
|
||||
}
|
||||
|
||||
void DebugSession::remove_breakpoint(const BreakPoint& breakpoint)
|
||||
bool DebugSession::disable_breakpoint(const BreakPoint& breakpoint)
|
||||
{
|
||||
ASSERT(m_breakpoints.contains(breakpoint.address));
|
||||
poke(reinterpret_cast<u32*>(reinterpret_cast<char*>(breakpoint.address)), breakpoint.original_first_word);
|
||||
m_breakpoints.remove(breakpoint.address);
|
||||
if (!poke(reinterpret_cast<u32*>(reinterpret_cast<char*>(breakpoint.address)), breakpoint.original_first_word))
|
||||
return false;
|
||||
|
||||
auto bp = m_breakpoints.get(breakpoint.address).value();
|
||||
bp.state = BreakPointState::Disabled;
|
||||
m_breakpoints.set(bp.address, bp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebugSession::enable_breakpoint(const BreakPoint& breakpoint)
|
||||
{
|
||||
ASSERT(m_breakpoints.contains(breakpoint.address));
|
||||
|
||||
if (!poke(reinterpret_cast<u32*>(breakpoint.address), (breakpoint.original_first_word & ~(uint32_t)0xff) | BREAKPOINT_INSTRUCTION))
|
||||
return false;
|
||||
|
||||
auto bp = m_breakpoints.get(breakpoint.address).value();
|
||||
bp.state = BreakPointState::Enabled;
|
||||
m_breakpoints.set(bp.address, bp);
|
||||
return true;
|
||||
}
|
||||
|
||||
PtraceRegisters DebugSession::get_registers() const
|
||||
@@ -164,11 +184,6 @@ void DebugSession::set_registers(const PtraceRegisters& regs)
|
||||
}
|
||||
}
|
||||
|
||||
Optional<DebugSession::BreakPoint> DebugSession::get_matching_breakpoint(const PtraceRegisters& regs) const
|
||||
{
|
||||
return m_breakpoints.get(reinterpret_cast<void*>(regs.eip - 1));
|
||||
}
|
||||
|
||||
void DebugSession::continue_debugee()
|
||||
{
|
||||
if (ptrace(PT_CONTINUE, m_debugee_pid, 0, 0) < 0) {
|
||||
@@ -176,3 +191,23 @@ void DebugSession::continue_debugee()
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void* DebugSession::single_step()
|
||||
{
|
||||
auto regs = get_registers();
|
||||
constexpr u32 TRAP_FLAG = 0x100;
|
||||
regs.eflags |= TRAP_FLAG;
|
||||
set_registers(regs);
|
||||
|
||||
continue_debugee();
|
||||
|
||||
if (waitpid(m_debugee_pid, 0, WSTOPPED) != m_debugee_pid) {
|
||||
perror("waitpid");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
regs = get_registers();
|
||||
regs.eflags &= ~(TRAP_FLAG);
|
||||
set_registers(regs);
|
||||
return (void*)regs.eip;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user