Merge pull request #86721 from Naros/gde-debugger-tooling

Improve GDExtension Tools Integration with Editor Debug Tooling
This commit is contained in:
Rémi Verschelde 2024-06-11 10:48:34 +02:00
commit 430812980c
No known key found for this signature in database
GPG key ID: C3336907360768E1
9 changed files with 256 additions and 1 deletions

View file

@ -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"
@ -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()) {
@ -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);
@ -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

View file

@ -576,9 +576,25 @@ public:
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();
};

View file

@ -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");

View file

@ -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" />
@ -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" />

View file

@ -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" />

View file

@ -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" />
@ -23,12 +49,28 @@
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" />
@ -36,6 +78,18 @@
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" />
@ -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" />
@ -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" />

View file

@ -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" />

View file

@ -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"));
@ -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;
@ -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));
}

View file

@ -62,6 +62,8 @@ public:
bool is_debuggable();
bool is_active();
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
EditorDebuggerSession(ScriptEditorDebugger *p_debugger);
~EditorDebuggerSession();
};
@ -90,7 +92,15 @@ public:
GDVIRTUAL1RC(bool, _has_capture, const String &);
GDVIRTUAL1(_setup_session, int);
EditorDebuggerPlugin() {}
virtual void goto_script_line(const Ref<Script> &p_script, int p_line);
virtual void breakpoints_cleared_in_tree();
virtual void breakpoint_set_in_tree(const Ref<Script> &p_script, int p_line, bool p_enabled);
GDVIRTUAL2(_goto_script_line, const Ref<Script> &, int);
GDVIRTUAL0(_breakpoints_cleared_in_tree);
GDVIRTUAL3(_breakpoint_set_in_tree, const Ref<Script> &, int, bool);
EditorDebuggerPlugin();
~EditorDebuggerPlugin();
};