1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 02:57:36 +00:00

LibDebug: Add ContinueBreakAtSyscall decision

When the user of the DebugSession uses this decision, the debugged
program will be continued until it is either stopped by a singal (e.g
as a reuslt of a breakpoint), or enters a syscall.
This commit is contained in:
Itamar 2020-04-15 21:03:22 +03:00 committed by Andreas Kling
parent f4418361c4
commit af338a34c0
2 changed files with 58 additions and 23 deletions

View file

@ -184,14 +184,26 @@ void DebugSession::set_registers(const PtraceRegisters& regs)
} }
} }
void DebugSession::continue_debugee() void DebugSession::continue_debugee(ContinueType type)
{ {
if (ptrace(PT_CONTINUE, m_debugee_pid, 0, 0) < 0) { int command = (type == ContinueType::FreeRun) ? PT_CONTINUE : PT_SYSCALL;
if (ptrace(command, m_debugee_pid, 0, 0) < 0) {
perror("continue"); perror("continue");
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
} }
int DebugSession::continue_debugee_and_wait(ContinueType type)
{
continue_debugee(type);
int wstatus = 0;
if (waitpid(m_debugee_pid, &wstatus, WSTOPPED | WEXITED) != m_debugee_pid) {
perror("waitpid");
ASSERT_NOT_REACHED();
}
return wstatus;
}
void* DebugSession::single_step() void* DebugSession::single_step()
{ {
auto regs = get_registers(); auto regs = get_registers();

View file

@ -71,7 +71,15 @@ public:
PtraceRegisters get_registers() const; PtraceRegisters get_registers() const;
void set_registers(const PtraceRegisters&); void set_registers(const PtraceRegisters&);
void continue_debugee(); enum class ContinueType {
FreeRun,
Syscall,
};
void continue_debugee(ContinueType type = ContinueType::FreeRun);
//returns the wstatus result of waitpid()
int continue_debugee_and_wait(ContinueType type = ContinueType::FreeRun);
void* single_step(); void* single_step();
template<typename Callback> template<typename Callback>
@ -83,12 +91,14 @@ public:
enum DebugDecision { enum DebugDecision {
Continue, Continue,
SingleStep, SingleStep,
ContinueBreakAtSyscall,
Detach, Detach,
Kill, Kill,
}; };
enum DebugBreakReason { enum DebugBreakReason {
Breakpoint, Breakpoint,
Syscall,
Exited, Exited,
}; };
@ -112,36 +122,39 @@ void DebugSession::run(Callback callback)
enum class State { enum class State {
FreeRun, FreeRun,
Syscall,
ConsecutiveBreakpoint, ConsecutiveBreakpoint,
SingleStep, SingleStep,
}; };
State state { State::FreeRun }; State state { State::FreeRun };
auto do_continue_and_wait = [&]() {
int wstatus = continue_debugee_and_wait((state == State::FreeRun) ? ContinueType::FreeRun : ContinueType::Syscall);
// FIXME: This check actually only checks whether the debugee
// stopped because it hit a breakpoint/syscall/is in single stepping mode or not
if (WSTOPSIG(wstatus) != SIGTRAP) {
callback(DebugBreakReason::Exited, Optional<PtraceRegisters>());
m_is_debugee_dead = true;
return true;
}
return false;
};
for (;;) { for (;;) {
if (state == State::FreeRun) { if (state == State::FreeRun || state == State::Syscall) {
continue_debugee(); if (do_continue_and_wait())
int wstatus = 0;
if (waitpid(m_debugee_pid, &wstatus, WSTOPPED | WEXITED) != m_debugee_pid) {
perror("waitpid");
ASSERT_NOT_REACHED();
}
// FIXME: This check actually only checks whether the debugee
// stopped because it hit a breakpoint/is in single stepping mode or not
if (WSTOPSIG(wstatus) != SIGTRAP) {
callback(DebugBreakReason::Exited, Optional<PtraceRegisters>());
m_is_debugee_dead = true;
break; break;
}
} }
auto regs = get_registers(); auto regs = get_registers();
Optional<BreakPoint> current_breakpoint; Optional<BreakPoint> current_breakpoint;
if (state == State::FreeRun) { if (state == State::FreeRun || state == State::Syscall) {
current_breakpoint = m_breakpoints.get((void*)((u32)regs.eip - 1)); current_breakpoint = m_breakpoints.get((void*)((u32)regs.eip - 1));
if (current_breakpoint.has_value())
state = State::FreeRun;
} else { } else {
current_breakpoint = m_breakpoints.get((void*)regs.eip); current_breakpoint = m_breakpoints.get((void*)regs.eip);
} }
@ -153,10 +166,19 @@ void DebugSession::run(Callback callback)
disable_breakpoint(current_breakpoint.value()); disable_breakpoint(current_breakpoint.value());
} }
DebugDecision decision = callback(DebugBreakReason::Breakpoint, regs); DebugBreakReason reason = (state == State::Syscall && !current_breakpoint.has_value()) ? DebugBreakReason::Syscall : DebugBreakReason::Breakpoint;
DebugDecision decision = callback(reason, regs);
if (reason == DebugBreakReason::Syscall) {
// skip the exit from the syscall
if (do_continue_and_wait())
break;
}
if (decision == DebugDecision::Continue) { if (decision == DebugDecision::Continue) {
state = State::FreeRun; state = State::FreeRun;
} else if (decision == DebugDecision::ContinueBreakAtSyscall) {
state = State::Syscall;
} }
if (current_breakpoint.has_value()) { if (current_breakpoint.has_value()) {
@ -174,9 +196,10 @@ void DebugSession::run(Callback callback)
if (decision == DebugDecision::SingleStep) { if (decision == DebugDecision::SingleStep) {
state = State::SingleStep; state = State::SingleStep;
} else { }
// TODO: implement DebugDecision:: Kill, Detach
ASSERT(decision == DebugDecision::Continue); if (decision == DebugDecision::Kill || decision == DebugDecision::Detach) {
ASSERT_NOT_REACHED(); // TODO: implement
} }
if (state == State::SingleStep) { if (state == State::SingleStep) {