mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:57:45 +00:00
Debugger: Add single step command
Also, this commit does some refactoring to the debugging loop logic.
This commit is contained in:
parent
c3faaeb9b9
commit
312559b131
2 changed files with 50 additions and 24 deletions
|
@ -81,6 +81,7 @@ public:
|
||||||
|
|
||||||
enum DebugDecision {
|
enum DebugDecision {
|
||||||
Continue,
|
Continue,
|
||||||
|
SingleStep,
|
||||||
Detach,
|
Detach,
|
||||||
Kill,
|
Kill,
|
||||||
};
|
};
|
||||||
|
@ -107,9 +108,17 @@ private:
|
||||||
template<typename Callback>
|
template<typename Callback>
|
||||||
void DebugSession::run(Callback callback)
|
void DebugSession::run(Callback callback)
|
||||||
{
|
{
|
||||||
bool in_consecutive_breakpoint = false;
|
|
||||||
|
enum class State {
|
||||||
|
FreeRun,
|
||||||
|
ConsecutiveBreakpoint,
|
||||||
|
SingleStep,
|
||||||
|
};
|
||||||
|
|
||||||
|
State state { State::FreeRun };
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!in_consecutive_breakpoint) {
|
if (state == State::FreeRun) {
|
||||||
continue_debugee();
|
continue_debugee();
|
||||||
|
|
||||||
int wstatus = 0;
|
int wstatus = 0;
|
||||||
|
@ -119,7 +128,7 @@ void DebugSession::run(Callback callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This check actually only checks whether the debugee
|
// FIXME: This check actually only checks whether the debugee
|
||||||
// Is stopped because it hit a breakpoint or not
|
// stopped because it hit a breakpoint/is in single stepping mode or not
|
||||||
if (WSTOPSIG(wstatus) != SIGTRAP) {
|
if (WSTOPSIG(wstatus) != SIGTRAP) {
|
||||||
callback(DebugBreakReason::Exited, Optional<PtraceRegisters>());
|
callback(DebugBreakReason::Exited, Optional<PtraceRegisters>());
|
||||||
m_is_debugee_dead = true;
|
m_is_debugee_dead = true;
|
||||||
|
@ -130,34 +139,47 @@ void DebugSession::run(Callback callback)
|
||||||
auto regs = get_registers();
|
auto regs = get_registers();
|
||||||
Optional<BreakPoint> current_breakpoint;
|
Optional<BreakPoint> current_breakpoint;
|
||||||
|
|
||||||
if (in_consecutive_breakpoint) {
|
if (state == State::FreeRun) {
|
||||||
current_breakpoint = m_breakpoints.get((void*)regs.eip);
|
|
||||||
} else {
|
|
||||||
current_breakpoint = m_breakpoints.get((void*)((u32)regs.eip - 1));
|
current_breakpoint = m_breakpoints.get((void*)((u32)regs.eip - 1));
|
||||||
|
} else {
|
||||||
|
current_breakpoint = m_breakpoints.get((void*)regs.eip);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(current_breakpoint.has_value());
|
if (current_breakpoint.has_value()) {
|
||||||
|
// We want to make the breakpoint transparrent to the user of the debugger
|
||||||
// We want to make the breakpoint transparrent to the user of the debugger
|
regs.eip = reinterpret_cast<u32>(current_breakpoint.value().address);
|
||||||
|
set_registers(regs);
|
||||||
regs.eip = reinterpret_cast<u32>(current_breakpoint.value().address);
|
disable_breakpoint(current_breakpoint.value());
|
||||||
set_registers(regs);
|
}
|
||||||
disable_breakpoint(current_breakpoint.value());
|
|
||||||
|
|
||||||
DebugDecision decision = callback(DebugBreakReason::Breakpoint, regs);
|
DebugDecision decision = callback(DebugBreakReason::Breakpoint, regs);
|
||||||
if (decision != DebugDecision::Continue) {
|
|
||||||
// FIXME: implement detach & kill
|
if (decision == DebugDecision::Continue) {
|
||||||
ASSERT_NOT_REACHED();
|
state = State::FreeRun;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable the breakpoint
|
if (current_breakpoint.has_value()) {
|
||||||
auto stopped_address = single_step();
|
// Re-enable the breakpoint
|
||||||
enable_breakpoint(current_breakpoint.value());
|
auto stopped_address = single_step();
|
||||||
|
enable_breakpoint(current_breakpoint.value());
|
||||||
|
// If there is another breakpoint after the current one,
|
||||||
|
// Then we are already on it (because of single_step)
|
||||||
|
auto breakpoint_at_next_instruction = m_breakpoints.get(stopped_address);
|
||||||
|
if (breakpoint_at_next_instruction.has_value()
|
||||||
|
&& breakpoint_at_next_instruction.value().state == BreakPointState::Enabled) {
|
||||||
|
state = State::ConsecutiveBreakpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If there is another breakpoint after the current one,
|
if (decision == DebugDecision::SingleStep) {
|
||||||
// Then we are already on it (because of single_step)
|
state = State::SingleStep;
|
||||||
auto breakpoint_at_next_instruction = m_breakpoints.get(stopped_address);
|
} else {
|
||||||
in_consecutive_breakpoint = breakpoint_at_next_instruction.has_value()
|
// TODO: implement DebugDecision:: Kill, Detach
|
||||||
&& breakpoint_at_next_instruction.value().state == BreakPointState::Enabled;
|
ASSERT(decision == DebugDecision::Continue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == State::SingleStep) {
|
||||||
|
single_step();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,7 @@ void print_help()
|
||||||
{
|
{
|
||||||
printf("Options:\n"
|
printf("Options:\n"
|
||||||
"cont - Continue execution\n"
|
"cont - Continue execution\n"
|
||||||
|
"s - step over the current instruction\n"
|
||||||
"regs - Print registers\n"
|
"regs - Print registers\n"
|
||||||
"dis [number of instructions] - Print disassembly\n"
|
"dis [number of instructions] - Print disassembly\n"
|
||||||
"bp <address/symbol> - Insert a breakpoint\n");
|
"bp <address/symbol> - Insert a breakpoint\n");
|
||||||
|
@ -222,6 +223,9 @@ int main(int argc, char** argv)
|
||||||
if (command == "cont") {
|
if (command == "cont") {
|
||||||
return DebugSession::DebugDecision::Continue;
|
return DebugSession::DebugDecision::Continue;
|
||||||
}
|
}
|
||||||
|
if (command == "s") {
|
||||||
|
return DebugSession::DebugDecision::SingleStep;
|
||||||
|
}
|
||||||
|
|
||||||
if (command == "regs") {
|
if (command == "regs") {
|
||||||
handle_print_registers(regs);
|
handle_print_registers(regs);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue