mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 04:28:13 +00:00
HackStudio: Make debugger support shared libraries
This commit is contained in:
parent
dcdb68a013
commit
94db04fc12
7 changed files with 55 additions and 21 deletions
|
@ -57,7 +57,10 @@ Vector<BacktraceModel::FrameInfo> BacktraceModel::create_backtrace(const Debug::
|
||||||
u32 current_instruction = regs.eip;
|
u32 current_instruction = regs.eip;
|
||||||
Vector<BacktraceModel::FrameInfo> frames;
|
Vector<BacktraceModel::FrameInfo> frames;
|
||||||
do {
|
do {
|
||||||
String name = debug_session.debug_info().name_of_containing_function(current_instruction);
|
auto lib = debug_session.library_at(regs.eip);
|
||||||
|
if (!lib)
|
||||||
|
continue;
|
||||||
|
String name = lib->debug_info->name_of_containing_function(current_instruction - lib->base_address);
|
||||||
if (name.is_null()) {
|
if (name.is_null()) {
|
||||||
dbgln("BacktraceModel: couldn't find containing function for address: {:p}", current_instruction);
|
dbgln("BacktraceModel: couldn't find containing function for address: {:p}", current_instruction);
|
||||||
name = "<missing>";
|
name = "<missing>";
|
||||||
|
|
|
@ -38,11 +38,12 @@ Debugger& Debugger::the()
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::initialize(
|
void Debugger::initialize(
|
||||||
|
String source_root,
|
||||||
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
||||||
Function<void()> on_continue_callback,
|
Function<void()> on_continue_callback,
|
||||||
Function<void()> on_exit_callback)
|
Function<void()> on_exit_callback)
|
||||||
{
|
{
|
||||||
s_the = new Debugger(move(on_stop_callback), move(on_continue_callback), move(on_exit_callback));
|
s_the = new Debugger(source_root, move(on_stop_callback), move(on_continue_callback), move(on_exit_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Debugger::is_initialized()
|
bool Debugger::is_initialized()
|
||||||
|
@ -51,10 +52,12 @@ bool Debugger::is_initialized()
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugger::Debugger(
|
Debugger::Debugger(
|
||||||
|
String source_root,
|
||||||
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
||||||
Function<void()> on_continue_callback,
|
Function<void()> on_continue_callback,
|
||||||
Function<void()> on_exit_callback)
|
Function<void()> on_exit_callback)
|
||||||
: m_on_stopped_callback(move(on_stop_callback))
|
: m_source_root(source_root)
|
||||||
|
, m_on_stopped_callback(move(on_stop_callback))
|
||||||
, m_on_continue_callback(move(on_continue_callback))
|
, m_on_continue_callback(move(on_continue_callback))
|
||||||
, m_on_exit_callback(move(on_exit_callback))
|
, m_on_exit_callback(move(on_exit_callback))
|
||||||
{
|
{
|
||||||
|
@ -76,7 +79,7 @@ void Debugger::on_breakpoint_change(const String& file, size_t line, BreakpointC
|
||||||
if (!session)
|
if (!session)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto address = session->debug_info().get_instruction_from_source(position.file_path, position.line_number);
|
auto address = session->get_address_from_source_position(position.file_path, position.line_number);
|
||||||
if (!address.has_value()) {
|
if (!address.has_value()) {
|
||||||
dbgln("Warning: couldn't get instruction address from source");
|
dbgln("Warning: couldn't get instruction address from source");
|
||||||
// TODO: Currently, the GUI will indicate that a breakpoint was inserted/removed at this line,
|
// TODO: Currently, the GUI will indicate that a breakpoint was inserted/removed at this line,
|
||||||
|
@ -86,10 +89,10 @@ void Debugger::on_breakpoint_change(const String& file, size_t line, BreakpointC
|
||||||
}
|
}
|
||||||
|
|
||||||
if (change_type == BreakpointChange::Added) {
|
if (change_type == BreakpointChange::Added) {
|
||||||
bool success = session->insert_breakpoint(reinterpret_cast<void*>(address.value()));
|
bool success = session->insert_breakpoint(reinterpret_cast<void*>(address.value().address));
|
||||||
ASSERT(success);
|
ASSERT(success);
|
||||||
} else {
|
} else {
|
||||||
bool success = session->remove_breakpoint(reinterpret_cast<void*>(address.value()));
|
bool success = session->remove_breakpoint(reinterpret_cast<void*>(address.value().address));
|
||||||
ASSERT(success);
|
ASSERT(success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,14 +112,14 @@ int Debugger::start_static()
|
||||||
|
|
||||||
void Debugger::start()
|
void Debugger::start()
|
||||||
{
|
{
|
||||||
m_debug_session = Debug::DebugSession::exec_and_attach(m_executable_path);
|
m_debug_session = Debug::DebugSession::exec_and_attach(m_executable_path, m_source_root);
|
||||||
ASSERT(!!m_debug_session);
|
ASSERT(!!m_debug_session);
|
||||||
|
|
||||||
for (const auto& breakpoint : m_breakpoints) {
|
for (const auto& breakpoint : m_breakpoints) {
|
||||||
dbgln("insertig breakpoint at: {}:{}", breakpoint.file_path, breakpoint.line_number);
|
dbgln("inserting breakpoint at: {}:{}", breakpoint.file_path, breakpoint.line_number);
|
||||||
auto address = m_debug_session->debug_info().get_instruction_from_source(breakpoint.file_path, breakpoint.line_number);
|
auto address = m_debug_session->get_address_from_source_position(breakpoint.file_path, breakpoint.line_number);
|
||||||
if (address.has_value()) {
|
if (address.has_value()) {
|
||||||
bool success = m_debug_session->insert_breakpoint(reinterpret_cast<void*>(address.value()));
|
bool success = m_debug_session->insert_breakpoint(reinterpret_cast<void*>(address.value().address));
|
||||||
ASSERT(success);
|
ASSERT(success);
|
||||||
} else {
|
} else {
|
||||||
dbgln("couldn't insert breakpoint");
|
dbgln("couldn't insert breakpoint");
|
||||||
|
@ -130,7 +133,7 @@ int Debugger::debugger_loop()
|
||||||
{
|
{
|
||||||
ASSERT(m_debug_session);
|
ASSERT(m_debug_session);
|
||||||
|
|
||||||
m_debug_session->run([this](Debug::DebugSession::DebugBreakReason reason, Optional<PtraceRegisters> optional_regs) {
|
m_debug_session->run(Debug::DebugSession::DesiredInitialDebugeeState::Running, [this](Debug::DebugSession::DebugBreakReason reason, Optional<PtraceRegisters> optional_regs) {
|
||||||
if (reason == Debug::DebugSession::DebugBreakReason::Exited) {
|
if (reason == Debug::DebugSession::DebugBreakReason::Exited) {
|
||||||
dbgln("Program exited");
|
dbgln("Program exited");
|
||||||
m_on_exit_callback();
|
m_on_exit_callback();
|
||||||
|
@ -140,9 +143,16 @@ int Debugger::debugger_loop()
|
||||||
ASSERT(optional_regs.has_value());
|
ASSERT(optional_regs.has_value());
|
||||||
const PtraceRegisters& regs = optional_regs.value();
|
const PtraceRegisters& regs = optional_regs.value();
|
||||||
|
|
||||||
auto source_position = m_debug_session->debug_info().get_source_position(regs.eip);
|
auto source_position = m_debug_session->get_source_position(regs.eip);
|
||||||
|
if (!source_position.has_value())
|
||||||
|
return Debug::DebugSession::DebugDecision::SingleStep;
|
||||||
|
|
||||||
|
// We currently do no support stepping through assembly source
|
||||||
|
if (source_position.value().file_path.ends_with(".S"))
|
||||||
|
return Debug::DebugSession::DebugDecision::SingleStep;
|
||||||
|
|
||||||
|
ASSERT(source_position.has_value());
|
||||||
if (m_state.get() == Debugger::DebuggingState::SingleStepping) {
|
if (m_state.get() == Debugger::DebuggingState::SingleStepping) {
|
||||||
ASSERT(source_position.has_value());
|
|
||||||
if (m_state.should_stop_single_stepping(source_position.value())) {
|
if (m_state.should_stop_single_stepping(source_position.value())) {
|
||||||
m_state.set_normal();
|
m_state.set_normal();
|
||||||
} else {
|
} else {
|
||||||
|
@ -241,11 +251,18 @@ void Debugger::do_step_over(const PtraceRegisters& regs)
|
||||||
{
|
{
|
||||||
// To step over, we insert a temporary breakpoint at each line in the current function,
|
// To step over, we insert a temporary breakpoint at each line in the current function,
|
||||||
// as well as at the current function's return point, and continue execution.
|
// as well as at the current function's return point, and continue execution.
|
||||||
auto current_function = m_debug_session->debug_info().get_containing_function(regs.eip);
|
auto lib = m_debug_session->library_at(regs.eip);
|
||||||
|
if (!lib)
|
||||||
|
return;
|
||||||
|
auto current_function = lib->debug_info->get_containing_function(regs.eip - lib->base_address);
|
||||||
|
if (!current_function.has_value()) {
|
||||||
|
dbgln("cannot perform step_over, failed to find containing function of: {:p}", regs.eip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ASSERT(current_function.has_value());
|
ASSERT(current_function.has_value());
|
||||||
auto lines_in_current_function = m_debug_session->debug_info().source_lines_in_scope(current_function.value());
|
auto lines_in_current_function = lib->debug_info->source_lines_in_scope(current_function.value());
|
||||||
for (const auto& line : lines_in_current_function) {
|
for (const auto& line : lines_in_current_function) {
|
||||||
insert_temporary_breakpoint(line.address_of_first_statement);
|
insert_temporary_breakpoint(line.address_of_first_statement.value() + lib->base_address);
|
||||||
}
|
}
|
||||||
insert_temporary_breakpoint_at_return_address(regs);
|
insert_temporary_breakpoint_at_return_address(regs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
static void initialize(
|
static void initialize(
|
||||||
|
String source_root,
|
||||||
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
||||||
Function<void()> on_continue_callback,
|
Function<void()> on_continue_callback,
|
||||||
Function<void()> on_exit_callback);
|
Function<void()> on_exit_callback);
|
||||||
|
@ -102,6 +103,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Debugger(
|
explicit Debugger(
|
||||||
|
String source_root,
|
||||||
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
Function<HasControlPassedToUser(const PtraceRegisters&)> on_stop_callback,
|
||||||
Function<void()> on_continue_callback,
|
Function<void()> on_continue_callback,
|
||||||
Function<void()> on_exit_callback);
|
Function<void()> on_exit_callback);
|
||||||
|
@ -118,6 +120,7 @@ private:
|
||||||
void insert_temporary_breakpoint_at_return_address(const PtraceRegisters&);
|
void insert_temporary_breakpoint_at_return_address(const PtraceRegisters&);
|
||||||
|
|
||||||
OwnPtr<Debug::DebugSession> m_debug_session;
|
OwnPtr<Debug::DebugSession> m_debug_session;
|
||||||
|
String m_source_root;
|
||||||
DebuggingState m_state;
|
DebuggingState m_state;
|
||||||
|
|
||||||
pthread_mutex_t m_ui_action_mutex {};
|
pthread_mutex_t m_ui_action_mutex {};
|
||||||
|
|
|
@ -38,7 +38,10 @@ namespace HackStudio {
|
||||||
|
|
||||||
DisassemblyModel::DisassemblyModel(const Debug::DebugSession& debug_session, const PtraceRegisters& regs)
|
DisassemblyModel::DisassemblyModel(const Debug::DebugSession& debug_session, const PtraceRegisters& regs)
|
||||||
{
|
{
|
||||||
auto containing_function = debug_session.debug_info().get_containing_function(regs.eip);
|
auto lib = debug_session.library_at(regs.eip);
|
||||||
|
if (!lib)
|
||||||
|
return;
|
||||||
|
auto containing_function = lib->debug_info->get_containing_function(regs.eip - lib->base_address);
|
||||||
if (!containing_function.has_value()) {
|
if (!containing_function.has_value()) {
|
||||||
dbgln("Cannot disassemble as the containing function was not found.");
|
dbgln("Cannot disassemble as the containing function was not found.");
|
||||||
return;
|
return;
|
||||||
|
@ -54,7 +57,7 @@ DisassemblyModel::DisassemblyModel(const Debug::DebugSession& debug_session, con
|
||||||
kernel_elf = make<ELF::Image>((const u8*)kernel_file->data(), kernel_file->size());
|
kernel_elf = make<ELF::Image>((const u8*)kernel_file->data(), kernel_file->size());
|
||||||
elf = kernel_elf.ptr();
|
elf = kernel_elf.ptr();
|
||||||
} else {
|
} else {
|
||||||
elf = &debug_session.elf();
|
elf = &lib->debug_info->elf();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto symbol = elf->find_symbol(containing_function.value().address_low);
|
auto symbol = elf->find_symbol(containing_function.value().address_low);
|
||||||
|
|
|
@ -64,7 +64,10 @@ void DisassemblyWidget::update_state(const Debug::DebugSession& debug_session, c
|
||||||
m_disassembly_view->set_model(DisassemblyModel::create(debug_session, regs));
|
m_disassembly_view->set_model(DisassemblyModel::create(debug_session, regs));
|
||||||
|
|
||||||
if (m_disassembly_view->model()->row_count() > 0) {
|
if (m_disassembly_view->model()->row_count() > 0) {
|
||||||
auto containing_function = debug_session.debug_info().get_containing_function(regs.eip);
|
auto lib = debug_session.library_at(regs.eip);
|
||||||
|
if (!lib)
|
||||||
|
return;
|
||||||
|
auto containing_function = lib->debug_info->get_containing_function(regs.eip - lib->base_address);
|
||||||
if (containing_function.has_value())
|
if (containing_function.has_value())
|
||||||
m_function_name_label->set_text(containing_function.value().name);
|
m_function_name_label->set_text(containing_function.value().name);
|
||||||
else
|
else
|
||||||
|
|
|
@ -184,7 +184,10 @@ void VariablesModel::update()
|
||||||
|
|
||||||
RefPtr<VariablesModel> VariablesModel::create(const PtraceRegisters& regs)
|
RefPtr<VariablesModel> VariablesModel::create(const PtraceRegisters& regs)
|
||||||
{
|
{
|
||||||
auto variables = Debugger::the().session()->debug_info().get_variables_in_current_scope(regs);
|
auto lib = Debugger::the().session()->library_at(regs.eip);
|
||||||
|
if (!lib)
|
||||||
|
return nullptr;
|
||||||
|
auto variables = lib->debug_info->get_variables_in_current_scope(regs);
|
||||||
return adopt(*new VariablesModel(move(variables), regs));
|
return adopt(*new VariablesModel(move(variables), regs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -522,14 +522,16 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_debug_action()
|
||||||
void HackStudioWidget::initialize_debugger()
|
void HackStudioWidget::initialize_debugger()
|
||||||
{
|
{
|
||||||
Debugger::initialize(
|
Debugger::initialize(
|
||||||
|
m_project->root_path(),
|
||||||
[this](const PtraceRegisters& regs) {
|
[this](const PtraceRegisters& regs) {
|
||||||
ASSERT(Debugger::the().session());
|
ASSERT(Debugger::the().session());
|
||||||
const auto& debug_session = *Debugger::the().session();
|
const auto& debug_session = *Debugger::the().session();
|
||||||
auto source_position = debug_session.debug_info().get_source_position(regs.eip);
|
auto source_position = debug_session.get_source_position(regs.eip);
|
||||||
if (!source_position.has_value()) {
|
if (!source_position.has_value()) {
|
||||||
dbgln("Could not find source position for address: {:p}", regs.eip);
|
dbgln("Could not find source position for address: {:p}", regs.eip);
|
||||||
return Debugger::HasControlPassedToUser::No;
|
return Debugger::HasControlPassedToUser::No;
|
||||||
}
|
}
|
||||||
|
dbgln("Debugger stopped at source position: {}:{}", source_position.value().file_path, source_position.value().line_number);
|
||||||
|
|
||||||
Core::EventLoop::main().post_event(
|
Core::EventLoop::main().post_event(
|
||||||
*window(),
|
*window(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue