Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve GDExtension Tools Integration with Editor Debug Tooling #86721

Merged
merged 1 commit into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
#include "core/io/file_access_compressed.h"
#include "core/io/file_access_encrypted.h"
#include "core/io/marshalls.h"
Expand Down Expand Up @@ -1919,6 +1920,16 @@ void EngineDebugger::send_message(const String &p_msg, const Array &p_data) {
::EngineDebugger::get_singleton()->send_message(p_msg, p_data);
}

void EngineDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
ERR_FAIL_COND_MSG(!::EngineDebugger::is_active(), "Can't send debug. No active debugger");
::EngineDebugger::get_singleton()->debug(p_can_continue, p_is_error_breakpoint);
}

void EngineDebugger::script_debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_is_error_breakpoint) {
ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't send debug. No active debugger");
::EngineDebugger::get_script_debugger()->debug(p_lang, p_can_continue, p_is_error_breakpoint);
}

Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
Callable &capture = *(Callable *)p_user;
if (!capture.is_valid()) {
Expand All @@ -1935,6 +1946,56 @@ Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arra
return OK;
}

void EngineDebugger::line_poll() {
ERR_FAIL_COND_MSG(!::EngineDebugger::is_active(), "Can't poll. No active debugger");
::EngineDebugger::get_singleton()->line_poll();
}

void EngineDebugger::set_lines_left(int p_lines) {
ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't set lines left. No active debugger");
::EngineDebugger::get_script_debugger()->set_lines_left(p_lines);
}

int EngineDebugger::get_lines_left() const {
ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), 0, "Can't get lines left. No active debugger");
return ::EngineDebugger::get_script_debugger()->get_lines_left();
}

void EngineDebugger::set_depth(int p_depth) {
ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't set depth. No active debugger");
::EngineDebugger::get_script_debugger()->set_depth(p_depth);
}

int EngineDebugger::get_depth() const {
ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), 0, "Can't get depth. No active debugger");
return ::EngineDebugger::get_script_debugger()->get_depth();
}

bool EngineDebugger::is_breakpoint(int p_line, const StringName &p_source) const {
ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), false, "Can't check breakpoint. No active debugger");
return ::EngineDebugger::get_script_debugger()->is_breakpoint(p_line, p_source);
}

bool EngineDebugger::is_skipping_breakpoints() const {
ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), false, "Can't check skipping breakpoint. No active debugger");
return ::EngineDebugger::get_script_debugger()->is_skipping_breakpoints();
}

void EngineDebugger::insert_breakpoint(int p_line, const StringName &p_source) {
ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't insert breakpoint. No active debugger");
::EngineDebugger::get_script_debugger()->insert_breakpoint(p_line, p_source);
}

void EngineDebugger::remove_breakpoint(int p_line, const StringName &p_source) {
ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't remove breakpoint. No active debugger");
::EngineDebugger::get_script_debugger()->remove_breakpoint(p_line, p_source);
}

void EngineDebugger::clear_breakpoints() {
ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't clear breakpoints. No active debugger");
::EngineDebugger::get_script_debugger()->clear_breakpoints();
}

EngineDebugger::~EngineDebugger() {
for (const KeyValue<StringName, Callable> &E : captures) {
::EngineDebugger::unregister_message_capture(E.key);
Expand All @@ -1960,7 +2021,23 @@ void EngineDebugger::_bind_methods() {
ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &EngineDebugger::unregister_message_capture);
ClassDB::bind_method(D_METHOD("has_capture", "name"), &EngineDebugger::has_capture);

ClassDB::bind_method(D_METHOD("line_poll"), &EngineDebugger::line_poll);

ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EngineDebugger::send_message);
ClassDB::bind_method(D_METHOD("debug", "can_continue", "is_error_breakpoint"), &EngineDebugger::debug, DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("script_debug", "language", "can_continue", "is_error_breakpoint"), &EngineDebugger::script_debug, DEFVAL(true), DEFVAL(false));

ClassDB::bind_method(D_METHOD("set_lines_left", "lines"), &EngineDebugger::set_lines_left);
ClassDB::bind_method(D_METHOD("get_lines_left"), &EngineDebugger::get_lines_left);

ClassDB::bind_method(D_METHOD("set_depth", "depth"), &EngineDebugger::set_depth);
ClassDB::bind_method(D_METHOD("get_depth"), &EngineDebugger::get_depth);

ClassDB::bind_method(D_METHOD("is_breakpoint", "line", "source"), &EngineDebugger::is_breakpoint);
ClassDB::bind_method(D_METHOD("is_skipping_breakpoints"), &EngineDebugger::is_skipping_breakpoints);
ClassDB::bind_method(D_METHOD("insert_breakpoint", "line", "source"), &EngineDebugger::insert_breakpoint);
ClassDB::bind_method(D_METHOD("remove_breakpoint", "line", "source"), &EngineDebugger::remove_breakpoint);
ClassDB::bind_method(D_METHOD("clear_breakpoints"), &EngineDebugger::clear_breakpoints);
}

} // namespace core_bind
16 changes: 16 additions & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,9 +576,25 @@ class EngineDebugger : public Object {
bool has_capture(const StringName &p_name);

void send_message(const String &p_msg, const Array &p_data);
void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false);
void script_debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false);

static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);

void line_poll();

void set_lines_left(int p_lines);
int get_lines_left() const;

void set_depth(int p_depth);
int get_depth() const;

bool is_breakpoint(int p_line, const StringName &p_source) const;
bool is_skipping_breakpoints() const;
void insert_breakpoint(int p_line, const StringName &p_source);
void remove_breakpoint(int p_line, const StringName &p_source);
void clear_breakpoints();

EngineDebugger() { singleton = this; }
~EngineDebugger();
};
Expand Down
1 change: 1 addition & 0 deletions core/object/script_language_extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ void ScriptLanguageExtension::_bind_methods() {

GDVIRTUAL_BIND(_debug_get_stack_level_line, "level");
GDVIRTUAL_BIND(_debug_get_stack_level_function, "level");
GDVIRTUAL_BIND(_debug_get_stack_level_source, "level");
GDVIRTUAL_BIND(_debug_get_stack_level_locals, "level", "max_subitems", "max_depth");
GDVIRTUAL_BIND(_debug_get_stack_level_members, "level", "max_subitems", "max_depth");
GDVIRTUAL_BIND(_debug_get_stack_level_instance, "level");
Expand Down
23 changes: 23 additions & 0 deletions doc/classes/EditorDebuggerPlugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@
<tutorials>
</tutorials>
<methods>
<method name="_breakpoint_set_in_tree" qualifiers="virtual">
<return type="void" />
<param index="0" name="script" type="Script" />
<param index="1" name="line" type="int" />
<param index="2" name="enabled" type="bool" />
<description>
Override this method to be notified when a breakpoint is set in the editor.
</description>
</method>
<method name="_breakpoints_cleared_in_tree" qualifiers="virtual">
<return type="void" />
<description>
Override this method to be notified when all breakpoints are cleared in the editor.
</description>
</method>
<method name="_capture" qualifiers="virtual">
<return type="bool" />
<param index="0" name="message" type="String" />
Expand All @@ -56,6 +71,14 @@
Override this method to process incoming messages. The [param session_id] is the ID of the [EditorDebuggerSession] that received the message (which you can retrieve via [method get_session]).
</description>
</method>
<method name="_goto_script_line" qualifiers="virtual">
<return type="void" />
<param index="0" name="script" type="Script" />
<param index="1" name="line" type="int" />
<description>
Override this method to be notified when a breakpoint line has been clicked in the debugger breakpoint panel.
</description>
</method>
<method name="_has_capture" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="capture" type="String" />
Expand Down
9 changes: 9 additions & 0 deletions doc/classes/EditorDebuggerSession.xml
Naros marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@
Sends the given [param message] to the attached remote instance, optionally passing additionally [param data]. See [EngineDebugger] for how to retrieve those messages.
</description>
</method>
<method name="set_breakpoint">
<return type="void" />
<param index="0" name="path" type="String" />
<param index="1" name="line" type="int" />
<param index="2" name="enabled" type="bool" />
<description>
Enables or disables a specific breakpoint based on [param enabled], updating the Editor Breakpoint Panel accordingly.
</description>
</method>
<method name="toggle_profiler">
<return type="void" />
<param index="0" name="profiler" type="String" />
Expand Down
85 changes: 85 additions & 0 deletions doc/classes/EngineDebugger.xml
Naros marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@
<tutorials>
</tutorials>
<methods>
<method name="clear_breakpoints">
<return type="void" />
<description>
Clears all breakpoints.
</description>
</method>
<method name="debug">
<return type="void" />
<param index="0" name="can_continue" type="bool" default="true" />
<param index="1" name="is_error_breakpoint" type="bool" default="false" />
<description>
Starts a debug break in script execution, optionally specifying whether the program can continue based on [param can_continue] and whether the break was due to a breakpoint.
</description>
</method>
<method name="get_depth" qualifiers="const" experimental="">
<return type="int" />
<description>
Returns the current debug depth.
</description>
</method>
<method name="get_lines_left" qualifiers="const" experimental="">
<return type="int" />
<description>
Returns the number of lines that remain.
</description>
</method>
<method name="has_capture">
<return type="bool" />
<param index="0" name="name" type="StringName" />
Expand All @@ -23,19 +49,47 @@
Returns [code]true[/code] if a profiler with the given name is present otherwise [code]false[/code].
</description>
</method>
<method name="insert_breakpoint">
<return type="void" />
<param index="0" name="line" type="int" />
<param index="1" name="source" type="StringName" />
<description>
Inserts a new breakpoint with the given [param source] and [param line].
</description>
</method>
<method name="is_active">
<return type="bool" />
<description>
Returns [code]true[/code] if the debugger is active otherwise [code]false[/code].
</description>
</method>
<method name="is_breakpoint" qualifiers="const">
<return type="bool" />
<param index="0" name="line" type="int" />
<param index="1" name="source" type="StringName" />
<description>
Returns [code]true[/code] if the given [param source] and [param line] represent an existing breakpoint.
</description>
</method>
<method name="is_profiling">
<return type="bool" />
<param index="0" name="name" type="StringName" />
<description>
Returns [code]true[/code] if a profiler with the given name is present and active otherwise [code]false[/code].
</description>
</method>
<method name="is_skipping_breakpoints" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the debugger is skipping breakpoints otherwise [code]false[/code].
</description>
</method>
<method name="line_poll">
<return type="void" />
<description>
Forces a processing loop of debugger events. The purpose of this method is just processing events every now and then when the script might get too busy, so that bugs like infinite loops can be caught
</description>
</method>
<method name="profiler_add_frame_data">
<return type="void" />
<param index="0" name="name" type="StringName" />
Expand Down Expand Up @@ -70,6 +124,23 @@
Registers a profiler with the given [param name]. See [EngineProfiler] for more information.
</description>
</method>
<method name="remove_breakpoint">
<return type="void" />
<param index="0" name="line" type="int" />
<param index="1" name="source" type="StringName" />
<description>
Removes a breakpoint with the given [param source] and [param line].
</description>
</method>
<method name="script_debug">
<return type="void" />
<param index="0" name="language" type="ScriptLanguage" />
<param index="1" name="can_continue" type="bool" default="true" />
<param index="2" name="is_error_breakpoint" type="bool" default="false" />
<description>
Starts a debug break in script execution, optionally specifying whether the program can continue based on [param can_continue] and whether the break was due to a breakpoint.
</description>
</method>
<method name="send_message">
<return type="void" />
<param index="0" name="message" type="String" />
Expand All @@ -78,6 +149,20 @@
Sends a message with given [param message] and [param data] array.
</description>
</method>
<method name="set_depth" experimental="">
<return type="void" />
<param index="0" name="depth" type="int" />
<description>
Sets the current debugging depth.
</description>
</method>
<method name="set_lines_left" experimental="">
<return type="void" />
<param index="0" name="lines" type="int" />
<description>
Sets the current debugging lines that remain.
</description>
</method>
<method name="unregister_message_capture">
<return type="void" />
<param index="0" name="name" type="StringName" />
Expand Down
7 changes: 7 additions & 0 deletions doc/classes/ScriptLanguageExtension.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@
<description>
</description>
</method>
<method name="_debug_get_stack_level_source" qualifiers="virtual const">
<return type="String" />
<param index="0" name="level" type="int" />
<description>
Returns the source associated with a given debug stack position.
</description>
</method>
<method name="_debug_parse_stack_level_expression" qualifiers="virtual">
<return type="String" />
<param index="0" name="level" type="int" />
Expand Down
27 changes: 27 additions & 0 deletions editor/plugins/editor_debugger_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void EditorDebuggerSession::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_active"), &EditorDebuggerSession::is_active);
ClassDB::bind_method(D_METHOD("add_session_tab", "control"), &EditorDebuggerSession::add_session_tab);
ClassDB::bind_method(D_METHOD("remove_session_tab", "control"), &EditorDebuggerSession::remove_session_tab);
ClassDB::bind_method(D_METHOD("set_breakpoint", "path", "line", "enabled"), &EditorDebuggerSession::set_breakpoint);

ADD_SIGNAL(MethodInfo("started"));
ADD_SIGNAL(MethodInfo("stopped"));
Expand Down Expand Up @@ -100,6 +101,11 @@ bool EditorDebuggerSession::is_active() {
return debugger->is_session_active();
}

void EditorDebuggerSession::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger.");
debugger->set_breakpoint(p_path, p_line, p_enabled);
}

void EditorDebuggerSession::detach_debugger() {
if (!debugger) {
return;
Expand Down Expand Up @@ -184,10 +190,31 @@ bool EditorDebuggerPlugin::capture(const String &p_message, const Array &p_data,
return false;
}

void EditorDebuggerPlugin::goto_script_line(const Ref<Script> &p_script, int p_line) {
GDVIRTUAL_CALL(_goto_script_line, p_script, p_line);
}

void EditorDebuggerPlugin::breakpoints_cleared_in_tree() {
GDVIRTUAL_CALL(_breakpoints_cleared_in_tree);
}

void EditorDebuggerPlugin::breakpoint_set_in_tree(const Ref<Script> &p_script, int p_line, bool p_enabled) {
GDVIRTUAL_CALL(_breakpoint_set_in_tree, p_script, p_line, p_enabled);
}

void EditorDebuggerPlugin::_bind_methods() {
GDVIRTUAL_BIND(_setup_session, "session_id");
GDVIRTUAL_BIND(_has_capture, "capture");
GDVIRTUAL_BIND(_capture, "message", "data", "session_id");
GDVIRTUAL_BIND(_goto_script_line, "script", "line");
GDVIRTUAL_BIND(_breakpoints_cleared_in_tree);
GDVIRTUAL_BIND(_breakpoint_set_in_tree, "script", "line", "enabled");
ClassDB::bind_method(D_METHOD("get_session", "id"), &EditorDebuggerPlugin::get_session);
ClassDB::bind_method(D_METHOD("get_sessions"), &EditorDebuggerPlugin::get_sessions);
}

EditorDebuggerPlugin::EditorDebuggerPlugin() {
EditorDebuggerNode::get_singleton()->connect("goto_script_line", callable_mp(this, &EditorDebuggerPlugin::goto_script_line));
EditorDebuggerNode::get_singleton()->connect("breakpoints_cleared_in_tree", callable_mp(this, &EditorDebuggerPlugin::breakpoints_cleared_in_tree));
EditorDebuggerNode::get_singleton()->connect("breakpoint_set_in_tree", callable_mp(this, &EditorDebuggerPlugin::breakpoint_set_in_tree));
}
Loading
Loading