1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 13:27:35 +00:00

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.
This commit is contained in:
Itamar 2020-08-21 12:27:15 +03:00 committed by Andreas Kling
parent cb432ffe8c
commit 99788e6b32
8 changed files with 171 additions and 16 deletions

View file

@ -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;
}

View file

@ -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::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_in_action = GUI::Action::create("Step In", Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-step-in.png"), [&](auto&) {
});
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);

View file

@ -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
}
}

View file

@ -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;