From 7eac9fe10e6917aaa24b6ca8f332f275aab74206 Mon Sep 17 00:00:00 2001 From: Itamar Date: Sat, 15 Aug 2020 10:58:16 +0300 Subject: [PATCH] HackStudio: Support debugging library code We can now step into library code in the debugger. Since we now need the whole source code of our libraries (and not just the headers), we clone the whole serenity git repo into /usr/share/serenity. --- .../HackStudio/Debugger/BacktraceModel.cpp | 4 +- .../HackStudio/Debugger/DebugInfoWidget.cpp | 7 +- DevTools/HackStudio/main.cpp | 82 ++++++++++++------- Libraries/LibDebug/DebugInfo.cpp | 8 +- 4 files changed, 69 insertions(+), 32 deletions(-) diff --git a/DevTools/HackStudio/Debugger/BacktraceModel.cpp b/DevTools/HackStudio/Debugger/BacktraceModel.cpp index 8cbd82627c..947a8a1662 100644 --- a/DevTools/HackStudio/Debugger/BacktraceModel.cpp +++ b/DevTools/HackStudio/Debugger/BacktraceModel.cpp @@ -57,12 +57,12 @@ Vector BacktraceModel::create_backtrace(const DebugSe String name = debug_session.debug_info().name_of_containing_function(current_instruction); if (name.is_null()) { dbg() << "BacktraceModel: couldn't find containing function for address: " << (void*)current_instruction; - break; + name = ""; } frames.append({ name, current_instruction, current_ebp }); current_instruction = Debugger::the().session()->peek(reinterpret_cast(current_ebp + 4)).value(); current_ebp = Debugger::the().session()->peek(reinterpret_cast(current_ebp)).value(); - } while (current_ebp); + } while (current_ebp && current_instruction); return frames; } diff --git a/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp b/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp index 2b310fa7dd..4e0b7a8fd0 100644 --- a/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp +++ b/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp @@ -96,7 +96,12 @@ void DebugInfoWidget::update_state(const DebugSession& debug_session, const Ptra { m_variables_view->set_model(VariablesModel::create(regs)); m_backtrace_view->set_model(BacktraceModel::create(debug_session, regs)); - m_backtrace_view->selection().set(m_backtrace_view->model()->index(0)); + auto selected_index = m_backtrace_view->model()->index(0); + if (!selected_index.is_valid()) { + dbg() << "Warning: DebugInfoWidget: backtrace selected index is invalid"; + return; + } + m_backtrace_view->selection().set(selected_index); } void DebugInfoWidget::program_stopped() diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp index aa7d91a3ea..9dc9c79e6a 100644 --- a/DevTools/HackStudio/main.cpp +++ b/DevTools/HackStudio/main.cpp @@ -119,7 +119,13 @@ static void set_edit_mode(EditMode mode) } } -static EditorWrapper& current_editor_wrapper() +static void build(TerminalWrapper&); +static void run(TerminalWrapper&); +void open_project(String); +void open_file(const String&); +bool make_is_available(); + +EditorWrapper& current_editor_wrapper() { ASSERT(g_current_editor_wrapper); return *g_current_editor_wrapper; @@ -130,15 +136,30 @@ GUI::TextEditor& current_editor() return current_editor_wrapper().editor(); } -static NonnullRefPtr get_editor_of_file(const String& file) +static String get_full_path_of_serenity_source(const String& file) { - for (auto& wrapper : g_all_editor_wrappers) { - String wrapper_file = wrapper.filename_label().text(); - if (wrapper_file == file || String::format("./%s", wrapper_file.characters()) == file) { - return wrapper; - } + auto path_parts = LexicalPath(file).parts(); + ASSERT(path_parts[0] == ".."); + path_parts.remove(0); + StringBuilder relative_path_builder; + relative_path_builder.join("/", path_parts); + constexpr char SERENITY_LIBS_PREFIX[] = "/usr/src/serenity"; + LexicalPath serenity_sources_base(SERENITY_LIBS_PREFIX); + return String::format("%s/%s", serenity_sources_base.string().characters(), relative_path_builder.to_string().characters()); +} + +NonnullRefPtr get_editor_of_file(const String& file) +{ + String file_path = file; + + // TODO: We can probably do a more specific condition here, something like + // "if (file.starts_with("../Libraries/") || file.starts_with("../AK/"))" + if (file.starts_with("../")) { + file_path = get_full_path_of_serenity_source(file); } - ASSERT_NOT_REACHED(); + + open_file(file_path); + return current_editor_wrapper(); } static String get_project_executable_path() @@ -148,12 +169,6 @@ static String get_project_executable_path() return g_project->path().substring(0, g_project->path().index_of(".").value()); } -static void build(TerminalWrapper&); -static void run(TerminalWrapper&); -void open_project(String); -void open_file(const String&); -bool make_is_available(); - int main(int argc, char** argv) { if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec unix fattr thread", nullptr) < 0) { @@ -620,27 +635,38 @@ int main(int argc, char** argv) dbg() << "Could not find source position for address: " << (void*)regs.eip; return Debugger::HasControlPassedToUser::No; } - current_editor_in_execution = get_editor_of_file(source_position.value().file_path); - current_editor_in_execution->editor().set_execution_position(source_position.value().line_number - 1); - debug_info_widget.update_state(debug_session, regs); - continue_action->set_enabled(true); - single_step_action->set_enabled(true); - reveal_action_tab(debug_info_widget); + + Core::EventLoop::main().post_event( + *g_window, + make( + [&, source_position](auto&) { + current_editor_in_execution = get_editor_of_file(source_position.value().file_path); + current_editor_in_execution->editor().set_execution_position(source_position.value().line_number - 1); + debug_info_widget.update_state(*Debugger::the().session(), regs); + continue_action->set_enabled(true); + single_step_action->set_enabled(true); + reveal_action_tab(debug_info_widget); + })); + Core::EventLoop::wake(); + return Debugger::HasControlPassedToUser::Yes; }, [&]() { dbg() << "Program continued"; - continue_action->set_enabled(false); - single_step_action->set_enabled(false); - if (current_editor_in_execution) { - current_editor_in_execution->editor().clear_execution_position(); - } + Core::EventLoop::main().post_event(*g_window, make([&](auto&) { + continue_action->set_enabled(false); + single_step_action->set_enabled(false); + if (current_editor_in_execution) { + current_editor_in_execution->editor().clear_execution_position(); + } + })); + Core::EventLoop::wake(); }, [&]() { dbg() << "Program exited"; - debug_info_widget.program_stopped(); - hide_action_tabs(); - Core::EventLoop::main().post_event(*g_window, make([=](auto&) { + Core::EventLoop::main().post_event(*g_window, make([&](auto&) { + debug_info_widget.program_stopped(); + hide_action_tabs(); GUI::MessageBox::show(g_window, "Program Exited", "Debugger", GUI::MessageBox::Type::Information); })); Core::EventLoop::wake(); diff --git a/Libraries/LibDebug/DebugInfo.cpp b/Libraries/LibDebug/DebugInfo.cpp index 16ee6131e4..6d66ddae0f 100644 --- a/Libraries/LibDebug/DebugInfo.cpp +++ b/Libraries/LibDebug/DebugInfo.cpp @@ -146,8 +146,14 @@ Optional DebugInfo::get_source_position(u32 target_ad Optional DebugInfo::get_instruction_from_source(const String& file, size_t line) const { + String file_path = file; + constexpr char SERENITY_LIBS_PREFIX[] = "/usr/src/serenity"; + if (file.starts_with(SERENITY_LIBS_PREFIX)) { + file_path = file.substring(sizeof(SERENITY_LIBS_PREFIX), file.length() - sizeof(SERENITY_LIBS_PREFIX)); + file_path = String::format("../%s", file_path.characters()); + } for (const auto& line_entry : m_sorted_lines) { - if (line_entry.file == file && line_entry.line == line) + if (line_entry.file == file_path && line_entry.line == line) return Optional(line_entry.address); } return {};