diff options
author | Itamar <itamar8910@gmail.com> | 2020-08-21 12:27:15 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-08-22 09:48:59 +0200 |
commit | 99788e6b32863734413fe1f5d99f8907c7840f41 (patch) | |
tree | 21eaf32d4a71e3fe91a0259d15bc538d20ab12cc /DevTools | |
parent | cb432ffe8c28a5a27daf2cf24ea2733a636e0a24 (diff) | |
download | serenity-99788e6b32863734413fe1f5d99f8907c7840f41.zip |
HackStudio: Implement "Step Out" debugging action
The "Step Out" action continues execution until the current function
returns.
Also, LibDebug/StackFrameUtils was introduced to eliminate the
duplication of stack frame inspection logic between the "Step Out"
action and the BacktraceModel.
Diffstat (limited to 'DevTools')
-rw-r--r-- | DevTools/HackStudio/Debugger/BacktraceModel.cpp | 7 | ||||
-rw-r--r-- | DevTools/HackStudio/Debugger/DebugInfoWidget.cpp | 12 | ||||
-rw-r--r-- | DevTools/HackStudio/Debugger/Debugger.cpp | 61 | ||||
-rw-r--r-- | DevTools/HackStudio/Debugger/Debugger.h | 20 |
4 files changed, 86 insertions, 14 deletions
diff --git a/DevTools/HackStudio/Debugger/BacktraceModel.cpp b/DevTools/HackStudio/Debugger/BacktraceModel.cpp index 281b621ffb..500b1a0747 100644 --- a/DevTools/HackStudio/Debugger/BacktraceModel.cpp +++ b/DevTools/HackStudio/Debugger/BacktraceModel.cpp @@ -26,6 +26,7 @@ #include "BacktraceModel.h" #include "Debugger.h" +#include <LibDebug/StackFrameUtils.h> namespace HackStudio { @@ -63,8 +64,10 @@ Vector<BacktraceModel::FrameInfo> BacktraceModel::create_backtrace(const DebugSe } frames.append({ name, current_instruction, current_ebp }); - current_instruction = Debugger::the().session()->peek(reinterpret_cast<u32*>(current_ebp + 4)).value(); - current_ebp = Debugger::the().session()->peek(reinterpret_cast<u32*>(current_ebp)).value(); + auto frame_info = StackFrameUtils::get_info(*Debugger::the().session(), current_ebp); + ASSERT(frame_info.has_value()); + current_instruction = frame_info.value().return_address; + current_ebp = frame_info.value().next_ebp; } while (current_ebp && current_instruction); return frames; } diff --git a/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp b/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp index ee1a2cdfc9..c3d7ebda5c 100644 --- a/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp +++ b/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp @@ -50,17 +50,25 @@ void DebugInfoWidget::init_toolbar() pthread_mutex_unlock(Debugger::the().continue_mutex()); }); - m_singlestep_action = GUI::Action::create("Single Step", Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-single-step.png"), [&](auto&) { + m_singlestep_action = GUI::Action::create("Step Over", Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-step-over.png"), [&](auto&) { pthread_mutex_lock(Debugger::the().continue_mutex()); - Debugger::the().set_continue_type(Debugger::ContinueType::SourceSingleStep); + Debugger::the().set_continue_type(Debugger::ContinueType::SourceStepOver); pthread_cond_signal(Debugger::the().continue_cond()); pthread_mutex_unlock(Debugger::the().continue_mutex()); }); m_step_in_action = GUI::Action::create("Step In", Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-step-in.png"), [&](auto&) { + pthread_mutex_lock(Debugger::the().continue_mutex()); + Debugger::the().set_continue_type(Debugger::ContinueType::SourceSingleStep); + pthread_cond_signal(Debugger::the().continue_cond()); + pthread_mutex_unlock(Debugger::the().continue_mutex()); }); m_step_out_action = GUI::Action::create("Step Out", Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-step-out.png"), [&](auto&) { + pthread_mutex_lock(Debugger::the().continue_mutex()); + Debugger::the().set_continue_type(Debugger::ContinueType::SourceStepOut); + pthread_cond_signal(Debugger::the().continue_cond()); + pthread_mutex_unlock(Debugger::the().continue_mutex()); }); m_toolbar->add_action(*m_continue_action); diff --git a/DevTools/HackStudio/Debugger/Debugger.cpp b/DevTools/HackStudio/Debugger/Debugger.cpp index 6e65a794d7..0438f3cc41 100644 --- a/DevTools/HackStudio/Debugger/Debugger.cpp +++ b/DevTools/HackStudio/Debugger/Debugger.cpp @@ -25,6 +25,7 @@ */ #include "Debugger.h" +#include <LibDebug/StackFrameUtils.h> namespace HackStudio { @@ -127,7 +128,7 @@ void Debugger::start() int Debugger::debugger_loop() { - DebuggingState state; + ASSERT(m_debug_session); m_debug_session->run([&](DebugSession::DebugBreakReason reason, Optional<PtraceRegisters> optional_regs) { if (reason == DebugSession::DebugBreakReason::Exited) { @@ -135,14 +136,15 @@ int Debugger::debugger_loop() m_on_exit_callback(); return DebugSession::DebugDecision::Detach; } + remove_temporary_breakpoints(); ASSERT(optional_regs.has_value()); const PtraceRegisters& regs = optional_regs.value(); auto source_position = m_debug_session->debug_info().get_source_position(regs.eip); - if (state.get() == Debugger::DebuggingState::SingleStepping) { + if (m_state.get() == Debugger::DebuggingState::SingleStepping) { ASSERT(source_position.has_value()); - if (state.should_stop_single_stepping(source_position.value())) { - state.set_normal(); + if (m_state.should_stop_single_stepping(source_position.value())) { + m_state.set_normal(); } else { return DebugSession::DebugDecision::SingleStep; } @@ -160,13 +162,21 @@ int Debugger::debugger_loop() m_continue_type = ContinueType::Continue; } - if (m_continue_type == ContinueType::Continue) { + switch (m_continue_type) { + case ContinueType::Continue: + m_state.set_normal(); return DebugSession::DebugDecision::Continue; - } - - if (m_continue_type == ContinueType::SourceSingleStep) { - state.set_single_stepping(source_position.value()); + case ContinueType::SourceSingleStep: + m_state.set_single_stepping(source_position.value()); return DebugSession::DebugDecision::SingleStep; + case ContinueType::SourceStepOut: + m_state.set_stepping_out(); + do_step_out(regs); + return DebugSession::DebugDecision::Continue; + case ContinueType::SourceStepOver: + m_state.set_stepping_over(); + do_step_over(regs); + return DebugSession::DebugDecision::Continue; } ASSERT_NOT_REACHED(); }); @@ -192,4 +202,37 @@ bool Debugger::DebuggingState::should_stop_single_stepping(const DebugInfo::Sour return m_original_source_position.value() != current_source_position; } +void Debugger::remove_temporary_breakpoints() +{ + for (auto breakpoint_address : m_state.temporary_breakpoints()) { + bool rc = m_debug_session->remove_breakpoint((void*)breakpoint_address); + ASSERT(rc); + } + m_state.clear_temporary_breakpoints(); +} + +void Debugger::DebuggingState::clear_temporary_breakpoints() +{ + m_addresses_of_temporary_breakpoints.clear(); +} +void Debugger::DebuggingState::add_temporary_breakpoint(u32 address) +{ + m_addresses_of_temporary_breakpoints.append(address); +} + +void Debugger::do_step_out(const PtraceRegisters& regs) +{ + auto frame_info = StackFrameUtils::get_info(*m_debug_session, regs.ebp); + ASSERT(frame_info.has_value()); + u32 return_address = frame_info.value().return_address; + bool success = m_debug_session->insert_breakpoint(reinterpret_cast<void*>(return_address)); + ASSERT(success); + m_state.add_temporary_breakpoint(return_address); +} + +void Debugger::do_step_over(const PtraceRegisters&) +{ + // TODO: Implement +} + } diff --git a/DevTools/HackStudio/Debugger/Debugger.h b/DevTools/HackStudio/Debugger/Debugger.h index 1600e4ba31..c1fe6b32c9 100644 --- a/DevTools/HackStudio/Debugger/Debugger.h +++ b/DevTools/HackStudio/Debugger/Debugger.h @@ -66,6 +66,8 @@ public: enum class ContinueType { Continue, SourceSingleStep, + SourceStepOut, + SourceStepOver, }; void set_continue_type(ContinueType type) { m_continue_type = type; } @@ -75,17 +77,27 @@ private: class DebuggingState { public: enum State { - Normal, + Normal, // Continue normally until we hit a breakpoint / program terminates SingleStepping, + SteppingOut, + SteppingOver, }; State get() const { return m_state; } + void set_normal(); void set_single_stepping(DebugInfo::SourcePosition original_source_position); + void set_stepping_out() { m_state = State::SteppingOut; } + void set_stepping_over() { m_state = State::SteppingOver; } + bool should_stop_single_stepping(const DebugInfo::SourcePosition& current_source_position) const; + void clear_temporary_breakpoints(); + void add_temporary_breakpoint(u32 address); + const Vector<u32>& temporary_breakpoints() const { return m_addresses_of_temporary_breakpoints; } private: State m_state { Normal }; Optional<DebugInfo::SourcePosition> m_original_source_position; // The source position at which we started the current single step + Vector<u32> m_addresses_of_temporary_breakpoints; }; explicit Debugger( @@ -98,12 +110,18 @@ private: void start(); int debugger_loop(); + void remove_temporary_breakpoints(); + void do_step_out(const PtraceRegisters&); + void do_step_over(const PtraceRegisters&); + OwnPtr<DebugSession> m_debug_session; + DebuggingState m_state; pthread_mutex_t m_continue_mutex {}; pthread_cond_t m_continue_cond {}; Vector<DebugInfo::SourcePosition> m_breakpoints; + String m_executable_path; Function<HasControlPassedToUser(const PtraceRegisters&)> m_on_stopped_callback; |