mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 08:08:12 +00:00
HackStudio: Add cpp debugger
The HackStudio debugger integrates with LibDebug to provide source-level debugging. The user can set breakpoints at various positions in the source code, and then run the program in debug mode. When the program is stopped, the current execution position is displayed, and the user can insert/remove breakpoints, continue execution, or single step the program.
This commit is contained in:
parent
393560d8a2
commit
74f41d5f98
8 changed files with 368 additions and 2 deletions
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include "CursorTool.h"
|
||||
#include "Debugger.h"
|
||||
#include "Editor.h"
|
||||
#include "EditorWrapper.h"
|
||||
#include "FindInFilesWidget.h"
|
||||
|
@ -36,7 +37,10 @@
|
|||
#include "WidgetTool.h"
|
||||
#include "WidgetTreeModel.h"
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibCore/Event.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibDebug/DebugSession.h>
|
||||
#include <LibGUI/AboutDialog.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
|
@ -62,6 +66,8 @@
|
|||
#include <LibGUI/TreeView.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibThread/Lock.h>
|
||||
#include <LibThread/Thread.h>
|
||||
#include <LibVT/TerminalWidget.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
|
@ -84,7 +90,7 @@ static RefPtr<GUI::TabWidget> s_action_tab_widget;
|
|||
|
||||
void add_new_editor(GUI::Widget& parent)
|
||||
{
|
||||
auto wrapper = EditorWrapper::construct();
|
||||
auto wrapper = EditorWrapper::construct(Debugger::on_breakpoint_change);
|
||||
if (s_action_tab_widget) {
|
||||
parent.insert_child_before(wrapper, *s_action_tab_widget);
|
||||
} else {
|
||||
|
@ -120,6 +126,24 @@ Editor& current_editor()
|
|||
return current_editor_wrapper().editor();
|
||||
}
|
||||
|
||||
NonnullRefPtr<EditorWrapper> get_editor_of_file(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;
|
||||
}
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
String get_project_executable_path()
|
||||
{
|
||||
// e.g /my/project.files => /my/project
|
||||
// TODO: Perhaps a Makefile rule for getting the value of $(PROGRAM) would be better?
|
||||
return g_project->path().substring(0, g_project->path().index_of(".").value());
|
||||
}
|
||||
|
||||
static void build(TerminalWrapper&);
|
||||
static void run(TerminalWrapper&);
|
||||
void open_project(String);
|
||||
|
@ -531,13 +555,78 @@ int main(int argc, char** argv)
|
|||
run(terminal_wrapper);
|
||||
stop_action->set_enabled(true);
|
||||
});
|
||||
|
||||
RefPtr<LibThread::Thread> debugger_thread;
|
||||
auto debug_action = GUI::Action::create("Debug", Gfx::Bitmap::load_from_file("/res/icons/16x16/play-debug.png"), [&](auto&) {
|
||||
if (g_project->type() != ProjectType::Cpp) {
|
||||
GUI::MessageBox::show(String::format("Cannot debug current project type", get_project_executable_path().characters()), "Error", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, g_window);
|
||||
return;
|
||||
}
|
||||
if (!GUI::FilePicker::file_exists(get_project_executable_path())) {
|
||||
GUI::MessageBox::show(String::format("Could not find file: %s. (did you build the project?)", get_project_executable_path().characters()), "Error", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, g_window);
|
||||
return;
|
||||
}
|
||||
if (Debugger::the().session()) {
|
||||
GUI::MessageBox::show("Debugger is already running", "Error", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, g_window);
|
||||
return;
|
||||
}
|
||||
Debugger::the().set_executable_path(get_project_executable_path());
|
||||
debugger_thread = adopt(*new LibThread::Thread(Debugger::start_static));
|
||||
debugger_thread->start();
|
||||
});
|
||||
|
||||
auto continue_action = GUI::Action::create("Continue", Gfx::Bitmap::load_from_file("/res/icons/16x16/go-last.png"), [&](auto&) {
|
||||
pthread_mutex_lock(Debugger::the().continue_mutex());
|
||||
Debugger::the().set_continue_type(Debugger::ContinueType::Continue);
|
||||
pthread_cond_signal(Debugger::the().continue_cond());
|
||||
pthread_mutex_unlock(Debugger::the().continue_mutex());
|
||||
});
|
||||
|
||||
auto single_step_action = GUI::Action::create("Single Step", Gfx::Bitmap::load_from_file("/res/icons/16x16/single-step.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());
|
||||
});
|
||||
continue_action->set_enabled(false);
|
||||
single_step_action->set_enabled(false);
|
||||
|
||||
toolbar.add_action(run_action);
|
||||
toolbar.add_action(stop_action);
|
||||
toolbar.add_action(debug_action);
|
||||
toolbar.add_action(continue_action);
|
||||
toolbar.add_action(single_step_action);
|
||||
|
||||
RefPtr<EditorWrapper> current_editor_in_execution;
|
||||
Debugger::initialize(
|
||||
[&](DebugInfo::SourcePosition source_position) {
|
||||
dbg() << "Program stopped";
|
||||
current_editor_in_execution = get_editor_of_file(source_position.file_path);
|
||||
current_editor_in_execution->editor().set_execution_position(source_position.line_number - 1);
|
||||
continue_action->set_enabled(true);
|
||||
single_step_action->set_enabled(true);
|
||||
},
|
||||
[&]() {
|
||||
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();
|
||||
}
|
||||
},
|
||||
[&]() {
|
||||
dbg() << "Program exited";
|
||||
Core::EventLoop::main().post_event(*g_window, make<Core::DeferredInvocationEvent>([=](auto&) {
|
||||
GUI::MessageBox::show("Program Exited", "Debugger", GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::OK, g_window);
|
||||
}));
|
||||
Core::EventLoop::wake();
|
||||
});
|
||||
|
||||
auto& build_menu = menubar->add_menu("Build");
|
||||
build_menu.add_action(build_action);
|
||||
build_menu.add_action(run_action);
|
||||
build_menu.add_action(stop_action);
|
||||
build_menu.add_action(debug_action);
|
||||
|
||||
auto& view_menu = menubar->add_menu("View");
|
||||
view_menu.add_action(hide_action_tabs_action);
|
||||
|
@ -613,6 +702,9 @@ void open_project(String filename)
|
|||
g_project_tree_view->toggle_index(g_project_tree_view->model()->index(0, 0));
|
||||
g_project_tree_view->update();
|
||||
}
|
||||
if (Debugger::is_initialized()) {
|
||||
Debugger::the().reset_breakpoints();
|
||||
}
|
||||
}
|
||||
|
||||
void open_file(const String& filename)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue