From af338a34c04d4e7fb741698521e9beb2f53027e1 Mon Sep 17 00:00:00 2001 From: Itamar Date: Wed, 15 Apr 2020 21:03:22 +0300 Subject: [PATCH] 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. --- Libraries/LibDebug/DebugSession.cpp | 16 ++++++- Libraries/LibDebug/DebugSession.h | 65 +++++++++++++++++++---------- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/Libraries/LibDebug/DebugSession.cpp b/Libraries/LibDebug/DebugSession.cpp index 5519c73289..65261dfe19 100644 --- a/Libraries/LibDebug/DebugSession.cpp +++ b/Libraries/LibDebug/DebugSession.cpp @@ -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"); 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() { auto regs = get_registers(); diff --git a/Libraries/LibDebug/DebugSession.h b/Libraries/LibDebug/DebugSession.h index e3138c1dcb..e52679640e 100644 --- a/Libraries/LibDebug/DebugSession.h +++ b/Libraries/LibDebug/DebugSession.h @@ -71,7 +71,15 @@ public: PtraceRegisters get_registers() const; 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(); template @@ -83,12 +91,14 @@ public: enum DebugDecision { Continue, SingleStep, + ContinueBreakAtSyscall, Detach, Kill, }; enum DebugBreakReason { Breakpoint, + Syscall, Exited, }; @@ -112,36 +122,39 @@ void DebugSession::run(Callback callback) enum class State { FreeRun, + Syscall, ConsecutiveBreakpoint, SingleStep, }; 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()); + m_is_debugee_dead = true; + return true; + } + return false; + }; + for (;;) { - if (state == State::FreeRun) { - continue_debugee(); - - 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()); - m_is_debugee_dead = true; + if (state == State::FreeRun || state == State::Syscall) { + if (do_continue_and_wait()) break; - } } auto regs = get_registers(); Optional current_breakpoint; - if (state == State::FreeRun) { + if (state == State::FreeRun || state == State::Syscall) { current_breakpoint = m_breakpoints.get((void*)((u32)regs.eip - 1)); + if (current_breakpoint.has_value()) + state = State::FreeRun; } else { current_breakpoint = m_breakpoints.get((void*)regs.eip); } @@ -153,10 +166,19 @@ void DebugSession::run(Callback callback) 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) { state = State::FreeRun; + } else if (decision == DebugDecision::ContinueBreakAtSyscall) { + state = State::Syscall; } if (current_breakpoint.has_value()) { @@ -174,9 +196,10 @@ void DebugSession::run(Callback callback) if (decision == DebugDecision::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) {