diff --git a/Userland/DevTools/HackStudio/CMakeLists.txt b/Userland/DevTools/HackStudio/CMakeLists.txt index 56b4fa7313..7308935417 100644 --- a/Userland/DevTools/HackStudio/CMakeLists.txt +++ b/Userland/DevTools/HackStudio/CMakeLists.txt @@ -50,5 +50,5 @@ set(SOURCES ) serenity_app(HackStudio ICON app-hack-studio) -target_link_libraries(HackStudio LibWeb LibMarkdown LibGUI LibCpp LibGfx LibCore LibVT LibDebug LibX86 LibDiff LibShell LibSymbolication LibRegex LibSQL) +target_link_libraries(HackStudio LibWeb LibMarkdown LibGUI LibCpp LibGfx LibCore LibVT LibDebug LibX86 LibDiff LibShell LibSymbolication LibRegex LibSQL LibCoredump) add_dependencies(HackStudio CppLanguageServer) diff --git a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp b/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp index 4c13ff71b6..df2946f6f8 100644 --- a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp +++ b/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.cpp @@ -76,10 +76,16 @@ DebugInfoWidget::DebugInfoWidget() // We currently only reconstruct eip & ebp. Ideally would also reconstruct the other registers somehow. // (Other registers may be needed to get the values of variables who are not stored on the stack) PtraceRegisters frame_regs {}; - frame_regs.set_ip(model.frames()[index.row()].instruction_address); - frame_regs.set_bp(model.frames()[index.row()].frame_base); + auto backtrace_frame = model.frames()[index.row()]; + frame_regs.set_ip(backtrace_frame.instruction_address); + frame_regs.set_bp(backtrace_frame.frame_base); - m_variables_view->set_model(VariablesModel::create(frame_regs)); + m_variables_view->set_model(VariablesModel::create(static_cast(*m_variables_view->model()).inspector(), frame_regs)); + if (on_backtrace_frame_selection && backtrace_frame.m_source_position.has_value()) { + on_backtrace_frame_selection(*backtrace_frame.m_source_position); + } else { + dbgln("no source position info"); + } }; } @@ -149,9 +155,9 @@ NonnullRefPtr DebugInfoWidget::build_registers_tab() return registers_widget; } -void DebugInfoWidget::update_state(const Debug::ProcessInspector& inspector, const PtraceRegisters& regs) +void DebugInfoWidget::update_state(Debug::ProcessInspector& inspector, const PtraceRegisters& regs) { - m_variables_view->set_model(VariablesModel::create(regs)); + m_variables_view->set_model(VariablesModel::create(inspector, regs)); m_backtrace_view->set_model(BacktraceModel::create(inspector, regs)); if (m_registers_view->model()) { auto& previous_registers = static_cast(m_registers_view->model())->raw_registers(); diff --git a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.h b/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.h index 0e3f06b628..0cf30d5648 100644 --- a/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.h +++ b/Userland/DevTools/HackStudio/Debugger/DebugInfoWidget.h @@ -26,10 +26,12 @@ class DebugInfoWidget final : public GUI::Widget { public: virtual ~DebugInfoWidget() override { } - void update_state(Debug::ProcessInspector const&, PtraceRegisters const&); + void update_state(Debug::ProcessInspector&, PtraceRegisters const&); void program_stopped(); void set_debug_actions_enabled(bool enabled); + Function on_backtrace_frame_selection; + private: explicit DebugInfoWidget(); void init_toolbar(); diff --git a/Userland/DevTools/HackStudio/HackStudioWidget.cpp b/Userland/DevTools/HackStudio/HackStudioWidget.cpp index 4e19862d03..d84d0db7c2 100644 --- a/Userland/DevTools/HackStudio/HackStudioWidget.cpp +++ b/Userland/DevTools/HackStudio/HackStudioWidget.cpp @@ -78,7 +78,7 @@ namespace HackStudio { -HackStudioWidget::HackStudioWidget(const String& path_to_project) +HackStudioWidget::HackStudioWidget(String path_to_project) : m_editor_font(read_editor_font_from_config()) { set_fill_with_background_color(true); @@ -207,7 +207,7 @@ void HackStudioWidget::open_project(const String& root_path) m_project_tree_view->set_model(m_project->model()); m_project_tree_view->update(); } - if (m_git_widget) { + if (m_git_widget && m_git_widget->initialized()) { m_git_widget->change_repo(LexicalPath(root_path)); m_git_widget->refresh(); } @@ -855,8 +855,9 @@ void HackStudioWidget::initialize_debugger() String HackStudioWidget::get_full_path_of_serenity_source(const String& file) { auto path_parts = LexicalPath(file).parts(); - VERIFY(path_parts[0] == ".."); - path_parts.remove(0); + while (!path_parts.is_empty() && 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"; @@ -864,12 +865,20 @@ String HackStudioWidget::get_full_path_of_serenity_source(const String& file) return String::formatted("{}/{}", serenity_sources_base, relative_path_builder.to_string()); } +String HackStudioWidget::get_absolute_path(const String& path) const +{ + // TODO: We can probably do a more specific condition here, something like + // "if (file.starts_with("../Libraries/") || file.starts_with("../AK/"))" + if (path.starts_with("..")) { + return get_full_path_of_serenity_source(path); + } + return m_project->to_absolute_path(path); +} + RefPtr HackStudioWidget::get_editor_of_file(const String& filename) { String file_path = filename; - // TODO: We can probably do a more specific condition here, something like - // "if (file.starts_with("../Libraries/") || file.starts_with("../AK/"))" if (filename.starts_with("../")) { file_path = get_full_path_of_serenity_source(filename); } @@ -1060,6 +1069,11 @@ void HackStudioWidget::create_action_tab(GUI::Widget& parent) m_todo_entries_widget = m_action_tab_widget->add_tab("TODO"); m_terminal_wrapper = m_action_tab_widget->add_tab("Build", false); m_debug_info_widget = m_action_tab_widget->add_tab("Debug"); + + m_debug_info_widget->on_backtrace_frame_selection = [this](Debug::DebugInfo::SourcePosition const& source_position) { + open_file(get_absolute_path(source_position.file_path), source_position.line_number - 1); + }; + m_disassembly_widget = m_action_tab_widget->add_tab("Disassembly"); m_git_widget = m_action_tab_widget->add_tab("Git", LexicalPath(m_project->root_path())); m_git_widget->set_view_diff_callback([this](const auto& original_content, const auto& diff) { @@ -1452,4 +1466,20 @@ void HackStudioWidget::change_editor_font(RefPtr font) Config::write_i32("HackStudio", "EditorFont", "Size", m_editor_font->presentation_size()); } +void HackStudioWidget::open_coredump(String const& coredump_path) +{ + open_project("/usr/src/serenity"); + m_mode = Mode::Coredump; + + m_coredump_inspector = Coredump::Inspector::create(coredump_path, [this](float progress) { + window()->set_progress(progress * 100); + }); + window()->set_progress(0); + + if (m_coredump_inspector) { + m_debug_info_widget->update_state(*m_coredump_inspector, m_coredump_inspector->get_registers()); + reveal_action_tab(*m_debug_info_widget); + } +} + } diff --git a/Userland/DevTools/HackStudio/HackStudioWidget.h b/Userland/DevTools/HackStudio/HackStudioWidget.h index 93b5db1960..1e6342d486 100644 --- a/Userland/DevTools/HackStudio/HackStudioWidget.h +++ b/Userland/DevTools/HackStudio/HackStudioWidget.h @@ -21,6 +21,7 @@ #include "ProjectFile.h" #include "TerminalWrapper.h" #include "ToDoEntriesWidget.h" +#include #include #include #include @@ -62,11 +63,19 @@ public: }; ContinueDecision warn_unsaved_changes(const String& prompt); + enum class Mode { + Code, + Coredump + }; + + void open_coredump(String const& coredump_path); + private: static String get_full_path_of_serenity_source(const String& file); + String get_absolute_path(String const&) const; Vector selected_file_paths() const; - HackStudioWidget(const String& path_to_project); + HackStudioWidget(String path_to_project); void open_project(const String& root_path); enum class EditMode { @@ -215,5 +224,8 @@ private: RefPtr m_no_wrapping_action; RefPtr m_wrap_anywhere_action; RefPtr m_wrap_at_words_action; + + Mode m_mode { Mode::Code }; + OwnPtr m_coredump_inspector; }; } diff --git a/Userland/DevTools/HackStudio/Project.cpp b/Userland/DevTools/HackStudio/Project.cpp index 2e6a4f3b77..5e5d2a2919 100644 --- a/Userland/DevTools/HackStudio/Project.cpp +++ b/Userland/DevTools/HackStudio/Project.cpp @@ -51,7 +51,7 @@ NonnullRefPtr Project::create_file(const String& path) const return ProjectFile::construct_with_name(full_path); } -String Project::to_absolute_path(const String& path) const +String Project::to_absolute_path(String const& path) const { if (LexicalPath { path }.is_absolute()) { return path; diff --git a/Userland/DevTools/HackStudio/Project.h b/Userland/DevTools/HackStudio/Project.h index 0e00ffdc13..0fc1022d41 100644 --- a/Userland/DevTools/HackStudio/Project.h +++ b/Userland/DevTools/HackStudio/Project.h @@ -29,12 +29,11 @@ public: NonnullRefPtr create_file(const String& path) const; void for_each_text_file(Function) const; + String to_absolute_path(String const&) const; private: explicit Project(const String& root_path); - String to_absolute_path(const String&) const; - RefPtr m_model; String m_root_path; diff --git a/Userland/DevTools/HackStudio/main.cpp b/Userland/DevTools/HackStudio/main.cpp index 33f306e0f4..c8b093106b 100644 --- a/Userland/DevTools/HackStudio/main.cpp +++ b/Userland/DevTools/HackStudio/main.cpp @@ -52,14 +52,16 @@ int main(int argc, char** argv) } const char* path_argument = nullptr; + bool mode_coredump = false; Core::ArgsParser args_parser; args_parser.add_positional_argument(path_argument, "Path to a workspace or a file", "path", Core::ArgsParser::Required::No); + args_parser.add_option(mode_coredump, "Inspect a coredump in HackStudio", "coredump", 'c'); args_parser.parse(argc, argv); auto argument_absolute_path = Core::File::real_path_for(path_argument); auto project_path = argument_absolute_path; - if (argument_absolute_path.is_null()) + if (argument_absolute_path.is_null() || mode_coredump) project_path = Core::File::real_path_for("."); s_hack_studio_widget = window->set_main_widget(project_path); @@ -76,9 +78,11 @@ int main(int argc, char** argv) }; window->show(); - s_hack_studio_widget->update_actions(); + if (mode_coredump) + s_hack_studio_widget->open_coredump(argument_absolute_path); + return app->exec(); }