mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 19:37:36 +00:00
HackStudio: Add option to inspect Coredump
This adds a --coredump <file> option to Hack Studio. When used, Hack Studio will open the coredump and allow the user to inspect it in the Debug tab.
This commit is contained in:
parent
ce726fe027
commit
8316eb7306
8 changed files with 72 additions and 19 deletions
|
@ -50,5 +50,5 @@ set(SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_app(HackStudio ICON app-hack-studio)
|
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)
|
add_dependencies(HackStudio CppLanguageServer)
|
||||||
|
|
|
@ -76,10 +76,16 @@ DebugInfoWidget::DebugInfoWidget()
|
||||||
// We currently only reconstruct eip & ebp. Ideally would also reconstruct the other registers somehow.
|
// 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)
|
// (Other registers may be needed to get the values of variables who are not stored on the stack)
|
||||||
PtraceRegisters frame_regs {};
|
PtraceRegisters frame_regs {};
|
||||||
frame_regs.set_ip(model.frames()[index.row()].instruction_address);
|
auto backtrace_frame = model.frames()[index.row()];
|
||||||
frame_regs.set_bp(model.frames()[index.row()].frame_base);
|
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<VariablesModel&>(*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<GUI::Widget> DebugInfoWidget::build_registers_tab()
|
||||||
return registers_widget;
|
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));
|
m_backtrace_view->set_model(BacktraceModel::create(inspector, regs));
|
||||||
if (m_registers_view->model()) {
|
if (m_registers_view->model()) {
|
||||||
auto& previous_registers = static_cast<RegistersModel*>(m_registers_view->model())->raw_registers();
|
auto& previous_registers = static_cast<RegistersModel*>(m_registers_view->model())->raw_registers();
|
||||||
|
|
|
@ -26,10 +26,12 @@ class DebugInfoWidget final : public GUI::Widget {
|
||||||
public:
|
public:
|
||||||
virtual ~DebugInfoWidget() override { }
|
virtual ~DebugInfoWidget() override { }
|
||||||
|
|
||||||
void update_state(Debug::ProcessInspector const&, PtraceRegisters const&);
|
void update_state(Debug::ProcessInspector&, PtraceRegisters const&);
|
||||||
void program_stopped();
|
void program_stopped();
|
||||||
void set_debug_actions_enabled(bool enabled);
|
void set_debug_actions_enabled(bool enabled);
|
||||||
|
|
||||||
|
Function<void(Debug::DebugInfo::SourcePosition const&)> on_backtrace_frame_selection;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit DebugInfoWidget();
|
explicit DebugInfoWidget();
|
||||||
void init_toolbar();
|
void init_toolbar();
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
|
|
||||||
namespace HackStudio {
|
namespace HackStudio {
|
||||||
|
|
||||||
HackStudioWidget::HackStudioWidget(const String& path_to_project)
|
HackStudioWidget::HackStudioWidget(String path_to_project)
|
||||||
: m_editor_font(read_editor_font_from_config())
|
: m_editor_font(read_editor_font_from_config())
|
||||||
{
|
{
|
||||||
set_fill_with_background_color(true);
|
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->set_model(m_project->model());
|
||||||
m_project_tree_view->update();
|
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->change_repo(LexicalPath(root_path));
|
||||||
m_git_widget->refresh();
|
m_git_widget->refresh();
|
||||||
}
|
}
|
||||||
|
@ -855,8 +855,9 @@ void HackStudioWidget::initialize_debugger()
|
||||||
String HackStudioWidget::get_full_path_of_serenity_source(const String& file)
|
String HackStudioWidget::get_full_path_of_serenity_source(const String& file)
|
||||||
{
|
{
|
||||||
auto path_parts = LexicalPath(file).parts();
|
auto path_parts = LexicalPath(file).parts();
|
||||||
VERIFY(path_parts[0] == "..");
|
while (!path_parts.is_empty() && path_parts[0] == "..") {
|
||||||
path_parts.remove(0);
|
path_parts.remove(0);
|
||||||
|
}
|
||||||
StringBuilder relative_path_builder;
|
StringBuilder relative_path_builder;
|
||||||
relative_path_builder.join("/", path_parts);
|
relative_path_builder.join("/", path_parts);
|
||||||
constexpr char SERENITY_LIBS_PREFIX[] = "/usr/src/serenity";
|
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());
|
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<EditorWrapper> HackStudioWidget::get_editor_of_file(const String& filename)
|
RefPtr<EditorWrapper> HackStudioWidget::get_editor_of_file(const String& filename)
|
||||||
{
|
{
|
||||||
String file_path = 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("../")) {
|
if (filename.starts_with("../")) {
|
||||||
file_path = get_full_path_of_serenity_source(filename);
|
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<ToDoEntriesWidget>("TODO");
|
m_todo_entries_widget = m_action_tab_widget->add_tab<ToDoEntriesWidget>("TODO");
|
||||||
m_terminal_wrapper = m_action_tab_widget->add_tab<TerminalWrapper>("Build", false);
|
m_terminal_wrapper = m_action_tab_widget->add_tab<TerminalWrapper>("Build", false);
|
||||||
m_debug_info_widget = m_action_tab_widget->add_tab<DebugInfoWidget>("Debug");
|
m_debug_info_widget = m_action_tab_widget->add_tab<DebugInfoWidget>("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<DisassemblyWidget>("Disassembly");
|
m_disassembly_widget = m_action_tab_widget->add_tab<DisassemblyWidget>("Disassembly");
|
||||||
m_git_widget = m_action_tab_widget->add_tab<GitWidget>("Git", LexicalPath(m_project->root_path()));
|
m_git_widget = m_action_tab_widget->add_tab<GitWidget>("Git", LexicalPath(m_project->root_path()));
|
||||||
m_git_widget->set_view_diff_callback([this](const auto& original_content, const auto& diff) {
|
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<Gfx::Font> font)
|
||||||
Config::write_i32("HackStudio", "EditorFont", "Size", m_editor_font->presentation_size());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "ProjectFile.h"
|
#include "ProjectFile.h"
|
||||||
#include "TerminalWrapper.h"
|
#include "TerminalWrapper.h"
|
||||||
#include "ToDoEntriesWidget.h"
|
#include "ToDoEntriesWidget.h"
|
||||||
|
#include <LibCoredump/Inspector.h>
|
||||||
#include <LibGUI/ActionGroup.h>
|
#include <LibGUI/ActionGroup.h>
|
||||||
#include <LibGUI/Scrollbar.h>
|
#include <LibGUI/Scrollbar.h>
|
||||||
#include <LibGUI/Splitter.h>
|
#include <LibGUI/Splitter.h>
|
||||||
|
@ -62,11 +63,19 @@ public:
|
||||||
};
|
};
|
||||||
ContinueDecision warn_unsaved_changes(const String& prompt);
|
ContinueDecision warn_unsaved_changes(const String& prompt);
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
Code,
|
||||||
|
Coredump
|
||||||
|
};
|
||||||
|
|
||||||
|
void open_coredump(String const& coredump_path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static String get_full_path_of_serenity_source(const String& file);
|
static String get_full_path_of_serenity_source(const String& file);
|
||||||
|
String get_absolute_path(String const&) const;
|
||||||
Vector<String> selected_file_paths() const;
|
Vector<String> selected_file_paths() const;
|
||||||
|
|
||||||
HackStudioWidget(const String& path_to_project);
|
HackStudioWidget(String path_to_project);
|
||||||
void open_project(const String& root_path);
|
void open_project(const String& root_path);
|
||||||
|
|
||||||
enum class EditMode {
|
enum class EditMode {
|
||||||
|
@ -215,5 +224,8 @@ private:
|
||||||
RefPtr<GUI::Action> m_no_wrapping_action;
|
RefPtr<GUI::Action> m_no_wrapping_action;
|
||||||
RefPtr<GUI::Action> m_wrap_anywhere_action;
|
RefPtr<GUI::Action> m_wrap_anywhere_action;
|
||||||
RefPtr<GUI::Action> m_wrap_at_words_action;
|
RefPtr<GUI::Action> m_wrap_at_words_action;
|
||||||
|
|
||||||
|
Mode m_mode { Mode::Code };
|
||||||
|
OwnPtr<Coredump::Inspector> m_coredump_inspector;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ NonnullRefPtr<ProjectFile> Project::create_file(const String& path) const
|
||||||
return ProjectFile::construct_with_name(full_path);
|
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()) {
|
if (LexicalPath { path }.is_absolute()) {
|
||||||
return path;
|
return path;
|
||||||
|
|
|
@ -29,12 +29,11 @@ public:
|
||||||
NonnullRefPtr<ProjectFile> create_file(const String& path) const;
|
NonnullRefPtr<ProjectFile> create_file(const String& path) const;
|
||||||
|
|
||||||
void for_each_text_file(Function<void(const ProjectFile&)>) const;
|
void for_each_text_file(Function<void(const ProjectFile&)>) const;
|
||||||
|
String to_absolute_path(String const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Project(const String& root_path);
|
explicit Project(const String& root_path);
|
||||||
|
|
||||||
String to_absolute_path(const String&) const;
|
|
||||||
|
|
||||||
RefPtr<GUI::FileSystemModel> m_model;
|
RefPtr<GUI::FileSystemModel> m_model;
|
||||||
|
|
||||||
String m_root_path;
|
String m_root_path;
|
||||||
|
|
|
@ -52,14 +52,16 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* path_argument = nullptr;
|
const char* path_argument = nullptr;
|
||||||
|
bool mode_coredump = false;
|
||||||
Core::ArgsParser args_parser;
|
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_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);
|
args_parser.parse(argc, argv);
|
||||||
|
|
||||||
auto argument_absolute_path = Core::File::real_path_for(path_argument);
|
auto argument_absolute_path = Core::File::real_path_for(path_argument);
|
||||||
|
|
||||||
auto project_path = argument_absolute_path;
|
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(".");
|
project_path = Core::File::real_path_for(".");
|
||||||
|
|
||||||
s_hack_studio_widget = window->set_main_widget<HackStudioWidget>(project_path);
|
s_hack_studio_widget = window->set_main_widget<HackStudioWidget>(project_path);
|
||||||
|
@ -76,9 +78,11 @@ int main(int argc, char** argv)
|
||||||
};
|
};
|
||||||
|
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
s_hack_studio_widget->update_actions();
|
s_hack_studio_widget->update_actions();
|
||||||
|
|
||||||
|
if (mode_coredump)
|
||||||
|
s_hack_studio_widget->open_coredump(argument_absolute_path);
|
||||||
|
|
||||||
return app->exec();
|
return app->exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue