summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorRĂ©mi Verschelde <rverschelde@gmail.com>2020-03-09 19:08:07 +0100
committerGitHub <noreply@github.com>2020-03-09 19:08:07 +0100
commit478337c412f767b908820d7bb56c786bf0b909cb (patch)
tree019ee1871f24bed0b9c56391830a48583dd15a54 /core
parent640169da5b20538c2d5c1106957461a3c988e2bc (diff)
parent3b47eb51e4025fae6dd63eb405dee54c49281a5f (diff)
Merge pull request #36751 from Faless/debugger/threads_and_profilers
ScriptDebugger refactor, threading, profilers.
Diffstat (limited to 'core')
-rw-r--r--core/SCsub1
-rw-r--r--core/debugger/SCsub5
-rw-r--r--core/debugger/debugger_marshalls.cpp329
-rw-r--r--core/debugger/debugger_marshalls.h175
-rw-r--r--core/debugger/engine_debugger.cpp186
-rw-r--r--core/debugger/engine_debugger.h130
-rw-r--r--core/debugger/local_debugger.cpp (renamed from core/script_debugger_local.cpp)292
-rw-r--r--core/debugger/local_debugger.h (renamed from core/script_debugger_local.h)39
-rw-r--r--core/debugger/remote_debugger.cpp935
-rw-r--r--core/debugger/remote_debugger.h114
-rw-r--r--core/debugger/remote_debugger_peer.cpp242
-rw-r--r--core/debugger/remote_debugger_peer.h94
-rw-r--r--core/debugger/script_debugger.cpp123
-rw-r--r--core/debugger/script_debugger.h80
-rw-r--r--core/io/multiplayer_api.cpp151
-rw-r--r--core/io/multiplayer_api.h34
-rw-r--r--core/io/stream_peer_tcp.cpp5
-rw-r--r--core/io/stream_peer_tcp.h3
-rw-r--r--core/script_debugger_remote.cpp1143
-rw-r--r--core/script_debugger_remote.h316
-rw-r--r--core/script_language.cpp89
-rw-r--r--core/script_language.h63
-rw-r--r--core/variant.cpp3
-rw-r--r--core/variant_call.cpp4
-rw-r--r--core/variant_op.cpp20
25 files changed, 2632 insertions, 1944 deletions
diff --git a/core/SCsub b/core/SCsub
index 755c5c65c6..b4974196bd 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -165,6 +165,7 @@ SConscript('os/SCsub')
SConscript('math/SCsub')
SConscript('crypto/SCsub')
SConscript('io/SCsub')
+SConscript('debugger/SCsub')
SConscript('bind/SCsub')
diff --git a/core/debugger/SCsub b/core/debugger/SCsub
new file mode 100644
index 0000000000..1c5f954470
--- /dev/null
+++ b/core/debugger/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.core_sources, "*.cpp")
diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp
new file mode 100644
index 0000000000..4bccf0805f
--- /dev/null
+++ b/core/debugger/debugger_marshalls.cpp
@@ -0,0 +1,329 @@
+/*************************************************************************/
+/* debugger_marshalls.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "debugger_marshalls.h"
+
+#include "core/io/marshalls.h"
+
+#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+
+Array DebuggerMarshalls::ResourceUsage::serialize() {
+ infos.sort();
+
+ Array arr;
+ arr.push_back(infos.size() * 4);
+ for (List<ResourceInfo>::Element *E = infos.front(); E; E = E->next()) {
+ arr.push_back(E->get().path);
+ arr.push_back(E->get().format);
+ arr.push_back(E->get().type);
+ arr.push_back(E->get().vram);
+ }
+ return arr;
+}
+
+bool DebuggerMarshalls::ResourceUsage::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "ResourceUsage");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "ResourceUsage");
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 4; i++) {
+ ResourceInfo info;
+ info.path = p_arr[idx];
+ info.format = p_arr[idx + 1];
+ info.type = p_arr[idx + 2];
+ info.vram = p_arr[idx + 3];
+ infos.push_back(info);
+ }
+ CHECK_END(p_arr, idx, "ResourceUsage");
+ return true;
+}
+
+Array DebuggerMarshalls::ScriptFunctionSignature::serialize() {
+ Array arr;
+ arr.push_back(name);
+ arr.push_back(id);
+ return arr;
+}
+
+bool DebuggerMarshalls::ScriptFunctionSignature::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature");
+ name = p_arr[0];
+ id = p_arr[1];
+ CHECK_END(p_arr, 2, "ScriptFunctionSignature");
+ return true;
+}
+
+Array DebuggerMarshalls::NetworkProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(infos.size() * 6);
+ for (int i = 0; i < infos.size(); ++i) {
+ arr.push_back(uint64_t(infos[i].node));
+ arr.push_back(infos[i].node_path);
+ arr.push_back(infos[i].incoming_rpc);
+ arr.push_back(infos[i].incoming_rset);
+ arr.push_back(infos[i].outgoing_rpc);
+ arr.push_back(infos[i].outgoing_rset);
+ }
+ return arr;
+}
+
+bool DebuggerMarshalls::NetworkProfilerFrame::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "NetworkProfilerFrame");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "NetworkProfilerFrame");
+ infos.resize(size);
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 6; ++i) {
+ infos.write[i].node = uint64_t(p_arr[idx]);
+ infos.write[i].node_path = p_arr[idx + 1];
+ infos.write[i].incoming_rpc = p_arr[idx + 2];
+ infos.write[i].incoming_rset = p_arr[idx + 3];
+ infos.write[i].outgoing_rpc = p_arr[idx + 4];
+ infos.write[i].outgoing_rset = p_arr[idx + 5];
+ }
+ CHECK_END(p_arr, idx, "NetworkProfilerFrame");
+ return true;
+}
+
+Array DebuggerMarshalls::ServersProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(frame_number);
+ arr.push_back(frame_time);
+ arr.push_back(idle_time);
+ arr.push_back(physics_time);
+ arr.push_back(physics_frame_time);
+ arr.push_back(script_time);
+
+ arr.push_back(servers.size());
+ for (int i = 0; i < servers.size(); i++) {
+ ServerInfo &s = servers[i];
+ arr.push_back(s.name);
+ arr.push_back(s.functions.size() * 2);
+ for (int j = 0; j < s.functions.size(); j++) {
+ ServerFunctionInfo &f = s.functions[j];
+ arr.push_back(f.name);
+ arr.push_back(f.time);
+ }
+ }
+
+ arr.push_back(script_functions.size() * 4);
+ for (int i = 0; i < script_functions.size(); i++) {
+ arr.push_back(script_functions[i].sig_id);
+ arr.push_back(script_functions[i].call_count);
+ arr.push_back(script_functions[i].self_time);
+ arr.push_back(script_functions[i].total_time);
+ }
+ return arr;
+}
+
+bool DebuggerMarshalls::ServersProfilerFrame::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 7, "ServersProfilerFrame");
+ frame_number = p_arr[0];
+ frame_time = p_arr[1];
+ idle_time = p_arr[2];
+ physics_time = p_arr[3];
+ physics_frame_time = p_arr[4];
+ script_time = p_arr[5];
+ int servers_size = p_arr[6];
+ int idx = 7;
+ while (servers_size) {
+ CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame");
+ servers_size--;
+ ServerInfo si;
+ si.name = p_arr[idx];
+ int sub_data_size = p_arr[idx + 1];
+ idx += 2;
+ CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame");
+ for (int j = 0; j < sub_data_size / 2; j++) {
+ ServerFunctionInfo sf;
+ sf.name = p_arr[idx];
+ sf.time = p_arr[idx + 1];
+ idx += 2;
+ si.functions.push_back(sf);
+ }
+ servers.push_back(si);
+ }
+ CHECK_SIZE(p_arr, idx + 3, "ServersProfilerFrame");
+ int func_size = p_arr[idx];
+ idx += 1;
+ CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame");
+ for (int i = 0; i < func_size / 4; i++) {
+ ScriptFunctionInfo fi;
+ fi.sig_id = p_arr[idx];
+ fi.call_count = p_arr[idx + 1];
+ fi.self_time = p_arr[idx + 2];
+ fi.total_time = p_arr[idx + 3];
+ script_functions.push_back(fi);
+ idx += 4;
+ }
+ CHECK_END(p_arr, idx, "ServersProfilerFrame");
+ return true;
+}
+
+Array DebuggerMarshalls::ScriptStackDump::serialize() {
+ Array arr;
+ arr.push_back(frames.size() * 3);
+ for (int i = 0; i < frames.size(); i++) {
+ arr.push_back(frames[i].file);
+ arr.push_back(frames[i].line);
+ arr.push_back(frames[i].func);
+ }
+ return arr;
+}
+
+bool DebuggerMarshalls::ScriptStackDump::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "ScriptStackDump");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "ScriptStackDump");
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 3; i++) {
+ ScriptLanguage::StackInfo sf;
+ sf.file = p_arr[idx];
+ sf.line = p_arr[idx + 1];
+ sf.func = p_arr[idx + 2];
+ frames.push_back(sf);
+ idx += 3;
+ }
+ CHECK_END(p_arr, idx, "ScriptStackDump");
+ return true;
+}
+
+Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) {
+ Array arr;
+ arr.push_back(name);
+ arr.push_back(type);
+
+ Variant var = value;
+ if (value.get_type() == Variant::OBJECT && value.get_validated_object() == nullptr) {
+ var = Variant();
+ }
+
+ int len = 0;
+ Error err = encode_variant(var, NULL, len, true);
+ if (err != OK)
+ ERR_PRINT("Failed to encode variant.");
+
+ if (len > max_size) {
+ arr.push_back(Variant());
+ } else {
+ arr.push_back(var);
+ }
+ return arr;
+}
+
+bool DebuggerMarshalls::ScriptStackVariable::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 3, "ScriptStackVariable");
+ name = p_arr[0];
+ type = p_arr[1];
+ value = p_arr[2];
+ CHECK_END(p_arr, 3, "ScriptStackVariable");
+ return true;
+}
+
+Array DebuggerMarshalls::OutputError::serialize() {
+ Array arr;
+ arr.push_back(hr);
+ arr.push_back(min);
+ arr.push_back(sec);
+ arr.push_back(msec);
+ arr.push_back(source_file);
+ arr.push_back(source_func);
+ arr.push_back(source_line);
+ arr.push_back(error);
+ arr.push_back(error_descr);
+ arr.push_back(warning);
+ unsigned int size = callstack.size();
+ const ScriptLanguage::StackInfo *r = callstack.ptr();
+ arr.push_back(size * 3);
+ for (int i = 0; i < callstack.size(); i++) {
+ arr.push_back(r[i].file);
+ arr.push_back(r[i].func);
+ arr.push_back(r[i].line);
+ }
+ return arr;
+}
+
+bool DebuggerMarshalls::OutputError::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 11, "OutputError");
+ hr = p_arr[0];
+ min = p_arr[1];
+ sec = p_arr[2];
+ msec = p_arr[3];
+ source_file = p_arr[4];
+ source_func = p_arr[5];
+ source_line = p_arr[6];
+ error = p_arr[7];
+ error_descr = p_arr[8];
+ warning = p_arr[9];
+ unsigned int stack_size = p_arr[10];
+ CHECK_SIZE(p_arr, stack_size, "OutputError");
+ int idx = 11;
+ callstack.resize(stack_size / 3);
+ ScriptLanguage::StackInfo *w = callstack.ptrw();
+ for (unsigned int i = 0; i < stack_size / 3; i++) {
+ w[i].file = p_arr[idx];
+ w[i].func = p_arr[idx + 1];
+ w[i].line = p_arr[idx + 2];
+ idx += 3;
+ }
+ CHECK_END(p_arr, idx, "OutputError");
+ return true;
+}
+
+Array DebuggerMarshalls::VisualProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(frame_number);
+ arr.push_back(areas.size() * 3);
+ for (int i = 0; i < areas.size(); i++) {
+ arr.push_back(areas[i].name);
+ arr.push_back(areas[i].cpu_msec);
+ arr.push_back(areas[i].gpu_msec);
+ }
+ return arr;
+}
+
+bool DebuggerMarshalls::VisualProfilerFrame::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 2, "VisualProfilerFrame");
+ frame_number = p_arr[0];
+ int size = p_arr[1];
+ CHECK_SIZE(p_arr, size, "VisualProfilerFrame");
+ int idx = 2;
+ areas.resize(size / 3);
+ VS::FrameProfileArea *w = areas.ptrw();
+ for (int i = 0; i < size / 3; i++) {
+ w[i].name = p_arr[idx];
+ w[i].cpu_msec = p_arr[idx + 1];
+ w[i].gpu_msec = p_arr[idx + 2];
+ idx += 3;
+ }
+ CHECK_END(p_arr, idx, "VisualProfilerFrame");
+ return true;
+}
diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h
new file mode 100644
index 0000000000..4c15adc555
--- /dev/null
+++ b/core/debugger/debugger_marshalls.h
@@ -0,0 +1,175 @@
+/*************************************************************************/
+/* debugger_marshalls.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef DEBUGGER_MARSHARLLS_H
+#define DEBUGGER_MARSHARLLS_H
+
+#include "core/script_language.h"
+#include "servers/visual_server.h"
+
+struct DebuggerMarshalls {
+
+ // Memory usage
+ struct ResourceInfo {
+ String path;
+ String format;
+ String type;
+ RID id;
+ int vram;
+ bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
+ ResourceInfo() {
+ vram = 0;
+ }
+ };
+
+ struct ResourceUsage {
+ List<ResourceInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ // Network profiler
+ struct MultiplayerNodeInfo {
+ ObjectID node;
+ String node_path;
+ int incoming_rpc = 0;
+ int incoming_rset = 0;
+ int outgoing_rpc = 0;
+ int outgoing_rset = 0;
+ };
+
+ struct NetworkProfilerFrame {
+ Vector<MultiplayerNodeInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ // Script Profiler
+ class ScriptFunctionSignature {
+ public:
+ StringName name;
+ int id = -1;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ struct ScriptFunctionInfo {
+ StringName name;
+ int sig_id = -1;
+ int call_count = 0;
+ float self_time = 0;
+ float total_time = 0;
+ };
+
+ // Servers profiler
+ struct ServerFunctionInfo {
+ StringName name;
+ float time = 0;
+ };
+
+ struct ServerInfo {
+ StringName name;
+ List<ServerFunctionInfo> functions;
+ };
+
+ struct ServersProfilerFrame {
+ int frame_number = 0;
+ float frame_time = 0;
+ float idle_time = 0;
+ float physics_time = 0;
+ float physics_frame_time = 0;
+ float script_time = 0;
+ List<ServerInfo> servers;
+ Vector<ScriptFunctionInfo> script_functions;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ struct ScriptStackVariable {
+ String name;
+ Variant value;
+ int type;
+ ScriptStackVariable() {
+ type = -1;
+ }
+
+ Array serialize(int max_size = 1 << 20); // 1 MiB default.
+ bool deserialize(const Array &p_arr);
+ };
+
+ struct ScriptStackDump {
+ List<ScriptLanguage::StackInfo> frames;
+ ScriptStackDump() {}
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ struct OutputError {
+ int hr;
+ int min;
+ int sec;
+ int msec;
+ String source_file;
+ String source_func;
+ int source_line;
+ String error;
+ String error_descr;
+ bool warning;
+ Vector<ScriptLanguage::StackInfo> callstack;
+
+ OutputError() {
+ hr = -1;
+ min = -1;
+ sec = -1;
+ msec = -1;
+ source_line = -1;
+ warning = false;
+ }
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ // Visual Profiler
+ struct VisualProfilerFrame {
+ uint64_t frame_number;
+ Vector<VS::FrameProfileArea> areas;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+};
+
+#endif // DEBUGGER_MARSHARLLS_H
diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp
new file mode 100644
index 0000000000..305802e5f2
--- /dev/null
+++ b/core/debugger/engine_debugger.cpp
@@ -0,0 +1,186 @@
+/*************************************************************************/
+/* engine_debugger.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "engine_debugger.h"
+
+#include "core/debugger/local_debugger.h"
+#include "core/debugger/remote_debugger.h"
+#include "core/debugger/script_debugger.h"
+#include "core/os/os.h"
+
+EngineDebugger *EngineDebugger::singleton = NULL;
+ScriptDebugger *EngineDebugger::script_debugger = NULL;
+
+Map<StringName, EngineDebugger::Profiler> EngineDebugger::profilers;
+Map<StringName, EngineDebugger::Capture> EngineDebugger::captures;
+
+void EngineDebugger::register_profiler(const StringName &p_name, const Profiler &p_func) {
+ ERR_FAIL_COND_MSG(profilers.has(p_name), "Profiler already registered: " + p_name);
+ profilers.insert(p_name, p_func);
+}
+
+void EngineDebugger::unregister_profiler(const StringName &p_name) {
+ ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
+ Profiler &p = profilers[p_name];
+ if (p.active && p.toggle) {
+ p.toggle(p.data, false, Array());
+ p.active = false;
+ }
+ profilers.erase(p_name);
+}
+
+void EngineDebugger::register_message_capture(const StringName &p_name, Capture p_func) {
+ ERR_FAIL_COND_MSG(captures.has(p_name), "Capture already registered: " + p_name);
+ captures.insert(p_name, p_func);
+}
+
+void EngineDebugger::unregister_message_capture(const StringName &p_name) {
+ ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name);
+ captures.erase(p_name);
+}
+
+void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
+ ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't change profiler state, no profiler: " + p_name);
+ Profiler &p = profilers[p_name];
+ if (p.toggle) {
+ p.toggle(p.data, p_enabled, p_opts);
+ }
+ p.active = p_enabled;
+}
+
+void EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
+ ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't add frame data, no profiler: " + p_name);
+ Profiler &p = profilers[p_name];
+ if (p.add) {
+ p.add(p.data, p_data);
+ }
+}
+
+bool EngineDebugger::is_profiling(const StringName &p_name) {
+ return profilers.has(p_name) && profilers[p_name].active;
+}
+
+bool EngineDebugger::has_profiler(const StringName &p_name) {
+ return profilers.has(p_name);
+}
+
+bool EngineDebugger::has_capture(const StringName &p_name) {
+ return captures.has(p_name);
+}
+
+Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured) {
+ r_captured = false;
+ ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, "Capture not registered: " + p_name);
+ const Capture &cap = captures[p_name];
+ return cap.capture(cap.data, p_msg, p_args, r_captured);
+}
+
+void EngineDebugger::line_poll() {
+ // The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught
+ if (poll_every % 2048 == 0)
+ poll_events(false);
+ poll_every++;
+}
+
+void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_idle_ticks, uint64_t p_physics_ticks, float p_physics_frame_time) {
+ frame_time = USEC_TO_SEC(p_frame_ticks);
+ idle_time = USEC_TO_SEC(p_idle_ticks);
+ physics_time = USEC_TO_SEC(p_physics_ticks);
+ physics_frame_time = p_physics_frame_time;
+ // Notify tick to running profilers
+ for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) {
+ Profiler &p = E->get();
+ if (!p.active || !p.tick)
+ continue;
+ p.tick(p.data, frame_time, idle_time, physics_time, physics_frame_time);
+ }
+ singleton->poll_events(true);
+}
+
+void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Vector<String> p_breakpoints) {
+ if (p_uri.empty())
+ return;
+ if (p_uri == "local://") {
+ singleton = memnew(LocalDebugger);
+ script_debugger = memnew(ScriptDebugger);
+ // Tell the OS that we want to handle termination signals.
+ OS::get_singleton()->initialize_debugging();
+ } else {
+ singleton = RemoteDebugger::create_for_uri(p_uri);
+ if (!singleton)
+ return;
+ script_debugger = memnew(ScriptDebugger);
+ // Notify editor of our pid (to allow focus stealing).
+ Array msg;
+ msg.push_back(OS::get_singleton()->get_process_id());
+ singleton->send_message("set_pid", msg);
+ }
+ if (!singleton)
+ return;
+
+ // There is a debugger, parse breakpoints.
+ ScriptDebugger *script_debugger = singleton->get_script_debugger();
+ script_debugger->set_skip_breakpoints(p_skip_breakpoints);
+
+ for (int i = 0; i < p_breakpoints.size(); i++) {
+
+ String bp = p_breakpoints[i];
+ int sp = bp.find_last(":");
+ ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format.");
+
+ script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp));
+ }
+}
+
+void EngineDebugger::deinitialize() {
+ if (!singleton)
+ return;
+
+ // Stop all profilers
+ for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) {
+ if (E->get().active)
+ singleton->profiler_enable(E->key(), false);
+ }
+
+ // Flush any remaining message
+ singleton->poll_events(false);
+
+ memdelete(singleton);
+ singleton = NULL;
+ profilers.clear();
+ captures.clear();
+}
+
+EngineDebugger::~EngineDebugger() {
+ if (script_debugger)
+ memdelete(script_debugger);
+ script_debugger = NULL;
+ singleton = NULL;
+}
diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h
new file mode 100644
index 0000000000..9e01aeba18
--- /dev/null
+++ b/core/debugger/engine_debugger.h
@@ -0,0 +1,130 @@
+/*************************************************************************/
+/* engine_debugger.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef ENGINE_DEBUGGER_H
+#define ENGINE_DEBUGGER_H
+
+#include "core/array.h"
+#include "core/map.h"
+#include "core/string_name.h"
+#include "core/ustring.h"
+#include "core/variant.h"
+#include "core/vector.h"
+
+class ScriptDebugger;
+
+class EngineDebugger {
+public:
+ typedef void (*ProfilingToggle)(void *p_user, bool p_enable, const Array &p_opts);
+ typedef void (*ProfilingTick)(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+ typedef void (*ProfilingAdd)(void *p_user, const Array &p_arr);
+ typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
+
+ class Profiler {
+ friend class EngineDebugger;
+
+ ProfilingToggle toggle = NULL;
+ ProfilingAdd add = NULL;
+ ProfilingTick tick = NULL;
+ void *data = NULL;
+ bool active = false;
+
+ public:
+ Profiler() {}
+ Profiler(void *p_data, ProfilingToggle p_toggle, ProfilingAdd p_add, ProfilingTick p_tick) {
+ data = p_data;
+ toggle = p_toggle;
+ add = p_add;
+ tick = p_tick;
+ }
+ };
+
+ class Capture {
+ friend class EngineDebugger;
+
+ CaptureFunc capture = NULL;
+ void *data = NULL;
+
+ public:
+ Capture() {}
+ Capture(void *p_data, CaptureFunc p_capture) {
+ data = p_data;
+ capture = p_capture;
+ }
+ };
+
+private:
+ float frame_time = 0.0;
+ float idle_time = 0.0;
+ float physics_time = 0.0;
+ float physics_frame_time = 0.0;
+
+ uint32_t poll_every = 0;
+
+protected:
+ static EngineDebugger *singleton;
+ static ScriptDebugger *script_debugger;
+
+ static Map<StringName, Profiler> profilers;
+ static Map<StringName, Capture> captures;
+
+public:
+ _FORCE_INLINE_ static EngineDebugger *get_singleton() { return singleton; }
+ _FORCE_INLINE_ static bool is_active() { return singleton != NULL && script_debugger != NULL; }
+
+ _FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; };
+
+ static void initialize(const String &p_uri, bool p_skip_breakpoints, Vector<String> p_breakpoints);
+ static void deinitialize();
+ static void register_profiler(const StringName &p_name, const Profiler &p_profiler);
+ static void unregister_profiler(const StringName &p_name);
+ static bool is_profiling(const StringName &p_name);
+ static bool has_profiler(const StringName &p_name);
+ static void profiler_add_frame_data(const StringName &p_name, const Array &p_data);
+
+ static void register_message_capture(const StringName &p_name, Capture p_func);
+ static void unregister_message_capture(const StringName &p_name);
+ static bool has_capture(const StringName &p_name);
+
+ void iteration(uint64_t p_frame_ticks, uint64_t p_idle_ticks, uint64_t p_physics_ticks, float p_physics_frame_time);
+ void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array());
+ Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured);
+
+ void line_poll();
+
+ virtual void poll_events(bool p_is_idle) {}
+ virtual void send_message(const String &p_msg, const Array &p_data) = 0;
+ virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) = 0;
+ virtual void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;
+
+ virtual ~EngineDebugger();
+};
+
+#endif // ENGINE_DEBUGGER_H
diff --git a/core/script_debugger_local.cpp b/core/debugger/local_debugger.cpp
index c64638e7ce..913d3fc031 100644
--- a/core/script_debugger_local.cpp
+++ b/core/debugger/local_debugger.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* script_debugger_local.cpp */
+/* local_debugger.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,28 +28,112 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "script_debugger_local.h"
+#include "local_debugger.h"
+#include "core/debugger/script_debugger.h"
#include "core/os/os.h"
#include "scene/main/scene_tree.h"
-void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
+struct LocalDebugger::ScriptsProfiler {
+ struct ProfileInfoSort {
+
+ bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const {
+ return A.total_time > B.total_time;
+ }
+ };
+
+ float frame_time = 0;
+ uint64_t idle_accum = 0;
+ Vector<ScriptLanguage::ProfilingInfo> pinfo;
+
+ void toggle(bool p_enable, const Array &p_opts) {
+ if (p_enable) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_start();
+ }
+
+ print_line("BEGIN PROFILING");
+ pinfo.resize(32768);
+ } else {
+ _print_frame_data(true);
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_stop();
+ }
+ }
+ }
+
+ void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ frame_time = p_frame_time;
+ _print_frame_data(false);
+ }
+
+ void _print_frame_data(bool p_accumulated) {
+ uint64_t diff = OS::get_singleton()->get_ticks_usec() - idle_accum;
+
+ if (!p_accumulated && diff < 1000000) //show every one second
+ return;
+
+ idle_accum = OS::get_singleton()->get_ticks_usec();
+
+ int ofs = 0;
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (p_accumulated)
+ ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs);
+ else
+ ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs);
+ }
+
+ SortArray<ScriptLanguage::ProfilingInfo, ProfileInfoSort> sort;
+ sort.sort(pinfo.ptrw(), ofs);
+
+ // compute total script frame time
+ uint64_t script_time_us = 0;
+ for (int i = 0; i < ofs; i++) {
+
+ script_time_us += pinfo[i].self_time;
+ }
+ float script_time = USEC_TO_SEC(script_time_us);
+ float total_time = p_accumulated ? script_time : frame_time;
+
+ if (!p_accumulated) {
+ print_line("FRAME: total: " + rtos(total_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %");
+ } else {
+ print_line("ACCUMULATED: total: " + rtos(total_time));
+ }
+
+ for (int i = 0; i < ofs; i++) {
+
+ print_line(itos(i) + ":" + pinfo[i].signature);
+ float tt = USEC_TO_SEC(pinfo[i].total_time);
+ float st = USEC_TO_SEC(pinfo[i].self_time);
+ print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count));
+ }
+ }
+
+ ScriptsProfiler() {
+ idle_accum = OS::get_singleton()->get_ticks_usec();
+ }
+};
+
+void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
+
+ ScriptLanguage *script_lang = script_debugger->get_break_language();
if (!target_function.empty()) {
- String current_function = p_script->debug_get_stack_level_function(0);
+ String current_function = script_lang->debug_get_stack_level_function(0);
if (current_function != target_function) {
- set_depth(0);
- set_lines_left(1);
+ script_debugger->set_depth(0);
+ script_debugger->set_lines_left(1);
return;
}
target_function = "";
}
- print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'");
- print_line("*Frame " + itos(0) + " - " + p_script->debug_get_stack_level_source(0) + ":" + itos(p_script->debug_get_stack_level_line(0)) + " in function '" + p_script->debug_get_stack_level_function(0) + "'");
+ print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'");
+ print_line("*Frame " + itos(0) + " - " + script_lang->debug_get_stack_level_source(0) + ":" + itos(script_lang->debug_get_stack_level_line(0)) + " in function '" + script_lang->debug_get_stack_level_function(0) + "'");
print_line("Enter \"help\" for assistance.");
int current_frame = 0;
- int total_frames = p_script->debug_get_stack_level_count();
+ int total_frames = script_lang->debug_get_stack_level_count();
while (true) {
OS::get_singleton()->print("debug> ");
@@ -59,8 +143,8 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
String variable_prefix = options["variable_prefix"];
if (line == "") {
- print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'");
- print_line("*Frame " + itos(current_frame) + " - " + p_script->debug_get_stack_level_source(current_frame) + ":" + itos(p_script->debug_get_stack_level_line(current_frame)) + " in function '" + p_script->debug_get_stack_level_function(current_frame) + "'");
+ print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'");
+ print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'");
print_line("Enter \"help\" for assistance.");
} else if (line == "c" || line == "continue")
break;
@@ -69,20 +153,20 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
for (int i = 0; i < total_frames; i++) {
String cfi = (current_frame == i) ? "*" : " "; //current frame indicator
- print_line(cfi + "Frame " + itos(i) + " - " + p_script->debug_get_stack_level_source(i) + ":" + itos(p_script->debug_get_stack_level_line(i)) + " in function '" + p_script->debug_get_stack_level_function(i) + "'");
+ print_line(cfi + "Frame " + itos(i) + " - " + script_lang->debug_get_stack_level_source(i) + ":" + itos(script_lang->debug_get_stack_level_line(i)) + " in function '" + script_lang->debug_get_stack_level_function(i) + "'");
}
} else if (line.begins_with("fr") || line.begins_with("frame")) {
if (line.get_slice_count(" ") == 1) {
- print_line("*Frame " + itos(current_frame) + " - " + p_script->debug_get_stack_level_source(current_frame) + ":" + itos(p_script->debug_get_stack_level_line(current_frame)) + " in function '" + p_script->debug_get_stack_level_function(current_frame) + "'");
+ print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'");
} else {
int frame = line.get_slicec(' ', 1).to_int();
if (frame < 0 || frame >= total_frames) {
print_line("Error: Invalid frame.");
} else {
current_frame = frame;
- print_line("*Frame " + itos(frame) + " - " + p_script->debug_get_stack_level_source(frame) + ":" + itos(p_script->debug_get_stack_level_line(frame)) + " in function '" + p_script->debug_get_stack_level_function(frame) + "'");
+ print_line("*Frame " + itos(frame) + " - " + script_lang->debug_get_stack_level_source(frame) + ":" + itos(script_lang->debug_get_stack_level_line(frame)) + " in function '" + script_lang->debug_get_stack_level_function(frame) + "'");
}
}
@@ -120,21 +204,21 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
List<String> locals;
List<Variant> values;
- p_script->debug_get_stack_level_locals(current_frame, &locals, &values);
+ script_lang->debug_get_stack_level_locals(current_frame, &locals, &values);
print_variables(locals, values, variable_prefix);
} else if (line == "gv" || line == "globals") {
List<String> globals;
List<Variant> values;
- p_script->debug_get_globals(&globals, &values);
+ script_lang->debug_get_globals(&globals, &values);
print_variables(globals, values, variable_prefix);
} else if (line == "mv" || line == "members") {
List<String> members;
List<Variant> values;
- p_script->debug_get_stack_level_members(current_frame, &members, &values);
+ script_lang->debug_get_stack_level_members(current_frame, &members, &values);
print_variables(members, values, variable_prefix);
} else if (line.begins_with("p") || line.begins_with("print")) {
@@ -144,29 +228,29 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
} else {
String expr = line.get_slicec(' ', 2);
- String res = p_script->debug_parse_stack_level_expression(current_frame, expr);
+ String res = script_lang->debug_parse_stack_level_expression(current_frame, expr);
print_line(res);
}
} else if (line == "s" || line == "step") {
- set_depth(-1);
- set_lines_left(1);
+ script_debugger->set_depth(-1);
+ script_debugger->set_lines_left(1);
break;
} else if (line == "n" || line == "next") {
- set_depth(0);
- set_lines_left(1);
+ script_debugger->set_depth(0);
+ script_debugger->set_lines_left(1);
break;
} else if (line == "fin" || line == "finish") {
- String current_function = p_script->debug_get_stack_level_function(0);
+ String current_function = script_lang->debug_get_stack_level_function(0);
for (int i = 0; i < total_frames; i++) {
- target_function = p_script->debug_get_stack_level_function(i);
+ target_function = script_lang->debug_get_stack_level_function(i);
if (target_function != current_function) {
- set_depth(0);
- set_lines_left(1);
+ script_debugger->set_depth(0);
+ script_debugger->set_lines_left(1);
return;
}
}
@@ -178,7 +262,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
if (line.get_slice_count(" ") <= 1) {
- const Map<int, Set<StringName> > &breakpoints = get_breakpoints();
+ const Map<int, Set<StringName> > &breakpoints = script_debugger->get_breakpoints();
if (breakpoints.size() == 0) {
print_line("No Breakpoints.");
continue;
@@ -199,7 +283,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
if (source.empty())
continue;
- insert_breakpoint(linenr, source);
+ script_debugger->insert_breakpoint(linenr, source);
print_line("Added breakpoint at " + source + ":" + itos(linenr));
}
@@ -207,16 +291,16 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
} else if (line == "q" || line == "quit") {
// Do not stop again on quit
- clear_breakpoints();
- ScriptDebugger::get_singleton()->set_depth(-1);
- ScriptDebugger::get_singleton()->set_lines_left(-1);
+ script_debugger->clear_breakpoints();
+ script_debugger->set_depth(-1);
+ script_debugger->set_lines_left(-1);
SceneTree::get_singleton()->quit();
break;
} else if (line.begins_with("delete")) {
if (line.get_slice_count(" ") <= 1) {
- clear_breakpoints();
+ script_debugger->clear_breakpoints();
} else {
Pair<String, int> breakpoint = to_breakpoint(line);
@@ -227,7 +311,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
if (source.empty())
continue;
- remove_breakpoint(linenr, source);
+ script_debugger->remove_breakpoint(linenr, source);
print_line("Removed breakpoint at " + source + ":" + itos(linenr));
}
@@ -255,7 +339,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
}
}
-void ScriptDebuggerLocal::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) {
+void LocalDebugger::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) {
String value;
Vector<String> value_lines;
@@ -279,7 +363,7 @@ void ScriptDebuggerLocal::print_variables(const List<String> &names, const List<
}
}
-Pair<String, int> ScriptDebuggerLocal::to_breakpoint(const String &p_line) {
+Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) {
String breakpoint_part = p_line.get_slicec(' ', 1);
Pair<String, int> breakpoint;
@@ -290,135 +374,43 @@ Pair<String, int> ScriptDebuggerLocal::to_breakpoint(const String &p_line) {
return breakpoint;
}
- breakpoint.first = breakpoint_find_source(breakpoint_part.left(last_colon).strip_edges());
+ breakpoint.first = script_debugger->breakpoint_find_source(breakpoint_part.left(last_colon).strip_edges());
breakpoint.second = breakpoint_part.right(last_colon).strip_edges().to_int();
return breakpoint;
}
-struct _ScriptDebuggerLocalProfileInfoSort {
-
- bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const {
- return A.total_time > B.total_time;
- }
-};
-
-void ScriptDebuggerLocal::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
-
- frame_time = p_frame_time;
- idle_time = p_idle_time;
- physics_time = p_physics_time;
- physics_frame_time = p_physics_frame_time;
-}
-
-void ScriptDebuggerLocal::idle_poll() {
-
- if (!profiling)
- return;
-
- uint64_t diff = OS::get_singleton()->get_ticks_usec() - idle_accum;
-
- if (diff < 1000000) //show every one second
- return;
-
- idle_accum = OS::get_singleton()->get_ticks_usec();
-
- int ofs = 0;
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs);
- }
-
- SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort;
- sort.sort(pinfo.ptrw(), ofs);
-
- //falta el frame time
-
- uint64_t script_time_us = 0;
-
- for (int i = 0; i < ofs; i++) {
-
- script_time_us += pinfo[i].self_time;
- }
-
- float script_time = USEC_TO_SEC(script_time_us);
-
- float total_time = frame_time;
-
- //print script total
-
- print_line("FRAME: total: " + rtos(frame_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %");
-
- for (int i = 0; i < ofs; i++) {
-
- print_line(itos(i) + ":" + pinfo[i].signature);
- float tt = USEC_TO_SEC(pinfo[i].total_time);
- float st = USEC_TO_SEC(pinfo[i].self_time);
- print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count));
- }
-}
-
-void ScriptDebuggerLocal::profiling_start() {
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->profiling_start();
- }
-
- print_line("BEGIN PROFILING");
- profiling = true;
- pinfo.resize(32768);
- frame_time = 0;
- physics_time = 0;
- idle_time = 0;
- physics_frame_time = 0;
-}
-
-void ScriptDebuggerLocal::profiling_end() {
-
- int ofs = 0;
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs);
- }
-
- SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort;
- sort.sort(pinfo.ptrw(), ofs);
-
- uint64_t total_us = 0;
- for (int i = 0; i < ofs; i++) {
- total_us += pinfo[i].self_time;
- }
-
- float total_time = total_us / 1000000.0;
-
- for (int i = 0; i < ofs; i++) {
-
- print_line(itos(i) + ":" + pinfo[i].signature);
- float tt = USEC_TO_SEC(pinfo[i].total_time);
- float st = USEC_TO_SEC(pinfo[i].self_time);
- print_line("\ttotal_ms: " + rtos(tt) + "\tself_ms: " + rtos(st) + "total%: " + itos(tt * 100 / total_time) + "\tself%: " + itos(st * 100 / total_time) + "\tcalls: " + itos(pinfo[i].call_count));
- }
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->profiling_stop();
- }
-
- profiling = false;
-}
-
-void ScriptDebuggerLocal::send_message(const String &p_message, const Array &p_args) {
+void LocalDebugger::send_message(const String &p_message, const Array &p_args) {
// This needs to be cleaned up entirely.
// print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));
}
-void ScriptDebuggerLocal::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
+void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) {
print_line("ERROR: '" + (p_descr.empty() ? p_err : p_descr) + "'");
}
-ScriptDebuggerLocal::ScriptDebuggerLocal() {
+LocalDebugger::LocalDebugger() {
- profiling = false;
- idle_accum = OS::get_singleton()->get_ticks_usec();
options["variable_prefix"] = "";
+
+ // Bind scripts profiler.
+ scripts_profiler = memnew(ScriptsProfiler);
+ Profiler scr_prof(
+ scripts_profiler,
+ [](void *p_user, bool p_enable, const Array &p_opts) {
+ ((ScriptsProfiler *)p_user)->toggle(p_enable, p_opts);
+ },
+ NULL,
+ [](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ ((ScriptsProfiler *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
+ });
+ register_profiler("scripts", scr_prof);
+}
+
+LocalDebugger::~LocalDebugger() {
+ unregister_profiler("scripts");
+ if (scripts_profiler)
+ memdelete(scripts_profiler);
}
diff --git a/core/script_debugger_local.h b/core/debugger/local_debugger.h
index 7a64400191..e299df0546 100644
--- a/core/script_debugger_local.h
+++ b/core/debugger/local_debugger.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* script_debugger_local.h */
+/* local_debugger.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,40 +28,33 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCRIPT_DEBUGGER_LOCAL_H
-#define SCRIPT_DEBUGGER_LOCAL_H
+#ifndef LOCAL_DEBUGGER_H
+#define LOCAL_DEBUGGER_H
+#include "core/debugger/engine_debugger.h"
#include "core/list.h"
#include "core/script_language.h"
-class ScriptDebuggerLocal : public ScriptDebugger {
+class LocalDebugger : public EngineDebugger {
+
+private:
+ struct ScriptsProfiler;
+
+ ScriptsProfiler *scripts_profiler = NULL;
- bool profiling;
- float frame_time, idle_time, physics_time, physics_frame_time;
- uint64_t idle_accum;
String target_function;
Map<String, String> options;
- Vector<ScriptLanguage::ProfilingInfo> pinfo;
-
Pair<String, int> to_breakpoint(const String &p_line);
void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix);
public:
- void debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint);
- virtual void send_message(const String &p_message, const Array &p_args);
- virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
-
- virtual bool is_profiling() const { return profiling; }
- virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) {}
-
- virtual void idle_poll();
-
- virtual void profiling_start();
- virtual void profiling_end();
- virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+ void debug(bool p_can_continue, bool p_is_error_breakpoint);
+ void send_message(const String &p_message, const Array &p_args);
+ void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type);
- ScriptDebuggerLocal();
+ LocalDebugger();
+ ~LocalDebugger();
};
-#endif // SCRIPT_DEBUGGER_LOCAL_H
+#endif // LOCAL_DEBUGGER_H
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
new file mode 100644
index 0000000000..7952391a27
--- /dev/null
+++ b/core/debugger/remote_debugger.cpp
@@ -0,0 +1,935 @@
+/*************************************************************************/
+/* remote_debugger.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "remote_debugger.h"
+
+#include "core/debugger/debugger_marshalls.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
+#include "core/os/input.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+#include "core/script_language.h"
+#include "scene/main/node.h"
+
+template <typename T>
+void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) {
+ EngineDebugger::Profiler prof(
+ p_prof,
+ [](void *p_user, bool p_enable, const Array &p_opts) {
+ ((T *)p_user)->toggle(p_enable, p_opts);
+ },
+ [](void *p_user, const Array &p_data) {
+ ((T *)p_user)->add(p_data);
+ },
+ [](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ ((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
+ });
+ EngineDebugger::register_profiler(p_name, prof);
+}
+
+struct RemoteDebugger::NetworkProfiler {
+
+public:
+ typedef DebuggerMarshalls::MultiplayerNodeInfo NodeInfo;
+ struct BandwidthFrame {
+ uint32_t timestamp;
+ int packet_size;
+ };
+
+ int bandwidth_in_ptr = 0;
+ Vector<BandwidthFrame> bandwidth_in;
+ int bandwidth_out_ptr = 0;
+ Vector<BandwidthFrame> bandwidth_out;
+ uint64_t last_bandwidth_time = 0;
+
+ Map<ObjectID, NodeInfo> multiplayer_node_data;
+ uint64_t last_profile_time = 0;
+
+ NetworkProfiler() {}
+
+ int bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
+ int total_bandwidth = 0;
+
+ uint32_t timestamp = OS::get_singleton()->get_ticks_msec();
+ uint32_t final_timestamp = timestamp - 1000;
+
+ int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
+
+ while (i != p_pointer && p_buffer[i].packet_size > 0) {
+ if (p_buffer[i].timestamp < final_timestamp) {
+ return total_bandwidth;
+ }
+ total_bandwidth += p_buffer[i].packet_size;
+ i = (i + p_buffer.size() - 1) % p_buffer.size();
+ }
+
+ ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
+ return total_bandwidth;
+ }
+
+ void init_node(const ObjectID p_node) {
+ if (multiplayer_node_data.has(p_node))
+ return;
+ multiplayer_node_data.insert(p_node, DebuggerMarshalls::MultiplayerNodeInfo());
+ multiplayer_node_data[p_node].node = p_node;
+ multiplayer_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
+ multiplayer_node_data[p_node].incoming_rpc = 0;
+ multiplayer_node_data[p_node].incoming_rset = 0;
+ multiplayer_node_data[p_node].outgoing_rpc = 0;
+ multiplayer_node_data[p_node].outgoing_rset = 0;
+ }
+
+ void toggle(bool p_enable, const Array &p_opts) {
+ multiplayer_node_data.clear();
+
+ if (!p_enable) {
+ bandwidth_in.clear();
+ bandwidth_out.clear();
+ } else {
+ bandwidth_in_ptr = 0;
+ bandwidth_in.resize(16384); // ~128kB
+ for (int i = 0; i < bandwidth_in.size(); ++i) {
+ bandwidth_in.write[i].packet_size = -1;
+ }
+ bandwidth_out_ptr = 0;
+ bandwidth_out.resize(16384); // ~128kB
+ for (int i = 0; i < bandwidth_out.size(); ++i) {
+ bandwidth_out.write[i].packet_size = -1;
+ }
+ }
+ }
+
+ void add(const Array &p_data) {
+ ERR_FAIL_COND(p_data.size() < 1);
+ const String type = p_data[0];
+ if (type == "node") {
+ ERR_FAIL_COND(p_data.size() < 3);
+ const ObjectID id = p_data[1];
+ const String what = p_data[2];
+ init_node(id);
+ NodeInfo &info = multiplayer_node_data[id];
+ if (what == "rpc_in") {
+ info.incoming_rpc++;
+ } else if (what == "rpc_out") {
+ info.outgoing_rpc++;
+ } else if (what == "rset_in") {
+ info.incoming_rset = 0;
+ } else if (what == "rset_out") {
+ info.outgoing_rset++;
+ }
+ } else if (type == "bandwidth") {
+ ERR_FAIL_COND(p_data.size() < 4);
+ const String inout = p_data[1];
+ int time = p_data[2];
+ int size = p_data[3];
+ if (inout == "in") {
+ bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
+ bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
+ bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
+ } else if (inout == "out") {
+ bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
+ bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
+ bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
+ }
+ }
+ }
+
+ void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_bandwidth_time > 200) {
+ last_bandwidth_time = pt;
+ int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);
+ int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);
+
+ Array arr;
+ arr.push_back(incoming_bandwidth);
+ arr.push_back(outgoing_bandwidth);
+ EngineDebugger::get_singleton()->send_message("network:bandwidth", arr);
+ }
+ if (pt - last_profile_time > 100) {
+ last_profile_time = pt;
+ DebuggerMarshalls::NetworkProfilerFrame frame;
+ for (Map<ObjectID, NodeInfo>::Element *E = multiplayer_node_data.front(); E; E = E->next()) {
+ frame.infos.push_back(E->get());
+ }
+ multiplayer_node_data.clear();
+ EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize());
+ }
+ }
+};
+
+struct RemoteDebugger::ScriptsProfiler {
+ typedef DebuggerMarshalls::ScriptFunctionSignature FunctionSignature;
+ typedef DebuggerMarshalls::ScriptFunctionInfo FunctionInfo;
+ struct ProfileInfoSort {
+
+ bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
+ return A->total_time < B->total_time;
+ }
+ };
+ Vector<ScriptLanguage::ProfilingInfo> info;
+ Vector<ScriptLanguage::ProfilingInfo *> ptrs;
+ Map<StringName, int> sig_map;
+ int max_frame_functions = 16;
+
+ void toggle(bool p_enable, const Array &p_opts) {
+ if (p_enable) {
+ sig_map.clear();
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_start();
+ }
+ if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) {
+ max_frame_functions = MAX(0, int(p_opts[0]));
+ }
+ } else {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_stop();
+ }
+ }
+ }
+
+ void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {
+ int ofs = 0;
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (p_accumulated)
+ ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);
+ else
+ ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);
+ }
+
+ for (int i = 0; i < ofs; i++) {
+ ptrs.write[i] = &info.write[i];
+ }
+
+ SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
+ sa.sort(ptrs.ptrw(), ofs);
+
+ int to_send = MIN(ofs, max_frame_functions);
+
+ // Check signatures first, and compute total time.
+ r_total = 0;
+ for (int i = 0; i < to_send; i++) {
+ if (!sig_map.has(ptrs[i]->signature)) {
+ int idx = sig_map.size();
+ FunctionSignature sig;
+ sig.name = ptrs[i]->signature;
+ sig.id = idx;
+ EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());
+ sig_map[ptrs[i]->signature] = idx;
+ }
+ r_total += ptrs[i]->self_time;
+ }
+
+ // Send frame, script time, functions information then
+ r_funcs.resize(to_send);
+
+ FunctionInfo *w = r_funcs.ptrw();
+ for (int i = 0; i < to_send; i++) {
+ if (sig_map.has(ptrs[i]->signature)) {
+ w[i].sig_id = sig_map[ptrs[i]->signature];
+ }
+ w[i].call_count = ptrs[i]->call_count;
+ w[i].total_time = ptrs[i]->total_time / 1000000.0;
+ w[i].self_time = ptrs[i]->self_time / 1000000.0;
+ }
+ }
+
+ ScriptsProfiler() {
+ info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+ ptrs.resize(info.size());
+ }
+};
+
+struct RemoteDebugger::ServersProfiler {
+
+ bool skip_profile_frame = false;
+ typedef DebuggerMarshalls::ServerInfo ServerInfo;
+ typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
+
+ Map<StringName, ServerInfo> server_data;
+ ScriptsProfiler scripts_profiler;
+
+ float frame_time = 0;
+ float idle_time = 0;
+ float physics_time = 0;
+ float physics_frame_time = 0;
+
+ void toggle(bool p_enable, const Array &p_opts) {
+ skip_profile_frame = false;
+ if (p_enable) {
+ server_data.clear(); // Clear old profiling data.
+ } else {
+ _send_frame_data(true); // Send final frame.
+ }
+ scripts_profiler.toggle(p_enable, p_opts);
+ }
+
+ void add(const Array &p_data) {
+ String name = p_data[0];
+ if (!server_data.has(name)) {
+ ServerInfo info;
+ info.name = name;
+ server_data[name] = info;
+ }
+ ServerInfo &srv = server_data[name];
+
+ ServerFunctionInfo fi;
+ fi.name = p_data[1];
+ fi.time = p_data[2];
+ srv.functions.push_back(fi);
+ }
+
+ void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ frame_time = p_frame_time;
+ idle_time = p_idle_time;
+ physics_time = p_physics_time;
+ physics_frame_time = p_physics_frame_time;
+ _send_frame_data(false);
+ }
+
+ void _send_frame_data(bool p_final) {
+ DebuggerMarshalls::ServersProfilerFrame frame;
+ frame.frame_number = Engine::get_singleton()->get_frames_drawn();
+ frame.frame_time = frame_time;
+ frame.idle_time = idle_time;
+ frame.physics_time = physics_time;
+ frame.physics_frame_time = physics_frame_time;
+ Map<StringName, ServerInfo>::Element *E = server_data.front();
+ while (E) {
+ if (!p_final) {
+ frame.servers.push_back(E->get());
+ }
+ E->get().functions.clear();
+ E = E->next();
+ }
+ uint64_t time = 0;
+ scripts_profiler.write_frame_data(frame.script_functions, time, p_final);
+ frame.script_time = USEC_TO_SEC(time);
+ if (skip_profile_frame) {
+ skip_profile_frame = false;
+ return;
+ }
+ if (p_final) {
+ EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());
+ } else {
+ EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());
+ }
+ }
+};
+
+struct RemoteDebugger::VisualProfiler {
+
+ typedef DebuggerMarshalls::ServerInfo ServerInfo;
+ typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
+
+ Map<StringName, ServerInfo> server_data;
+
+ void toggle(bool p_enable, const Array &p_opts) {
+ VS::get_singleton()->set_frame_profiling_enabled(p_enable);
+ }
+
+ void add(const Array &p_data) {}
+
+ void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ Vector<VS::FrameProfileArea> profile_areas = VS::get_singleton()->get_frame_profile();
+ DebuggerMarshalls::VisualProfilerFrame frame;
+ if (!profile_areas.size())
+ return;
+
+ frame.frame_number = VS::get_singleton()->get_frame_profile_frame();
+ frame.areas.append_array(profile_areas);
+ EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());
+ }
+};
+
+struct RemoteDebugger::PerformanceProfiler {
+
+ Object *performance = NULL;
+ int last_perf_time = 0;
+
+ void toggle(bool p_enable, const Array &p_opts) {}
+ void add(const Array &p_data) {}
+ void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ if (!performance)
+ return;
+
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_perf_time < 1000)
+ return;
+ last_perf_time = pt;
+ int max = performance->get("MONITOR_MAX");
+ Array arr;
+ arr.resize(max);
+ for (int i = 0; i < max; i++) {
+ arr[i] = performance->call("get_monitor", i);
+ }
+ EngineDebugger::get_singleton()->send_message("performance:profile_frame", arr);
+ }
+
+ PerformanceProfiler(Object *p_performance) {
+ performance = p_performance;
+ }
+};
+
+void RemoteDebugger::_send_resource_usage() {
+
+ DebuggerMarshalls::ResourceUsage usage;
+
+ List<VS::TextureInfo> tinfo;
+ VS::get_singleton()->texture_debug_usage(&tinfo);
+
+ for (List<VS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) {
+
+ DebuggerMarshalls::ResourceInfo info;
+ info.path = E->get().path;
+ info.vram = E->get().bytes;
+ info.id = E->get().texture;
+ info.type = "Texture";
+ if (E->get().depth == 0) {
+ info.format = itos(E->get().width) + "x" + itos(E->get().height) + " " + Image::get_format_name(E->get().format);
+ } else {
+ info.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format);
+ }
+ usage.infos.push_back(info);
+ }
+
+ EngineDebugger::get_singleton()->send_message("memory:usage", usage.serialize());
+}
+
+Error RemoteDebugger::_put_msg(String p_message, Array p_data) {
+ Array msg;
+ msg.push_back(p_message);
+ msg.push_back(p_data);
+ Error err = peer->put_message(msg);
+ if (err != OK)
+ n_messages_dropped++;
+ return err;
+}
+
+void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) {
+
+ if (p_type == ERR_HANDLER_SCRIPT)
+ return; //ignore script errors, those go through debugger
+
+ RemoteDebugger *rd = (RemoteDebugger *)p_this;
+ if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) // Can't handle recursive errors during flush.
+ return;
+
+ Vector<ScriptLanguage::StackInfo> si;
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ si = ScriptServer::get_language(i)->debug_get_current_stack_info();
+ if (si.size())
+ break;
+ }
+
+ // send_error will lock internally.
+ rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
+}
+
+void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) {
+
+ RemoteDebugger *rd = (RemoteDebugger *)p_this;
+
+ if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) // Can't handle recursive prints during flush.
+ return;
+
+ String s = p_string;
+ int allowed_chars = MIN(MAX(rd->max_chars_per_second - rd->char_count, 0), s.length());
+
+ if (allowed_chars == 0)
+ return;
+
+ if (allowed_chars < s.length()) {
+ s = s.substr(0, allowed_chars);
+ }
+
+ MutexLock lock(rd->mutex);
+
+ rd->char_count += allowed_chars;
+ bool overflowed = rd->char_count >= rd->max_chars_per_second;
+ if (rd->is_peer_connected()) {
+ if (overflowed)
+ s += "[...]";
+ rd->output_strings.push_back(s);
+
+ if (overflowed) {
+ rd->output_strings.push_back("[output overflow, print less text!]");
+ }
+ }
+}
+
+RemoteDebugger::ErrorMessage RemoteDebugger::_create_overflow_error(const String &p_what, const String &p_descr) {
+ ErrorMessage oe;
+ oe.error = p_what;
+ oe.error_descr = p_descr;
+ oe.warning = false;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ return oe;
+}
+
+void RemoteDebugger::flush_output() {
+ flush_thread = Thread::get_caller_id();
+ flushing = true;
+ MutexLock lock(mutex);
+ if (!is_peer_connected())
+ return;
+
+ if (n_messages_dropped > 0) {
+ ErrorMessage err_msg = _create_overflow_error("TOO_MANY_MESSAGES", "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped. Profiling might misbheave, try raising 'network/limits/debugger/max_queued_messages' in project setting.");
+ if (_put_msg("error", err_msg.serialize()) == OK)
+ n_messages_dropped = 0;
+ }
+
+ if (output_strings.size()) {
+
+ // Join output strings so we generate less messages.
+ Vector<String> strings;
+ strings.resize(output_strings.size());
+ String *w = strings.ptrw();
+ for (int i = 0; i < output_strings.size(); i++) {
+ w[i] = output_strings[i];
+ }
+
+ Array arr;
+ arr.push_back(strings);
+ _put_msg("output", arr);
+ output_strings.clear();
+ }
+
+ while (errors.size()) {
+ ErrorMessage oe = errors.front()->get();
+ _put_msg("error", oe.serialize());
+ errors.pop_front();
+ }
+
+ // Update limits
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+
+ if (ticks - last_reset > 1000) {
+ last_reset = ticks;
+ char_count = 0;
+ err_count = 0;
+ n_errors_dropped = 0;
+ warn_count = 0;
+ n_warnings_dropped = 0;
+ }
+ flushing = false;
+}
+
+void RemoteDebugger::send_message(const String &p_message, const Array &p_args) {
+
+ MutexLock lock(mutex);
+ if (is_peer_connected()) {
+ _put_msg(p_message, p_args);
+ }
+}
+
+void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) {
+
+ ErrorMessage oe;
+ oe.error = p_err;
+ oe.error_descr = p_descr;
+ oe.source_file = p_file;
+ oe.source_line = p_line;
+ oe.source_func = p_func;
+ oe.warning = p_type == ERR_HANDLER_WARNING;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ oe.callstack.append_array(script_debugger->get_error_stack_info());
+
+ if (flushing && Thread::get_caller_id() == flush_thread) // Can't handle recursive errors during flush.
+ return;
+
+ MutexLock lock(mutex);
+
+ if (oe.warning) {
+ warn_count++;
+ } else {
+ err_count++;
+ }
+
+ if (is_peer_connected()) {
+
+ if (oe.warning) {
+ if (warn_count > max_warnings_per_second) {
+ n_warnings_dropped++;
+ if (n_warnings_dropped == 1) {
+ // Only print one message about dropping per second
+ ErrorMessage overflow = _create_overflow_error("TOO_MANY_WARNINGS", "Too many warnings! Ignoring warnings for up to 1 second.");
+ errors.push_back(overflow);
+ }
+ } else {
+ errors.push_back(oe);
+ }
+ } else {
+ if (err_count > max_errors_per_second) {
+ n_errors_dropped++;
+ if (n_errors_dropped == 1) {
+ // Only print one message about dropping per second
+ ErrorMessage overflow = _create_overflow_error("TOO_MANY_ERRORS", "Too many errors! Ignoring errors for up to 1 second.");
+ errors.push_back(overflow);
+ }
+ } else {
+ errors.push_back(oe);
+ }
+ }
+ }
+}
+
+void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type) {
+ DebuggerMarshalls::ScriptStackVariable stvar;
+ List<String>::Element *E = p_names.front();
+ List<Variant>::Element *F = p_vals.front();
+ while (E) {
+ stvar.name = E->get();
+ stvar.value = F->get();
+ stvar.type = p_type;
+ send_message("stack_frame_var", stvar.serialize());
+ E = E->next();
+ F = F->next();
+ }
+}
+
+Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, bool &r_captured) {
+ const int idx = p_msg.find(":");
+ r_captured = false;
+ if (idx < 0) { // No prefix, unknown message.
+ return OK;
+ }
+ const String cap = p_msg.substr(0, idx);
+ if (!has_capture(cap))
+ return ERR_UNAVAILABLE; // Unknown message...
+ const String msg = p_msg.substr(idx + 1);
+ return capture_parse(cap, msg, p_data, r_captured);
+}
+
+void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
+
+ //this function is called when there is a debugger break (bug on script)
+ //or when execution is paused from editor
+
+ if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint)
+ return;
+
+ ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
+
+ ScriptLanguage *script_lang = script_debugger->get_break_language();
+ const String error_str = script_lang ? script_lang->debug_get_error() : "";
+ Array msg;
+ msg.push_back(p_can_continue);
+ msg.push_back(error_str);
+ send_message("debug_enter", msg);
+
+ servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug.
+
+ Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+
+ uint64_t loop_begin_usec = 0;
+ uint64_t loop_time_sec = 0;
+ while (is_peer_connected()) {
+ loop_begin_usec = OS::get_singleton()->get_ticks_usec();
+
+ flush_output();
+ peer->poll();
+
+ if (peer->has_message()) {
+
+ Array cmd = peer->get_message();
+
+ ERR_CONTINUE(cmd.size() != 2);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+ ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
+
+ String command = cmd[0];
+ Array data = cmd[1];
+
+ if (command == "step") {
+ script_debugger->set_depth(-1);
+ script_debugger->set_lines_left(1);
+ break;
+
+ } else if (command == "next") {
+ script_debugger->set_depth(0);
+ script_debugger->set_lines_left(1);
+ break;
+
+ } else if (command == "continue") {
+ script_debugger->set_depth(-1);
+ script_debugger->set_lines_left(-1);
+ OS::get_singleton()->move_window_to_foreground();
+ break;
+
+ } else if (command == "break") {
+ ERR_PRINT("Got break when already broke!");
+ break;
+
+ } else if (command == "get_stack_dump") {
+ ERR_FAIL_COND(!script_lang);
+ DebuggerMarshalls::ScriptStackDump dump;
+ int slc = script_lang->debug_get_stack_level_count();
+ for (int i = 0; i < slc; i++) {
+ ScriptLanguage::StackInfo frame;
+ frame.file = script_lang->debug_get_stack_level_source(i);
+ frame.line = script_lang->debug_get_stack_level_line(i);
+ frame.func = script_lang->debug_get_stack_level_function(i);
+ dump.frames.push_back(frame);
+ }
+ send_message("stack_dump", dump.serialize());
+
+ } else if (command == "get_stack_frame_vars") {
+ ERR_FAIL_COND(data.size() != 1);
+ ERR_FAIL_COND(!script_lang);
+ int lv = data[0];
+
+ List<String> members;
+ List<Variant> member_vals;
+ if (ScriptInstance *inst = script_lang->debug_get_stack_level_instance(lv)) {
+ members.push_back("self");
+ member_vals.push_back(inst->get_owner());
+ }
+ script_lang->debug_get_stack_level_members(lv, &members, &member_vals);
+ ERR_FAIL_COND(members.size() != member_vals.size());
+
+ List<String> locals;
+ List<Variant> local_vals;
+ script_lang->debug_get_stack_level_locals(lv, &locals, &local_vals);
+ ERR_FAIL_COND(locals.size() != local_vals.size());
+
+ List<String> globals;
+ List<Variant> globals_vals;
+ script_lang->debug_get_globals(&globals, &globals_vals);
+ ERR_FAIL_COND(globals.size() != globals_vals.size());
+
+ send_message("stack_frame_vars", Array());
+ _send_stack_vars(locals, local_vals, 0);
+ _send_stack_vars(members, member_vals, 1);
+ _send_stack_vars(globals, globals_vals, 2);
+
+ } else if (command == "reload_scripts") {
+ reload_all_scripts = true;
+
+ } else if (command == "breakpoint") {
+ ERR_FAIL_COND(data.size() < 3);
+ bool set = data[2];
+ if (set)
+ script_debugger->insert_breakpoint(data[1], data[0]);
+ else
+ script_debugger->remove_breakpoint(data[1], data[0]);
+
+ } else if (command == "set_skip_breakpoints") {
+ ERR_FAIL_COND(data.size() < 1);
+ script_debugger->set_skip_breakpoints(data[0]);
+ } else {
+ bool captured = false;
+ ERR_CONTINUE(_try_capture(command, data, captured) != OK);
+ if (!captured)
+ WARN_PRINT("Unknown message received from debugger: " + command);
+ }
+ } else {
+ OS::get_singleton()->delay_usec(10000);
+ OS::get_singleton()->process_and_drop_events();
+ }
+
+ // This is for the camera override to stay live even when the game is paused from the editor
+ loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f;
+ VisualServer::get_singleton()->sync();
+ if (VisualServer::get_singleton()->has_changed()) {
+ VisualServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale());
+ }
+ }
+
+ send_message("debug_exit", Array());
+
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(mouse_mode);
+}
+
+void RemoteDebugger::poll_events(bool p_is_idle) {
+ if (peer.is_null())
+ return;
+
+ flush_output();
+ peer->poll();
+ while (peer->has_message()) {
+
+ Array arr = peer->get_message();
+
+ ERR_CONTINUE(arr.size() != 2);
+ ERR_CONTINUE(arr[0].get_type() != Variant::STRING);
+ ERR_CONTINUE(arr[1].get_type() != Variant::ARRAY);
+
+ const String cmd = arr[0];
+ const int idx = cmd.find(":");
+ bool parsed = false;
+ if (idx < 0) { // Not prefix, use scripts capture.
+ capture_parse("core", cmd, arr[1], parsed);
+ continue;
+ }
+
+ const String cap = cmd.substr(0, idx);
+ if (!has_capture(cap))
+ continue; // Unknown message...
+
+ const String msg = cmd.substr(idx + 1);
+ capture_parse(cap, msg, arr[1], parsed);
+ }
+
+ // Reload scripts during idle poll only.
+ if (p_is_idle && reload_all_scripts) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_all_scripts();
+ }
+ reload_all_scripts = false;
+ }
+}
+
+Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
+ r_captured = true;
+ if (p_cmd == "reload_scripts") {
+ reload_all_scripts = true;
+
+ } else if (p_cmd == "breakpoint") {
+ ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA);
+ bool set = p_data[2];
+ if (set)
+ script_debugger->insert_breakpoint(p_data[1], p_data[0]);
+ else
+ script_debugger->remove_breakpoint(p_data[1], p_data[0]);
+
+ } else if (p_cmd == "set_skip_breakpoints") {
+ ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
+ script_debugger->set_skip_breakpoints(p_data[0]);
+ } else if (p_cmd == "memory") {
+ _send_resource_usage();
+ } else if (p_cmd == "break") {
+ script_debugger->debug(script_debugger->get_break_language());
+ } else {
+ r_captured = false;
+ }
+ return OK;
+}
+
+Error RemoteDebugger::_profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
+ r_captured = false;
+ ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(p_data[0].get_type() != Variant::BOOL, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(!has_profiler(p_cmd), ERR_UNAVAILABLE);
+ Array opts;
+ if (p_data.size() > 1) { // Optional profiler parameters.
+ ERR_FAIL_COND_V(p_data[1].get_type() != Variant::ARRAY, ERR_INVALID_DATA);
+ opts = p_data[1];
+ }
+ r_captured = true;
+ profiler_enable(p_cmd, p_data[0], opts);
+ return OK;
+}
+
+RemoteDebugger *RemoteDebugger::create_for_uri(const String &p_uri) {
+ Ref<RemoteDebuggerPeer> peer = RemoteDebuggerPeer::create_from_uri(p_uri);
+ if (peer.is_valid())
+ return memnew(RemoteDebugger(peer));
+ return NULL;
+}
+
+RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
+ peer = p_peer;
+ max_chars_per_second = GLOBAL_GET("network/limits/debugger/max_chars_per_second");
+ max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second");
+ max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second");
+
+ // Network Profiler
+ network_profiler = memnew(NetworkProfiler);
+ _bind_profiler("network", network_profiler);
+
+ // Servers Profiler (audio/physics/...)
+ servers_profiler = memnew(ServersProfiler);
+ _bind_profiler("servers", servers_profiler);
+
+ // Visual Profiler (cpu/gpu times)
+ visual_profiler = memnew(VisualProfiler);
+ _bind_profiler("visual", visual_profiler);
+
+ // Perfromance Profiler
+ Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
+ if (perf) {
+ performance_profiler = memnew(PerformanceProfiler(perf));
+ _bind_profiler("performance", performance_profiler);
+ profiler_enable("performance", true);
+ }
+
+ // Core and profiler captures.
+ Capture core_cap(this,
+ [](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
+ return ((RemoteDebugger *)p_user)->_core_capture(p_cmd, p_data, r_captured);
+ });
+ register_message_capture("core", core_cap);
+ Capture profiler_cap(this,
+ [](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
+ return ((RemoteDebugger *)p_user)->_profiler_capture(p_cmd, p_data, r_captured);
+ });
+ register_message_capture("profiler", profiler_cap);
+
+ // Error handlers
+ phl.printfunc = _print_handler;
+ phl.userdata = this;
+ add_print_handler(&phl);
+
+ eh.errfunc = _err_handler;
+ eh.userdata = this;
+ add_error_handler(&eh);
+}
+
+RemoteDebugger::~RemoteDebugger() {
+ remove_print_handler(&phl);
+ remove_error_handler(&eh);
+
+ EngineDebugger::get_singleton()->unregister_profiler("servers");
+ EngineDebugger::get_singleton()->unregister_profiler("network");
+ EngineDebugger::get_singleton()->unregister_profiler("visual");
+ if (EngineDebugger::has_profiler("performance")) {
+ EngineDebugger::get_singleton()->unregister_profiler("performance");
+ }
+ memdelete(servers_profiler);
+ memdelete(network_profiler);
+ memdelete(visual_profiler);
+ if (performance_profiler)
+ memdelete(performance_profiler);
+}
diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h
new file mode 100644
index 0000000000..83789c67f9
--- /dev/null
+++ b/core/debugger/remote_debugger.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* remote_debugger.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REMOTE_DEBUGGER_H
+#define REMOTE_DEBUGGER_H
+
+#include "core/array.h"
+#include "core/debugger/debugger_marshalls.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/remote_debugger_peer.h"
+#include "core/object.h"
+#include "core/string_name.h"
+#include "core/ustring.h"
+
+class RemoteDebugger : public EngineDebugger {
+
+private:
+ typedef DebuggerMarshalls::OutputError ErrorMessage;
+
+ struct NetworkProfiler;
+ struct ServersProfiler;
+ struct ScriptsProfiler;
+ struct VisualProfiler;
+ struct PerformanceProfiler;
+
+ NetworkProfiler *network_profiler = NULL;
+ ServersProfiler *servers_profiler = NULL;
+ VisualProfiler *visual_profiler = NULL;
+ PerformanceProfiler *performance_profiler = NULL;
+
+ Ref<RemoteDebuggerPeer> peer;
+
+ List<String> output_strings;
+ List<ErrorMessage> errors;
+
+ int n_messages_dropped = 0;
+ int max_errors_per_second = 0;
+ int max_chars_per_second = 0;
+ int max_warnings_per_second = 0;
+ int n_errors_dropped = 0;
+ int n_warnings_dropped = 0;
+ int char_count = 0;
+ int err_count = 0;
+ int warn_count = 0;
+ int last_reset = 0;
+ bool reload_all_scripts = false;
+
+ // Make handlers and send_message thread safe.
+ Mutex mutex;
+ bool flushing = false;
+ Thread::ID flush_thread = 0;
+
+ PrintHandlerList phl;
+ static void _print_handler(void *p_this, const String &p_string, bool p_error);
+ ErrorHandlerList eh;
+ static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type);
+
+ ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr);
+ Error _put_msg(String p_message, Array p_data);
+
+ bool is_peer_connected() { return peer->is_peer_connected(); }
+ void flush_output();
+
+ void _send_resource_usage();
+ void _send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type);
+
+ Error _profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured);
+ Error _core_capture(const String &p_cmd, const Array &p_data, bool &r_captured);
+
+ template <typename T>
+ void _bind_profiler(const String &p_name, T *p_prof);
+ Error _try_capture(const String &p_name, const Array &p_data, bool &r_captured);
+
+public:
+ static RemoteDebugger *create_for_uri(const String &p_uri);
+
+ // Overrides
+ void poll_events(bool p_is_idle);
+ void send_message(const String &p_message, const Array &p_args);
+ void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type);
+ void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false);
+
+ RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer);
+ ~RemoteDebugger();
+};
+
+#endif // REMOTE_DEBUGGER_H
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
new file mode 100644
index 0000000000..42c2c8e309
--- /dev/null
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -0,0 +1,242 @@
+/*************************************************************************/
+/* remote_debugger_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "remote_debugger_peer.h"
+
+#include "core/io/marshalls.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+
+bool RemoteDebuggerPeerTCP::is_peer_connected() {
+ return connected;
+}
+
+bool RemoteDebuggerPeerTCP::has_message() {
+ return in_queue.size() > 0;
+}
+
+Array RemoteDebuggerPeerTCP::get_message() {
+ MutexLock lock(mutex);
+ ERR_FAIL_COND_V(!has_message(), Array());
+ Array out = in_queue[0];
+ in_queue.pop_front();
+ return out;
+}
+
+Error RemoteDebuggerPeerTCP::put_message(const Array &p_arr) {
+ MutexLock lock(mutex);
+ if (out_queue.size() >= max_queued_messages)
+ return ERR_OUT_OF_MEMORY;
+
+ out_queue.push_back(p_arr);
+ return OK;
+}
+
+int RemoteDebuggerPeerTCP::get_max_message_size() const {
+ return 8 << 20; // 8 MiB
+}
+
+void RemoteDebuggerPeerTCP::close() {
+ if (thread) {
+ running = false;
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+ thread = NULL;
+ }
+ tcp_client->disconnect_from_host();
+ out_buf.resize(0);
+ in_buf.resize(0);
+}
+
+RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
+ // This means remote debugger takes 16 MiB just because it exists...
+ in_buf.resize((8 << 20) + 4); // 8 MiB should be way more than enough (need 4 extra bytes for encoding packet size).
+ out_buf.resize(8 << 20); // 8 MiB should be way more than enough
+ tcp_client = p_tcp;
+ if (tcp_client.is_valid()) { // Attaching to an already connected stream.
+ connected = true;
+#ifndef NO_THREADS
+ running = true;
+ thread = Thread::create(_thread_func, this);
+#endif
+ } else {
+ tcp_client.instance();
+ }
+}
+
+RemoteDebuggerPeerTCP::~RemoteDebuggerPeerTCP() {
+ close();
+}
+
+void RemoteDebuggerPeerTCP::_write_out() {
+ while (tcp_client->poll(NetSocket::POLL_TYPE_OUT) == OK) {
+ uint8_t *buf = out_buf.ptrw();
+ if (out_left <= 0) {
+ if (out_queue.size() == 0)
+ break; // Nothing left to send
+ mutex.lock();
+ Variant var = out_queue[0];
+ out_queue.pop_front();
+ mutex.unlock();
+ int size = 0;
+ Error err = encode_variant(var, NULL, size);
+ ERR_CONTINUE(err != OK || size > out_buf.size() - 4); // 4 bytes separator.
+ encode_uint32(size, buf);
+ encode_variant(var, buf + 4, size);
+ out_left = size + 4;
+ out_pos = 0;
+ }
+ int sent = 0;
+ tcp_client->put_partial_data(buf + out_pos, out_left, sent);
+ out_left -= sent;
+ out_pos += sent;
+ }
+}
+
+void RemoteDebuggerPeerTCP::_read_in() {
+ while (tcp_client->poll(NetSocket::POLL_TYPE_IN) == OK) {
+ uint8_t *buf = in_buf.ptrw();
+ if (in_left <= 0) {
+ if (in_queue.size() > max_queued_messages) {
+ break; // Too many messages already in queue.
+ }
+ if (tcp_client->get_available_bytes() < 4) {
+ break; // Need 4 more bytes.
+ }
+ uint32_t size = 0;
+ int read = 0;
+ Error err = tcp_client->get_partial_data((uint8_t *)&size, 4, read);
+ ERR_CONTINUE(read != 4 || err != OK || size > (uint32_t)in_buf.size());
+ in_left = size;
+ in_pos = 0;
+ }
+ int read = 0;
+ tcp_client->get_partial_data(buf + in_pos, in_left, read);
+ in_left -= read;
+ in_pos += read;
+ if (in_left == 0) {
+ Variant var;
+ Error err = decode_variant(var, buf, in_pos, &read);
+ ERR_CONTINUE(read != in_pos || err != OK);
+ ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
+ mutex.lock();
+ in_queue.push_back(var);
+ mutex.unlock();
+ }
+ }
+}
+
+Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_port) {
+
+ IP_Address ip;
+ if (p_host.is_valid_ip_address())
+ ip = p_host;
+ else
+ ip = IP::get_singleton()->resolve_hostname(p_host);
+
+ int port = p_port;
+
+ const int tries = 6;
+ int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
+
+ tcp_client->connect_to_host(ip, port);
+
+ for (int i = 0; i < tries; i++) {
+
+ if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
+ print_verbose("Remote Debugger: Connected!");
+ break;
+ } else {
+
+ const int ms = waits[i];
+ OS::get_singleton()->delay_usec(ms * 1000);
+ print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
+ };
+ };
+
+ if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+
+ ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
+ return FAILED;
+ };
+ connected = true;
+#ifndef NO_THREADS
+ running = true;
+ thread = Thread::create(_thread_func, this);
+#endif
+ return OK;
+}
+
+void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) {
+ RemoteDebuggerPeerTCP *peer = (RemoteDebuggerPeerTCP *)p_ud;
+ while (peer->running && peer->is_peer_connected()) {
+ peer->_poll();
+ if (!peer->is_peer_connected())
+ break;
+ peer->tcp_client->poll(NetSocket::POLL_TYPE_IN_OUT, 1);
+ }
+}
+
+void RemoteDebuggerPeerTCP::poll() {
+#ifdef NO_THREADS
+ _poll();
+#endif
+}
+
+void RemoteDebuggerPeerTCP::_poll() {
+ if (connected) {
+ _write_out();
+ _read_in();
+ connected = tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
+ }
+}
+
+Ref<RemoteDebuggerPeer> RemoteDebuggerPeer::create_from_uri(const String p_uri) {
+ if (!p_uri.begins_with("tcp://"))
+ return Ref<RemoteDebuggerPeer>(); // Only TCP supported for now, more to come.
+
+ String debug_host = p_uri.replace("tcp://", "");
+ uint16_t debug_port = 6007;
+
+ if (debug_host.find(":") != -1) {
+ int sep_pos = debug_host.find_last(":");
+ debug_port = debug_host.substr(sep_pos + 1).to_int();
+ debug_host = debug_host.substr(0, sep_pos);
+ }
+ Ref<RemoteDebuggerPeerTCP> peer = Ref<RemoteDebuggerPeer>(memnew(RemoteDebuggerPeerTCP));
+ Error err = peer->connect_to_host(debug_host, debug_port);
+ if (err != OK)
+ return Ref<RemoteDebuggerPeer>();
+ return peer;
+}
+
+RemoteDebuggerPeer::RemoteDebuggerPeer() {
+ max_queued_messages = (int)GLOBAL_GET("network/limits/debugger/max_queued_messages");
+}
diff --git a/core/debugger/remote_debugger_peer.h b/core/debugger/remote_debugger_peer.h
new file mode 100644
index 0000000000..6fc3214a51
--- /dev/null
+++ b/core/debugger/remote_debugger_peer.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* remote_debugger_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REMOTE_DEBUGGER_PEER_H
+#define REMOTE_DEBUGGER_PEER_H
+
+#include "core/io/stream_peer_tcp.h"
+#include "core/os/mutex.h"
+#include "core/os/thread.h"
+#include "core/reference.h"
+#include "core/ustring.h"
+
+class RemoteDebuggerPeer : public Reference {
+protected:
+ int max_queued_messages = 4096;
+
+public:
+ static Ref<RemoteDebuggerPeer> create_from_uri(const String p_uri);
+ virtual bool is_peer_connected() = 0;
+ virtual bool has_message() = 0;
+ virtual Error put_message(const Array &p_arr) = 0;
+ virtual Array get_message() = 0;
+ virtual void close() = 0;
+ virtual void poll() = 0;
+ virtual int get_max_message_size() const = 0;
+
+ RemoteDebuggerPeer();
+};
+
+class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer {
+private:
+ Ref<StreamPeerTCP> tcp_client;
+ Mutex mutex;
+ Thread *thread = NULL;
+ List<Array> in_queue;
+ List<Array> out_queue;
+ int out_left = 0;
+ int out_pos = 0;
+ Vector<uint8_t> out_buf;
+ int in_left = 0;
+ int in_pos = 0;
+ Vector<uint8_t> in_buf;
+ bool connected = false;
+ bool running = false;
+
+ static void _thread_func(void *p_ud);
+
+ void _poll();
+ void _write_out();
+ void _read_in();
+
+public:
+ Error connect_to_host(const String &p_host, uint16_t p_port);
+
+ void poll();
+ bool is_peer_connected();
+ bool has_message();
+ Array get_message();
+ Error put_message(const Array &p_arr);
+ int get_max_message_size() const;
+ void close();
+
+ RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>());
+ ~RemoteDebuggerPeerTCP();
+};
+
+#endif // REMOTE_DEBUGGER_PEER_H
diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp
new file mode 100644
index 0000000000..935ad01d80
--- /dev/null
+++ b/core/debugger/script_debugger.cpp
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* script_debugger.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "script_debugger.h"
+
+#include "core/debugger/engine_debugger.h"
+
+void ScriptDebugger::set_lines_left(int p_left) {
+
+ lines_left = p_left;
+}
+
+int ScriptDebugger::get_lines_left() const {
+
+ return lines_left;
+}
+
+void ScriptDebugger::set_depth(int p_depth) {
+
+ depth = p_depth;
+}
+
+int ScriptDebugger::get_depth() const {
+
+ return depth;
+}
+
+void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) {
+
+ if (!breakpoints.has(p_line))
+ breakpoints[p_line] = Set<StringName>();
+ breakpoints[p_line].insert(p_source);
+}
+
+void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) {
+
+ if (!breakpoints.has(p_line))
+ return;
+
+ breakpoints[p_line].erase(p_source);
+ if (breakpoints[p_line].size() == 0)
+ breakpoints.erase(p_line);
+}
+bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const {
+
+ if (!breakpoints.has(p_line))
+ return false;
+ return breakpoints[p_line].has(p_source);
+}
+bool ScriptDebugger::is_breakpoint_line(int p_line) const {
+
+ return breakpoints.has(p_line);
+}
+
+String ScriptDebugger::breakpoint_find_source(const String &p_source) const {
+
+ return p_source;
+}
+
+void ScriptDebugger::clear_breakpoints() {
+
+ breakpoints.clear();
+}
+
+void ScriptDebugger::set_skip_breakpoints(bool p_skip_breakpoints) {
+
+ skip_breakpoints = p_skip_breakpoints;
+}
+
+bool ScriptDebugger::is_skipping_breakpoints() {
+
+ return skip_breakpoints;
+}
+
+void ScriptDebugger::debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_is_error_breakpoint) {
+ ScriptLanguage *prev = break_lang;
+ break_lang = p_lang;
+ EngineDebugger::get_singleton()->debug(p_can_continue, p_is_error_breakpoint);
+ break_lang = prev;
+}
+
+void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) {
+ // Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.
+ error_stack_info.append_array(p_stack_info);
+ EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_type);
+ error_stack_info.resize(0);
+}
+
+Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const {
+ return error_stack_info;
+}
+
+ScriptLanguage *ScriptDebugger::get_break_language() const {
+
+ return break_lang;
+}
diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h
new file mode 100644
index 0000000000..d8ddf353bf
--- /dev/null
+++ b/core/debugger/script_debugger.h
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* script_debugger.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCRIPT_DEBUGGER_H
+#define SCRIPT_DEBUGGER_H
+
+#include "core/map.h"
+#include "core/script_language.h"
+#include "core/set.h"
+#include "core/string_name.h"
+#include "core/vector.h"
+
+class ScriptDebugger {
+
+ typedef ScriptLanguage::StackInfo StackInfo;
+
+ int lines_left = -1;
+ int depth = -1;
+ bool skip_breakpoints = false;
+
+ Map<int, Set<StringName> > breakpoints;
+
+ ScriptLanguage *break_lang = NULL;
+ Vector<StackInfo> error_stack_info;
+
+public:
+ void set_lines_left(int p_left);
+ int get_lines_left() const;
+
+ void set_depth(int p_depth);
+ int get_depth() const;
+
+ String breakpoint_find_source(const String &p_source) const;
+ void set_break_language(ScriptLanguage *p_lang) { break_lang = p_lang; }
+ ScriptLanguage *get_break_language() { return break_lang; }
+ void set_skip_breakpoints(bool p_skip_breakpoints);
+ bool is_skipping_breakpoints();
+ void insert_breakpoint(int p_line, const StringName &p_source);
+ void remove_breakpoint(int p_line, const StringName &p_source);
+ bool is_breakpoint(int p_line, const StringName &p_source) const;
+ bool is_breakpoint_line(int p_line) const;
+ void clear_breakpoints();
+ const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; }
+
+ void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false);
+ ScriptLanguage *get_break_language() const;
+
+ void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info);
+ Vector<StackInfo> get_error_stack_info() const;
+ ScriptDebugger() {}
+};
+
+#endif
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index d7c82fddd9..4b864f0dd7 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -30,6 +30,7 @@
#include "multiplayer_api.h"
+#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
#include "scene/main/node.h"
#include <stdint.h>
@@ -172,6 +173,28 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
return network_peer;
}
+#ifdef DEBUG_ENABLED
+void _profile_node_data(const String &p_what, ObjectID p_id) {
+ if (EngineDebugger::is_profiling("multiplayer")) {
+ Array values;
+ values.push_back("node");
+ values.push_back(p_id);
+ values.push_back(p_what);
+ EngineDebugger::profiler_add_frame_data("multiplayer", values);
+ }
+}
+void _profile_bandwidth_data(const String &p_inout, int p_size) {
+ if (EngineDebugger::is_profiling("multiplayer")) {
+ Array values;
+ values.push_back("bandwidth");
+ values.push_back(p_inout);
+ values.push_back(OS::get_singleton()->get_ticks_msec());
+ values.push_back(p_size);
+ EngineDebugger::profiler_add_frame_data("multiplayer", values);
+ }
+}
+#endif
+
// Returns the packet size stripping the node path added when the node is not yet cached.
int get_packet_len(uint32_t p_node_target, int p_packet_len) {
if (p_node_target & 0x80000000) {
@@ -188,11 +211,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
#ifdef DEBUG_ENABLED
- if (profiling) {
- bandwidth_incoming_data.write[bandwidth_incoming_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
- bandwidth_incoming_data.write[bandwidth_incoming_pointer].packet_size = p_packet_len;
- bandwidth_incoming_pointer = (bandwidth_incoming_pointer + 1) % bandwidth_incoming_data.size();
- }
+ _profile_bandwidth_data("in", p_packet_len);
#endif
// Extract the `packet_type` from the LSB three bits:
@@ -381,11 +400,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id,
argp.resize(argc);
#ifdef DEBUG_ENABLED
- if (profiling) {
- ObjectID id = p_node->get_instance_id();
- _init_node_profile(id);
- profiler_frame_data[id].incoming_rpc += 1;
- }
+ _profile_node_data("in_rpc", p_node->get_instance_id());
#endif
if (byte_only) {
@@ -437,11 +452,7 @@ void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_i
ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
#ifdef DEBUG_ENABLED
- if (profiling) {
- ObjectID id = p_node->get_instance_id();
- _init_node_profile(id);
- profiler_frame_data[id].incoming_rset += 1;
- }
+ _profile_node_data("in_rset", p_node->get_instance_id());
#endif
Variant value;
@@ -912,11 +923,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT);
#ifdef DEBUG_ENABLED
- if (profiling) {
- bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
- bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].packet_size = ofs;
- bandwidth_outgoing_pointer = (bandwidth_outgoing_pointer + 1) % bandwidth_outgoing_data.size();
- }
+ _profile_bandwidth_data("out", ofs);
#endif
// Take chance and set transfer mode, since all send methods will use it.
@@ -1031,11 +1038,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
if (!skip_rpc) {
#ifdef DEBUG_ENABLED
- if (profiling) {
- ObjectID id = p_node->get_instance_id();
- _init_node_profile(id);
- profiler_frame_data[id].outgoing_rpc += 1;
- }
+ _profile_node_data("out_rpc", p_node->get_instance_id());
#endif
_send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
@@ -1130,11 +1133,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
}
#ifdef DEBUG_ENABLED
- if (profiling) {
- ObjectID id = p_node->get_instance_id();
- _init_node_profile(id);
- profiler_frame_data[id].outgoing_rset += 1;
- }
+ _profile_node_data("out_rset", p_node->get_instance_id());
#endif
const Variant *vptr = &p_value;
@@ -1220,95 +1219,6 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
return allow_object_decoding;
}
-void MultiplayerAPI::profiling_start() {
-#ifdef DEBUG_ENABLED
- profiling = true;
- profiler_frame_data.clear();
-
- bandwidth_incoming_pointer = 0;
- bandwidth_incoming_data.resize(16384); // ~128kB
- for (int i = 0; i < bandwidth_incoming_data.size(); ++i) {
- bandwidth_incoming_data.write[i].packet_size = -1;
- }
-
- bandwidth_outgoing_pointer = 0;
- bandwidth_outgoing_data.resize(16384); // ~128kB
- for (int i = 0; i < bandwidth_outgoing_data.size(); ++i) {
- bandwidth_outgoing_data.write[i].packet_size = -1;
- }
-#endif
-}
-
-void MultiplayerAPI::profiling_end() {
-#ifdef DEBUG_ENABLED
- profiling = false;
- bandwidth_incoming_data.clear();
- bandwidth_outgoing_data.clear();
-#endif
-}
-
-int MultiplayerAPI::get_profiling_frame(ProfilingInfo *r_info) {
- int i = 0;
-#ifdef DEBUG_ENABLED
- for (Map<ObjectID, ProfilingInfo>::Element *E = profiler_frame_data.front(); E; E = E->next()) {
- r_info[i] = E->get();
- ++i;
- }
- profiler_frame_data.clear();
-#endif
- return i;
-}
-
-int MultiplayerAPI::get_incoming_bandwidth_usage() {
-#ifdef DEBUG_ENABLED
- return _get_bandwidth_usage(bandwidth_incoming_data, bandwidth_incoming_pointer);
-#else
- return 0;
-#endif
-}
-
-int MultiplayerAPI::get_outgoing_bandwidth_usage() {
-#ifdef DEBUG_ENABLED
- return _get_bandwidth_usage(bandwidth_outgoing_data, bandwidth_outgoing_pointer);
-#else
- return 0;
-#endif
-}
-
-#ifdef DEBUG_ENABLED
-int MultiplayerAPI::_get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
- int total_bandwidth = 0;
-
- uint32_t timestamp = OS::get_singleton()->get_ticks_msec();
- uint32_t final_timestamp = timestamp - 1000;
-
- int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
-
- while (i != p_pointer && p_buffer[i].packet_size > 0) {
- if (p_buffer[i].timestamp < final_timestamp) {
- return total_bandwidth;
- }
- total_bandwidth += p_buffer[i].packet_size;
- i = (i + p_buffer.size() - 1) % p_buffer.size();
- }
-
- ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
- return total_bandwidth;
-}
-
-void MultiplayerAPI::_init_node_profile(ObjectID p_node) {
- if (profiler_frame_data.has(p_node))
- return;
- profiler_frame_data.insert(p_node, ProfilingInfo());
- profiler_frame_data[p_node].node = p_node;
- profiler_frame_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
- profiler_frame_data[p_node].incoming_rpc = 0;
- profiler_frame_data[p_node].incoming_rset = 0;
- profiler_frame_data[p_node].outgoing_rpc = 0;
- profiler_frame_data[p_node].outgoing_rset = 0;
-}
-#endif
-
void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE));
@@ -1352,9 +1262,6 @@ MultiplayerAPI::MultiplayerAPI() :
allow_object_decoding(false) {
rpc_sender_id = 0;
root_node = NULL;
-#ifdef DEBUG_ENABLED
- profiling = false;
-#endif
clear();
}
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
index a706a0e450..52f918aefa 100644
--- a/core/io/multiplayer_api.h
+++ b/core/io/multiplayer_api.h
@@ -38,16 +38,6 @@ class MultiplayerAPI : public Reference {
GDCLASS(MultiplayerAPI, Reference);
-public:
- struct ProfilingInfo {
- ObjectID node;
- String node_path;
- int incoming_rpc;
- int incoming_rset;
- int outgoing_rpc;
- int outgoing_rset;
- };
-
private:
//path sent caches
struct PathSentCache {
@@ -65,23 +55,6 @@ private:
Map<int, NodeInfo> nodes;
};
-#ifdef DEBUG_ENABLED
- struct BandwidthFrame {
- uint32_t timestamp;
- int packet_size;
- };
-
- int bandwidth_incoming_pointer;
- Vector<BandwidthFrame> bandwidth_incoming_data;
- int bandwidth_outgoing_pointer;
- Vector<BandwidthFrame> bandwidth_outgoing_data;
- Map<ObjectID, ProfilingInfo> profiler_frame_data;
- bool profiling;
-
- void _init_node_profile(ObjectID p_node);
- int _get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer);
-#endif
-
Ref<NetworkedMultiplayerPeer> network_peer;
int rpc_sender_id;
Set<int> connected_peers;
@@ -169,13 +142,6 @@ public:
void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const;
- void profiling_start();
- void profiling_end();
-
- int get_profiling_frame(ProfilingInfo *r_info);
- int get_incoming_bandwidth_usage();
- int get_outgoing_bandwidth_usage();
-
MultiplayerAPI();
~MultiplayerAPI();
};
diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp
index 044431743a..f0c5816d73 100644
--- a/core/io/stream_peer_tcp.cpp
+++ b/core/io/stream_peer_tcp.cpp
@@ -288,6 +288,11 @@ void StreamPeerTCP::disconnect_from_host() {
peer_port = 0;
}
+Error StreamPeerTCP::poll(NetSocket::PollType p_type, int timeout) {
+ ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
+ return _sock->poll(p_type, timeout);
+}
+
Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
int total;
diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h
index f16d4a2bd4..327aa3cc3b 100644
--- a/core/io/stream_peer_tcp.h
+++ b/core/io/stream_peer_tcp.h
@@ -78,6 +78,9 @@ public:
void set_no_delay(bool p_enabled);
+ // Poll functions (wait or check for writable, readable)
+ Error poll(NetSocket::PollType p_type, int timeout = 0);
+
// Read/Write from StreamPeer
Error put_data(const uint8_t *p_data, int p_bytes);
Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
deleted file mode 100644
index 67375da6e2..0000000000
--- a/core/script_debugger_remote.cpp
+++ /dev/null
@@ -1,1143 +0,0 @@
-/*************************************************************************/
-/* script_debugger_remote.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "script_debugger_remote.h"
-
-#include "core/engine.h"
-#include "core/io/ip.h"
-#include "core/io/marshalls.h"
-#include "core/os/input.h"
-#include "core/os/os.h"
-#include "core/project_settings.h"
-#include "servers/visual_server.h"
-
-#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
-#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too short. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
-
-Array ScriptDebuggerRemote::ScriptStackDump::serialize() {
- Array arr;
- arr.push_back(frames.size() * 3);
- for (int i = 0; i < frames.size(); i++) {
- arr.push_back(frames[i].file);
- arr.push_back(frames[i].line);
- arr.push_back(frames[i].func);
- }
- return arr;
-}
-
-bool ScriptDebuggerRemote::ScriptStackDump::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 1, "ScriptStackDump");
- uint32_t size = p_arr[0];
- CHECK_SIZE(p_arr, size, "ScriptStackDump");
- int idx = 1;
- for (uint32_t i = 0; i < size / 3; i++) {
- ScriptLanguage::StackInfo sf;
- sf.file = p_arr[idx];
- sf.line = p_arr[idx + 1];
- sf.func = p_arr[idx + 2];
- frames.push_back(sf);
- idx += 3;
- }
- CHECK_END(p_arr, idx, "ScriptStackDump");
- return true;
-}
-
-Array ScriptDebuggerRemote::ScriptStackVariable::serialize(int max_size) {
- Array arr;
- arr.push_back(name);
- arr.push_back(type);
-
- Variant var = value;
- if (value.get_type() == Variant::OBJECT && value.get_validated_object() == nullptr) {
- var = Variant();
- }
-
- int len = 0;
- Error err = encode_variant(var, NULL, len, true);
- if (err != OK)
- ERR_PRINT("Failed to encode variant.");
-
- if (len > max_size) {
- arr.push_back(Variant());
- } else {
- arr.push_back(var);
- }
- return arr;
-}
-
-bool ScriptDebuggerRemote::ScriptStackVariable::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 3, "ScriptStackVariable");
- name = p_arr[0];
- type = p_arr[1];
- value = p_arr[2];
- CHECK_END(p_arr, 3, "ScriptStackVariable");
- return true;
-}
-
-Array ScriptDebuggerRemote::OutputError::serialize() {
- Array arr;
- arr.push_back(hr);
- arr.push_back(min);
- arr.push_back(sec);
- arr.push_back(msec);
- arr.push_back(source_file);
- arr.push_back(source_func);
- arr.push_back(source_line);
- arr.push_back(error);
- arr.push_back(error_descr);
- arr.push_back(warning);
- unsigned int size = callstack.size();
- const ScriptLanguage::StackInfo *r = callstack.ptr();
- arr.push_back(size * 3);
- for (int i = 0; i < callstack.size(); i++) {
- arr.push_back(r[i].file);
- arr.push_back(r[i].func);
- arr.push_back(r[i].line);
- }
- return arr;
-}
-
-bool ScriptDebuggerRemote::OutputError::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 11, "OutputError");
- hr = p_arr[0];
- min = p_arr[1];
- sec = p_arr[2];
- msec = p_arr[3];
- source_file = p_arr[4];
- source_func = p_arr[5];
- source_line = p_arr[6];
- error = p_arr[7];
- error_descr = p_arr[8];
- warning = p_arr[9];
- unsigned int stack_size = p_arr[10];
- CHECK_SIZE(p_arr, stack_size, "OutputError");
- int idx = 11;
- callstack.resize(stack_size / 3);
- ScriptLanguage::StackInfo *w = callstack.ptrw();
- for (unsigned int i = 0; i < stack_size / 3; i++) {
- w[i].file = p_arr[idx];
- w[i].func = p_arr[idx + 1];
- w[i].line = p_arr[idx + 2];
- idx += 3;
- }
- CHECK_END(p_arr, idx, "OutputError");
- return true;
-}
-
-Array ScriptDebuggerRemote::ResourceUsage::serialize() {
- infos.sort();
-
- Array arr;
- arr.push_back(infos.size() * 4);
- for (List<ResourceInfo>::Element *E = infos.front(); E; E = E->next()) {
- arr.push_back(E->get().path);
- arr.push_back(E->get().format);
- arr.push_back(E->get().type);
- arr.push_back(E->get().vram);
- }
- return arr;
-}
-
-bool ScriptDebuggerRemote::ResourceUsage::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 1, "ResourceUsage");
- uint32_t size = p_arr[0];
- CHECK_SIZE(p_arr, size, "ResourceUsage");
- int idx = 1;
- for (uint32_t i = 0; i < size / 4; i++) {
- ResourceInfo info;
- info.path = p_arr[idx];
- info.format = p_arr[idx + 1];
- info.type = p_arr[idx + 2];
- info.vram = p_arr[idx + 3];
- infos.push_back(info);
- }
- CHECK_END(p_arr, idx, "ResourceUsage");
- return true;
-}
-
-Array ScriptDebuggerRemote::ProfilerSignature::serialize() {
- Array arr;
- arr.push_back(name);
- arr.push_back(id);
- return arr;
-}
-
-bool ScriptDebuggerRemote::ProfilerSignature::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 2, "ProfilerSignature");
- name = p_arr[0];
- id = p_arr[1];
- CHECK_END(p_arr, 2, "ProfilerSignature");
- return true;
-}
-
-Array ScriptDebuggerRemote::ProfilerFrame::serialize() {
- Array arr;
- arr.push_back(frame_number);
- arr.push_back(frame_time);
- arr.push_back(idle_time);
- arr.push_back(physics_time);
- arr.push_back(physics_frame_time);
- arr.push_back(USEC_TO_SEC(script_time));
-
- arr.push_back(frames_data.size());
- arr.push_back(frame_functions.size() * 4);
-
- // Servers profiling info.
- for (int i = 0; i < frames_data.size(); i++) {
- arr.push_back(frames_data[i].name); // Type (physics/process/audio/...)
- arr.push_back(frames_data[i].data.size());
- for (int j = 0; j < frames_data[i].data.size() / 2; j++) {
- arr.push_back(frames_data[i].data[2 * j]); // NAME
- arr.push_back(frames_data[i].data[2 * j + 1]); // TIME
- }
- }
- for (int i = 0; i < frame_functions.size(); i++) {
- arr.push_back(frame_functions[i].sig_id);
- arr.push_back(frame_functions[i].call_count);
- arr.push_back(frame_functions[i].self_time);
- arr.push_back(frame_functions[i].total_time);
- }
- return arr;
-}
-
-bool ScriptDebuggerRemote::ProfilerFrame::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 8, "ProfilerFrame");
- frame_number = p_arr[0];
- frame_time = p_arr[1];
- idle_time = p_arr[2];
- physics_time = p_arr[3];
- physics_frame_time = p_arr[4];
- script_time = p_arr[5];
- uint32_t frame_data_size = p_arr[6];
- int frame_func_size = p_arr[7];
- int idx = 8;
- while (frame_data_size) {
- CHECK_SIZE(p_arr, idx + 2, "ProfilerFrame");
- frame_data_size--;
- FrameData fd;
- fd.name = p_arr[idx];
- int sub_data_size = p_arr[idx + 1];
- idx += 2;
- CHECK_SIZE(p_arr, idx + sub_data_size, "ProfilerFrame");
- for (int j = 0; j < sub_data_size / 2; j++) {
- fd.data.push_back(p_arr[idx]); // NAME
- fd.data.push_back(p_arr[idx + 1]); // TIME
- idx += 2;
- }
- frames_data.push_back(fd);
- }
- CHECK_SIZE(p_arr, idx + frame_func_size, "ProfilerFrame");
- for (int i = 0; i < frame_func_size / 4; i++) {
- FrameFunction ff;
- ff.sig_id = p_arr[idx];
- ff.call_count = p_arr[idx + 1];
- ff.self_time = p_arr[idx + 2];
- ff.total_time = p_arr[idx + 3];
- frame_functions.push_back(ff);
- idx += 4;
- }
- CHECK_END(p_arr, idx, "ProfilerFrame");
- return true;
-}
-
-Array ScriptDebuggerRemote::NetworkProfilerFrame::serialize() {
- Array arr;
- arr.push_back(infos.size() * 6);
- for (int i = 0; i < infos.size(); ++i) {
- arr.push_back(uint64_t(infos[i].node));
- arr.push_back(infos[i].node_path);
- arr.push_back(infos[i].incoming_rpc);
- arr.push_back(infos[i].incoming_rset);
- arr.push_back(infos[i].outgoing_rpc);
- arr.push_back(infos[i].outgoing_rset);
- }
- return arr;
-}
-
-bool ScriptDebuggerRemote::NetworkProfilerFrame::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 1, "NetworkProfilerFrame");
- uint32_t size = p_arr[0];
- CHECK_SIZE(p_arr, size, "NetworkProfilerFrame");
- infos.resize(size);
- int idx = 1;
- for (uint32_t i = 0; i < size / 6; ++i) {
- infos.write[i].node = uint64_t(p_arr[idx]);
- infos.write[i].node_path = p_arr[idx + 1];
- infos.write[i].incoming_rpc = p_arr[idx + 2];
- infos.write[i].incoming_rset = p_arr[idx + 3];
- infos.write[i].outgoing_rpc = p_arr[idx + 4];
- infos.write[i].outgoing_rset = p_arr[idx + 5];
- }
- CHECK_END(p_arr, idx, "NetworkProfilerFrame");
- return true;
-}
-
-void ScriptDebuggerRemote::_put_msg(String p_message, Array p_data) {
- Array msg;
- msg.push_back(p_message);
- msg.push_back(p_data);
- packet_peer_stream->put_var(msg);
-}
-
-bool ScriptDebuggerRemote::is_peer_connected() {
- return tcp_client->is_connected_to_host() && tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
-}
-
-void ScriptDebuggerRemote::_send_video_memory() {
-
- ResourceUsage usage;
- if (resource_usage_func)
- resource_usage_func(&usage);
-
- _put_msg("message:video_mem", usage.serialize());
-}
-
-Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) {
-
- IP_Address ip;
- if (p_host.is_valid_ip_address())
- ip = p_host;
- else
- ip = IP::get_singleton()->resolve_hostname(p_host);
-
- int port = p_port;
-
- const int tries = 6;
- int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
-
- tcp_client->connect_to_host(ip, port);
-
- for (int i = 0; i < tries; i++) {
-
- if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
- print_verbose("Remote Debugger: Connected!");
- break;
- } else {
-
- const int ms = waits[i];
- OS::get_singleton()->delay_usec(ms * 1000);
- print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
- };
- };
-
- if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
-
- ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
- return FAILED;
- };
-
- packet_peer_stream->set_stream_peer(tcp_client);
- Array msg;
- msg.push_back(OS::get_singleton()->get_process_id());
- send_message("set_pid", msg);
-
- return OK;
-}
-
-void ScriptDebuggerRemote::_parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script) {
-
- if (p_command == "request_video_mem") {
- _send_video_memory();
-
- } else if (p_command == "start_profiling") {
- ERR_FAIL_COND(p_data.size() < 1);
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->profiling_start();
- }
-
- max_frame_functions = p_data[0];
- profiler_function_signature_map.clear();
- profiling = true;
- frame_time = 0;
- idle_time = 0;
- physics_time = 0;
- physics_frame_time = 0;
- print_line("PROFILING ALRIGHT!");
-
- } else if (p_command == "stop_profiling") {
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->profiling_stop();
- }
- profiling = false;
- _send_profiling_data(false);
- print_line("PROFILING END!");
-
- } else if (p_command == "start_visual_profiling") {
-
- visual_profiling = true;
- VS::get_singleton()->set_frame_profiling_enabled(true);
- } else if (p_command == "stop_visual_profiling") {
-
- visual_profiling = false;
- VS::get_singleton()->set_frame_profiling_enabled(false);
-
- } else if (p_command == "start_network_profiling") {
-
- network_profiling = true;
- multiplayer->profiling_start();
-
- } else if (p_command == "stop_network_profiling") {
-
- network_profiling = false;
- multiplayer->profiling_end();
-
- } else if (p_command == "reload_scripts") {
- reload_all_scripts = true;
-
- } else if (p_command == "breakpoint") {
- ERR_FAIL_COND(p_data.size() < 3);
- bool set = p_data[2];
- if (set)
- insert_breakpoint(p_data[1], p_data[0]);
- else
- remove_breakpoint(p_data[1], p_data[0]);
-
- } else if (p_command == "set_skip_breakpoints") {
- ERR_FAIL_COND(p_data.size() < 1);
- skip_breakpoints = p_data[0];
-
- } else if (p_command == "get_stack_dump") {
- ERR_FAIL_COND(!p_script);
- ScriptStackDump dump;
- int slc = p_script->debug_get_stack_level_count();
- for (int i = 0; i < slc; i++) {
- ScriptLanguage::StackInfo frame;
- frame.file = p_script->debug_get_stack_level_source(i);
- frame.line = p_script->debug_get_stack_level_line(i);
- frame.func = p_script->debug_get_stack_level_function(i);
- dump.frames.push_back(frame);
- }
- _put_msg("stack_dump", dump.serialize());
-
- } else if (p_command == "get_stack_frame_vars") {
- ERR_FAIL_COND(p_data.size() != 1);
- ERR_FAIL_COND(!p_script);
- int lv = p_data[0];
-
- List<String> members;
- List<Variant> member_vals;
- if (ScriptInstance *inst = p_script->debug_get_stack_level_instance(lv)) {
- members.push_back("self");
- member_vals.push_back(inst->get_owner());
- }
- p_script->debug_get_stack_level_members(lv, &members, &member_vals);
- ERR_FAIL_COND(members.size() != member_vals.size());
-
- List<String> locals;
- List<Variant> local_vals;
- p_script->debug_get_stack_level_locals(lv, &locals, &local_vals);
- ERR_FAIL_COND(locals.size() != local_vals.size());
-
- List<String> globals;
- List<Variant> globals_vals;
- p_script->debug_get_globals(&globals, &globals_vals);
- ERR_FAIL_COND(globals.size() != globals_vals.size());
-
- _put_msg("stack_frame_vars", Array());
-
- ScriptStackVariable stvar;
- { //locals
- List<String>::Element *E = locals.front();
- List<Variant>::Element *F = local_vals.front();
- while (E) {
- stvar.name = E->get();
- stvar.value = F->get();
- stvar.type = 0;
- _put_msg("stack_frame_var", stvar.serialize());
-
- E = E->next();
- F = F->next();
- }
- }
-
- { //members
- List<String>::Element *E = members.front();
- List<Variant>::Element *F = member_vals.front();
- while (E) {
- stvar.name = E->get();
- stvar.value = F->get();
- stvar.type = 1;
- _put_msg("stack_frame_var", stvar.serialize());
-
- E = E->next();
- F = F->next();
- }
- }
-
- { //globals
- List<String>::Element *E = globals.front();
- List<Variant>::Element *F = globals_vals.front();
- while (E) {
- stvar.name = E->get();
- stvar.value = F->get();
- stvar.type = 2;
- _put_msg("stack_frame_var", stvar.serialize());
-
- E = E->next();
- F = F->next();
- }
- }
-
- } else {
- if (scene_tree_parse_func) {
- scene_tree_parse_func(p_command, p_data);
- }
- // Unknown message...
- }
-}
-
-void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
-
- //this function is called when there is a debugger break (bug on script)
- //or when execution is paused from editor
-
- if (skip_breakpoints && !p_is_error_breakpoint)
- return;
-
- ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
-
- Array msg;
- msg.push_back(p_can_continue);
- msg.push_back(p_script->debug_get_error());
- _put_msg("debug_enter", msg);
-
- skip_profile_frame = true; // to avoid super long frame time for the frame
-
- Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
- if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
-
- uint64_t loop_begin_usec = 0;
- uint64_t loop_time_sec = 0;
- while (true) {
- loop_begin_usec = OS::get_singleton()->get_ticks_usec();
-
- _get_output();
-
- if (packet_peer_stream->get_available_packet_count() > 0) {
-
- Variant var;
- Error err = packet_peer_stream->get_var(var);
-
- ERR_CONTINUE(err != OK);
- ERR_CONTINUE(var.get_type() != Variant::ARRAY);
-
- Array cmd = var;
-
- ERR_CONTINUE(cmd.size() != 2);
- ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
- ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
-
- String command = cmd[0];
- Array data = cmd[1];
- if (command == "step") {
-
- set_depth(-1);
- set_lines_left(1);
- break;
- } else if (command == "next") {
-
- set_depth(0);
- set_lines_left(1);
- break;
-
- } else if (command == "continue") {
- set_depth(-1);
- set_lines_left(-1);
- OS::get_singleton()->move_window_to_foreground();
- break;
- } else if (command == "break") {
- ERR_PRINT("Got break when already broke!");
- break;
- }
-
- _parse_message(command, data, p_script);
- } else {
- OS::get_singleton()->delay_usec(10000);
- OS::get_singleton()->process_and_drop_events();
- }
-
- // This is for the camera override to stay live even when the game is paused from the editor
- loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f;
- VisualServer::get_singleton()->sync();
- if (VisualServer::get_singleton()->has_changed()) {
- VisualServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale());
- }
- }
-
- _put_msg("debug_exit", Array());
-
- if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
- Input::get_singleton()->set_mouse_mode(mouse_mode);
-}
-
-void ScriptDebuggerRemote::_get_output() {
-
- MutexLock lock(mutex);
-
- if (output_strings.size()) {
-
- locking = true;
-
- while (output_strings.size()) {
-
- Array arr;
- arr.push_back(output_strings.front()->get());
- _put_msg("output", arr);
- output_strings.pop_front();
- }
- locking = false;
- }
-
- if (n_messages_dropped > 0) {
- Message msg;
- msg.message = "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped.";
- messages.push_back(msg);
- n_messages_dropped = 0;
- }
-
- while (messages.size()) {
- locking = true;
- Message msg = messages.front()->get();
- _put_msg("message:" + msg.message, msg.data);
- messages.pop_front();
- locking = false;
- }
-
- if (n_errors_dropped == 1) {
- // Only print one message about dropping per second
- OutputError oe;
- oe.error = "TOO_MANY_ERRORS";
- oe.error_descr = "Too many errors! Ignoring errors for up to 1 second.";
- oe.warning = false;
- uint64_t time = OS::get_singleton()->get_ticks_msec();
- oe.hr = time / 3600000;
- oe.min = (time / 60000) % 60;
- oe.sec = (time / 1000) % 60;
- oe.msec = time % 1000;
- errors.push_back(oe);
- }
-
- if (n_warnings_dropped == 1) {
- // Only print one message about dropping per second
- OutputError oe;
- oe.error = "TOO_MANY_WARNINGS";
- oe.error_descr = "Too many warnings! Ignoring warnings for up to 1 second.";
- oe.warning = true;
- uint64_t time = OS::get_singleton()->get_ticks_msec();
- oe.hr = time / 3600000;
- oe.min = (time / 60000) % 60;
- oe.sec = (time / 1000) % 60;
- oe.msec = time % 1000;
- errors.push_back(oe);
- }
-
- while (errors.size()) {
- locking = true;
- OutputError oe = errors.front()->get();
- _put_msg("error", oe.serialize());
- errors.pop_front();
- locking = false;
- }
-}
-
-void ScriptDebuggerRemote::line_poll() {
-
- //the purpose of this is just processing events every now and then when the script might get too busy
- //otherwise bugs like infinite loops can't be caught
- if (poll_every % 2048 == 0)
- _poll_events();
- poll_every++;
-}
-
-void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) {
-
- if (p_type == ERR_HANDLER_SCRIPT)
- return; //ignore script errors, those go through debugger
-
- Vector<ScriptLanguage::StackInfo> si;
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- si = ScriptServer::get_language(i)->debug_get_current_stack_info();
- if (si.size())
- break;
- }
-
- ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
- sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
-}
-
-void ScriptDebuggerRemote::_poll_events() {
-
- //this si called from ::idle_poll, happens only when running the game,
- //does not get called while on debug break
-
- while (packet_peer_stream->get_available_packet_count() > 0) {
-
- _get_output();
-
- //send over output_strings
-
- Variant var;
- Error err = packet_peer_stream->get_var(var);
-
- ERR_CONTINUE(err != OK);
- ERR_CONTINUE(var.get_type() != Variant::ARRAY);
-
- Array cmd = var;
-
- ERR_CONTINUE(cmd.size() < 2);
- ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
- ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
-
- String command = cmd[0];
- Array data = cmd[1];
-
- if (command == "break") {
-
- if (get_break_language())
- debug(get_break_language());
- } else {
- _parse_message(command, data);
- }
- }
-}
-
-void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) {
-
- int ofs = 0;
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- if (p_for_frame)
- ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs);
- else
- ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs);
- }
-
- for (int i = 0; i < ofs; i++) {
- profile_info_ptrs.write[i] = &profile_info.write[i];
- }
-
- SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
- sa.sort(profile_info_ptrs.ptrw(), ofs);
-
- int to_send = MIN(ofs, max_frame_functions);
-
- //check signatures first
- uint64_t total_script_time = 0;
-
- for (int i = 0; i < to_send; i++) {
-
- if (!profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
-
- int idx = profiler_function_signature_map.size();
- ProfilerSignature sig;
- sig.name = profile_info_ptrs[i]->signature;
- sig.id = idx;
- _put_msg("profile_sig", sig.serialize());
- profiler_function_signature_map[profile_info_ptrs[i]->signature] = idx;
- }
-
- total_script_time += profile_info_ptrs[i]->self_time;
- }
-
- //send frames then
- ProfilerFrame metric;
- metric.frame_number = Engine::get_singleton()->get_frames_drawn();
- metric.frame_time = frame_time;
- metric.idle_time = idle_time;
- metric.physics_time = physics_time;
- metric.physics_frame_time = physics_frame_time;
- metric.script_time = total_script_time;
-
- // Add script functions information.
- metric.frame_functions.resize(to_send);
- FrameFunction *w = metric.frame_functions.ptrw();
- for (int i = 0; i < to_send; i++) {
-
- if (profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
- w[i].sig_id = profiler_function_signature_map[profile_info_ptrs[i]->signature];
- }
-
- w[i].call_count = profile_info_ptrs[i]->call_count;
- w[i].total_time = profile_info_ptrs[i]->total_time / 1000000.0;
- w[i].self_time = profile_info_ptrs[i]->self_time / 1000000.0;
- }
- if (p_for_frame) {
- // Add profile frame data information.
- metric.frames_data.append_array(profile_frame_data);
- _put_msg("profile_frame", metric.serialize());
- profile_frame_data.clear();
- } else {
- _put_msg("profile_total", metric.serialize());
- }
-}
-
-void ScriptDebuggerRemote::idle_poll() {
-
- // this function is called every frame, except when there is a debugger break (::debug() in this class)
- // execution stops and remains in the ::debug function
-
- _get_output();
-
- if (requested_quit) {
-
- _put_msg("kill_me", Array());
- requested_quit = false;
- }
-
- if (performance) {
-
- uint64_t pt = OS::get_singleton()->get_ticks_msec();
- if (pt - last_perf_time > 1000) {
-
- last_perf_time = pt;
- int max = performance->get("MONITOR_MAX");
- Array arr;
- arr.resize(max);
- for (int i = 0; i < max; i++) {
- arr[i] = performance->call("get_monitor", i);
- }
- _put_msg("performance", arr);
- }
- }
-
- if (visual_profiling) {
- Vector<VS::FrameProfileArea> profile_areas = VS::get_singleton()->get_frame_profile();
- if (profile_areas.size()) {
- Vector<String> area_names;
- Vector<real_t> area_times;
- area_names.resize(profile_areas.size());
- area_times.resize(profile_areas.size() * 2);
- {
- String *area_namesw = area_names.ptrw();
- real_t *area_timesw = area_times.ptrw();
-
- for (int i = 0; i < profile_areas.size(); i++) {
- area_namesw[i] = profile_areas[i].name;
- area_timesw[i * 2 + 0] = profile_areas[i].cpu_msec;
- area_timesw[i * 2 + 1] = profile_areas[i].gpu_msec;
- }
- }
- Array msg;
- msg.push_back(VS::get_singleton()->get_frame_profile_frame());
- msg.push_back(area_names);
- msg.push_back(area_times);
- _put_msg("visual_profile", msg);
- }
- }
-
- if (profiling) {
-
- if (skip_profile_frame) {
- skip_profile_frame = false;
- } else {
- //send profiling info normally
- _send_profiling_data(true);
- }
- }
-
- if (network_profiling) {
- uint64_t pt = OS::get_singleton()->get_ticks_msec();
- if (pt - last_net_bandwidth_time > 200) {
- last_net_bandwidth_time = pt;
- _send_network_bandwidth_usage();
- }
- if (pt - last_net_prof_time > 100) {
- last_net_prof_time = pt;
- _send_network_profiling_data();
- }
- }
-
- if (reload_all_scripts) {
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->reload_all_scripts();
- }
- reload_all_scripts = false;
- }
-
- _poll_events();
-}
-
-void ScriptDebuggerRemote::_send_network_profiling_data() {
- ERR_FAIL_COND(multiplayer.is_null());
-
- int n_nodes = multiplayer->get_profiling_frame(&network_profile_info.write[0]);
-
- NetworkProfilerFrame frame;
- for (int i = 0; i < n_nodes; i++) {
- frame.infos.push_back(network_profile_info[i]);
- }
- _put_msg("network_profile", frame.serialize());
-}
-
-void ScriptDebuggerRemote::_send_network_bandwidth_usage() {
- ERR_FAIL_COND(multiplayer.is_null());
-
- int incoming_bandwidth = multiplayer->get_incoming_bandwidth_usage();
- int outgoing_bandwidth = multiplayer->get_outgoing_bandwidth_usage();
-
- Array arr;
- arr.push_back(incoming_bandwidth);
- arr.push_back(outgoing_bandwidth);
- _put_msg("network_bandwidth", arr);
-}
-
-void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) {
-
- MutexLock lock(mutex);
-
- if (!locking && is_peer_connected()) {
-
- if (messages.size() >= max_messages_per_frame) {
- n_messages_dropped++;
- } else {
- Message msg;
- msg.message = p_message;
- msg.data = p_args;
- messages.push_back(msg);
- }
- }
-}
-
-void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
-
- OutputError oe;
- oe.error = p_err;
- oe.error_descr = p_descr;
- oe.source_file = p_file;
- oe.source_line = p_line;
- oe.source_func = p_func;
- oe.warning = p_type == ERR_HANDLER_WARNING;
- uint64_t time = OS::get_singleton()->get_ticks_msec();
- oe.hr = time / 3600000;
- oe.min = (time / 60000) % 60;
- oe.sec = (time / 1000) % 60;
- oe.msec = time % 1000;
- Array cstack;
-
- uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
- msec_count += ticks - last_msec;
- last_msec = ticks;
-
- if (msec_count > 1000) {
- msec_count = 0;
-
- err_count = 0;
- n_errors_dropped = 0;
- warn_count = 0;
- n_warnings_dropped = 0;
- }
-
- cstack.resize(p_stack_info.size() * 3);
- for (int i = 0; i < p_stack_info.size(); i++) {
- cstack[i * 3 + 0] = p_stack_info[i].file;
- cstack[i * 3 + 1] = p_stack_info[i].func;
- cstack[i * 3 + 2] = p_stack_info[i].line;
- }
-
- //oe.callstack = cstack;
- if (oe.warning) {
- warn_count++;
- } else {
- err_count++;
- }
-
- MutexLock lock(mutex);
-
- if (!locking && is_peer_connected()) {
-
- if (oe.warning) {
- if (warn_count > max_warnings_per_second) {
- n_warnings_dropped++;
- } else {
- errors.push_back(oe);
- }
- } else {
- if (err_count > max_errors_per_second) {
- n_errors_dropped++;
- } else {
- errors.push_back(oe);
- }
- }
- }
-}
-
-void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) {
-
- ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this;
-
- uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
- sdr->msec_count += ticks - sdr->last_msec;
- sdr->last_msec = ticks;
-
- if (sdr->msec_count > 1000) {
- sdr->char_count = 0;
- sdr->msec_count = 0;
- }
-
- String s = p_string;
- int allowed_chars = MIN(MAX(sdr->max_cps - sdr->char_count, 0), s.length());
-
- if (allowed_chars == 0)
- return;
-
- if (allowed_chars < s.length()) {
- s = s.substr(0, allowed_chars);
- }
-
- sdr->char_count += allowed_chars;
- bool overflowed = sdr->char_count >= sdr->max_cps;
-
- {
- MutexLock lock(sdr->mutex);
-
- if (!sdr->locking && sdr->is_peer_connected()) {
-
- if (overflowed)
- s += "[...]";
-
- sdr->output_strings.push_back(s);
-
- if (overflowed) {
- sdr->output_strings.push_back("[output overflow, print less text!]");
- }
- }
- }
-}
-
-void ScriptDebuggerRemote::request_quit() {
-
- requested_quit = true;
-}
-
-void ScriptDebuggerRemote::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- multiplayer = p_multiplayer;
-}
-
-bool ScriptDebuggerRemote::is_profiling() const {
-
- return profiling;
-}
-void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, const Array &p_data) {
-
- int idx = -1;
- for (int i = 0; i < profile_frame_data.size(); i++) {
- if (profile_frame_data[i].name == p_name) {
- idx = i;
- break;
- }
- }
-
- FrameData fd;
- fd.name = p_name;
- fd.data = p_data;
-
- if (idx == -1) {
- profile_frame_data.push_back(fd);
- } else {
- profile_frame_data.write[idx] = fd;
- }
-}
-
-void ScriptDebuggerRemote::profiling_start() {
- //ignores this, uses it via connection
-}
-
-void ScriptDebuggerRemote::profiling_end() {
- //ignores this, uses it via connection
-}
-
-void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
-
- frame_time = p_frame_time;
- idle_time = p_idle_time;
- physics_time = p_physics_time;
- physics_frame_time = p_physics_frame_time;
-}
-
-void ScriptDebuggerRemote::set_skip_breakpoints(bool p_skip_breakpoints) {
- skip_breakpoints = p_skip_breakpoints;
-}
-
-ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
-ScriptDebuggerRemote::ParseMessageFunc ScriptDebuggerRemote::scene_tree_parse_func = NULL;
-
-ScriptDebuggerRemote::ScriptDebuggerRemote() :
- profiling(false),
- visual_profiling(false),
- network_profiling(false),
- max_frame_functions(16),
- skip_profile_frame(false),
- reload_all_scripts(false),
- tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
- packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
- last_perf_time(0),
- last_net_prof_time(0),
- last_net_bandwidth_time(0),
- performance(Engine::get_singleton()->get_singleton_object("Performance")),
- requested_quit(false),
- max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")),
- n_messages_dropped(0),
- max_errors_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_second")),
- max_warnings_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_warnings_per_second")),
- n_errors_dropped(0),
- max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")),
- char_count(0),
- err_count(0),
- warn_count(0),
- last_msec(0),
- msec_count(0),
- locking(false),
- poll_every(0) {
-
- packet_peer_stream->set_stream_peer(tcp_client);
- packet_peer_stream->set_output_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be way more than enough, minus 4 bytes for separator.
-
- phl.printfunc = _print_handler;
- phl.userdata = this;
- add_print_handler(&phl);
-
- eh.errfunc = _err_handler;
- eh.userdata = this;
- add_error_handler(&eh);
-
- profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
- network_profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
- profile_info_ptrs.resize(profile_info.size());
-}
-
-ScriptDebuggerRemote::~ScriptDebuggerRemote() {
-
- remove_print_handler(&phl);
- remove_error_handler(&eh);
-}
diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h
deleted file mode 100644
index b7a309b2f9..0000000000
--- a/core/script_debugger_remote.h
+++ /dev/null
@@ -1,316 +0,0 @@
-/*************************************************************************/
-/* script_debugger_remote.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef SCRIPT_DEBUGGER_REMOTE_H
-#define SCRIPT_DEBUGGER_REMOTE_H
-
-#include "core/io/packet_peer.h"
-#include "core/io/stream_peer_tcp.h"
-#include "core/list.h"
-#include "core/os/os.h"
-#include "core/script_language.h"
-
-class ScriptDebuggerRemote : public ScriptDebugger {
-
-public:
- class ResourceInfo {
- public:
- String path;
- String format;
- String type;
- RID id;
- int vram;
- bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
- ResourceInfo() {
- vram = 0;
- }
- };
-
- class ResourceUsage {
- public:
- List<ResourceInfo> infos;
-
- Array serialize();
- bool deserialize(const Array &p_arr);
- };
-
- class FrameInfo {
- public:
- StringName name;
- float self_time;
- float total_time;
-
- FrameInfo() {
- self_time = 0;
- total_time = 0;
- }
- };
-
- class FrameFunction {
- public:
- int sig_id;
- int call_count;
- StringName name;
- float self_time;
- float total_time;
-
- FrameFunction() {
- sig_id = -1;
- call_count = 0;
- self_time = 0;
- total_time = 0;
- }
- };
-
- class ScriptStackVariable {
- public:
- String name;
- Variant value;
- int type;
- ScriptStackVariable() {
- type = -1;
- }
-
- Array serialize(int max_size = 1 << 20); // 1 MiB default.
- bool deserialize(const Array &p_arr);
- };
-
- class ScriptStackDump {
- public:
- List<ScriptLanguage::StackInfo> frames;
- ScriptStackDump() {}
-
- Array serialize();
- bool deserialize(const Array &p_arr);
- };
-
- class Message {
-
- public:
- String message;
- Array data;
-
- Message() {}
- };
-
- class OutputError {
- public:
- int hr;
- int min;
- int sec;
- int msec;
- String source_file;
- String source_func;
- int source_line;
- String error;
- String error_descr;
- bool warning;
- Vector<ScriptLanguage::StackInfo> callstack;
-
- OutputError() {
- hr = -1;
- min = -1;
- sec = -1;
- msec = -1;
- source_line = -1;
- warning = false;
- }
-
- Array serialize();
- bool deserialize(const Array &p_arr);
- };
-
- struct FrameData {
-
- StringName name;
- Array data;
- };
-
- class ProfilerSignature {
- public:
- StringName name;
- int id;
-
- Array serialize();
- bool deserialize(const Array &p_arr);
-
- ProfilerSignature() {
- id = -1;
- };
- };
-
- class ProfilerFrame {
- public:
- int frame_number;
- float frame_time;
- float idle_time;
- float physics_time;
- float physics_frame_time;
- float script_time;
-
- Vector<FrameData> frames_data;
- Vector<FrameFunction> frame_functions;
-
- ProfilerFrame() {
- frame_number = 0;
- frame_time = 0;
- idle_time = 0;
- physics_time = 0;
- physics_frame_time = 0;
- }
-
- Array serialize();
- bool deserialize(const Array &p_arr);
- };
-
- class NetworkProfilerFrame {
- public:
- Vector<MultiplayerAPI::ProfilingInfo> infos;
-
- Array serialize();
- bool deserialize(const Array &p_arr);
-
- NetworkProfilerFrame(){};
- };
-
-protected:
- struct ProfileInfoSort {
-
- bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
- return A->total_time < B->total_time;
- }
- };
-
- Vector<ScriptLanguage::ProfilingInfo> profile_info;
- Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs;
- Vector<MultiplayerAPI::ProfilingInfo> network_profile_info;
-
- Map<StringName, int> profiler_function_signature_map;
- float frame_time, idle_time, physics_time, physics_frame_time;
-
- bool profiling;
- bool visual_profiling;
- bool network_profiling;
- int max_frame_functions;
- bool skip_profile_frame;
- bool reload_all_scripts;
-
- Ref<StreamPeerTCP> tcp_client;
- Ref<PacketPeerStream> packet_peer_stream;
-
- uint64_t last_perf_time;
- uint64_t last_net_prof_time;
- uint64_t last_net_bandwidth_time;
- Object *performance;
- bool requested_quit;
- Mutex mutex;
-
- List<String> output_strings;
- List<Message> messages;
- int max_messages_per_frame;
- int n_messages_dropped;
- List<OutputError> errors;
- int max_errors_per_second;
- int max_warnings_per_second;
- int n_errors_dropped;
- int n_warnings_dropped;
-
- int max_cps;
- int char_count;
- int err_count;
- int warn_count;
- uint64_t last_msec;
- uint64_t msec_count;
-
- bool locking; //hack to avoid a deadloop
- static void _print_handler(void *p_this, const String &p_string, bool p_error);
-
- PrintHandlerList phl;
-
- void _get_output();
- void _poll_events();
- uint32_t poll_every;
-
- void _parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script = NULL);
-
- void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
-
- void _send_object_id(ObjectID p_id);
- void _send_video_memory();
-
- Ref<MultiplayerAPI> multiplayer;
-
- ErrorHandlerList eh;
- static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
-
- void _put_msg(String p_message, Array p_data);
- void _send_profiling_data(bool p_for_frame);
- void _send_network_profiling_data();
- void _send_network_bandwidth_usage();
-
- Vector<FrameData> profile_frame_data;
-
- bool skip_breakpoints;
-
-public:
- typedef void (*ResourceUsageFunc)(ResourceUsage *);
- typedef Error (*ParseMessageFunc)(const String &p_name, const Array &p_msg); // Returns true if something was found (stopping propagation).
-
- static ResourceUsageFunc resource_usage_func;
- static ParseMessageFunc scene_tree_parse_func; // Could be made into list, extensible...
-
- Error connect_to_host(const String &p_host, uint16_t p_port);
- bool is_peer_connected();
- virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
- virtual void idle_poll();
- virtual void line_poll();
-
- virtual bool is_remote() const { return true; }
- virtual void request_quit();
-
- virtual void send_message(const String &p_message, const Array &p_args);
- virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
-
- virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
-
- virtual bool is_profiling() const;
- virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data);
-
- virtual void profiling_start();
- virtual void profiling_end();
- virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
-
- virtual void set_skip_breakpoints(bool p_skip_breakpoints);
-
- ScriptDebuggerRemote();
- ~ScriptDebuggerRemote();
-};
-
-#endif // SCRIPT_DEBUGGER_REMOTE_H
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 0b00247502..428f363b17 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -31,6 +31,8 @@
#include "script_language.h"
#include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
#include "core/project_settings.h"
#include <stdint.h>
@@ -46,8 +48,8 @@ void Script::_notification(int p_what) {
if (p_what == NOTIFICATION_POSTINITIALIZE) {
- if (ScriptDebugger::get_singleton())
- ScriptDebugger::get_singleton()->set_break_language(get_language());
+ if (EngineDebugger::is_active())
+ EngineDebugger::get_script_debugger()->set_break_language(get_language());
}
}
@@ -356,89 +358,6 @@ ScriptCodeCompletionCache::ScriptCodeCompletionCache() {
void ScriptLanguage::frame() {
}
-ScriptDebugger *ScriptDebugger::singleton = NULL;
-
-void ScriptDebugger::set_lines_left(int p_left) {
-
- lines_left = p_left;
-}
-
-int ScriptDebugger::get_lines_left() const {
-
- return lines_left;
-}
-
-void ScriptDebugger::set_depth(int p_depth) {
-
- depth = p_depth;
-}
-
-int ScriptDebugger::get_depth() const {
-
- return depth;
-}
-
-void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) {
-
- if (!breakpoints.has(p_line))
- breakpoints[p_line] = Set<StringName>();
- breakpoints[p_line].insert(p_source);
-}
-
-void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) {
-
- if (!breakpoints.has(p_line))
- return;
-
- breakpoints[p_line].erase(p_source);
- if (breakpoints[p_line].size() == 0)
- breakpoints.erase(p_line);
-}
-bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const {
-
- if (!breakpoints.has(p_line))
- return false;
- return breakpoints[p_line].has(p_source);
-}
-bool ScriptDebugger::is_breakpoint_line(int p_line) const {
-
- return breakpoints.has(p_line);
-}
-
-String ScriptDebugger::breakpoint_find_source(const String &p_source) const {
-
- return p_source;
-}
-
-void ScriptDebugger::clear_breakpoints() {
-
- breakpoints.clear();
-}
-
-void ScriptDebugger::idle_poll() {
-}
-
-void ScriptDebugger::line_poll() {
-}
-
-void ScriptDebugger::set_break_language(ScriptLanguage *p_lang) {
-
- break_lang = p_lang;
-}
-
-ScriptLanguage *ScriptDebugger::get_break_language() const {
-
- return break_lang;
-}
-
-ScriptDebugger::ScriptDebugger() {
-
- singleton = this;
- lines_left = -1;
- depth = -1;
- break_lang = NULL;
-}
-
bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) {
if (script->is_placeholder_fallback_enabled())
diff --git a/core/script_language.h b/core/script_language.h
index 48570ae546..f1b809425b 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -350,6 +350,11 @@ public:
virtual void thread_exit() {}
/* DEBUGGER FUNCTIONS */
+ struct StackInfo {
+ String file;
+ String func;
+ int line;
+ };
virtual String debug_get_error() const = 0;
virtual int debug_get_stack_level_count() const = 0;
@@ -362,12 +367,6 @@ public:
virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0;
virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) = 0;
- struct StackInfo {
- String file;
- String func;
- int line;
- };
-
virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
virtual void reload_all_scripts() = 0;
@@ -460,56 +459,4 @@ public:
PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
~PlaceHolderScriptInstance();
};
-
-class ScriptDebugger {
-
- int lines_left;
- int depth;
-
- static ScriptDebugger *singleton;
- Map<int, Set<StringName> > breakpoints;
-
- ScriptLanguage *break_lang;
-
-public:
- _FORCE_INLINE_ static ScriptDebugger *get_singleton() { return singleton; }
- void set_lines_left(int p_left);
- int get_lines_left() const;
-
- void set_depth(int p_depth);
- int get_depth() const;
-
- String breakpoint_find_source(const String &p_source) const;
- void insert_breakpoint(int p_line, const StringName &p_source);
- void remove_breakpoint(int p_line, const StringName &p_source);
- bool is_breakpoint(int p_line, const StringName &p_source) const;
- bool is_breakpoint_line(int p_line) const;
- void clear_breakpoints();
- const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; }
-
- virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;
- virtual void idle_poll();
- virtual void line_poll();
-
- void set_break_language(ScriptLanguage *p_lang);
- ScriptLanguage *get_break_language() const;
-
- virtual void send_message(const String &p_message, const Array &p_args) = 0;
- virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) = 0;
-
- virtual bool is_remote() const { return false; }
- virtual void request_quit() {}
-
- virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {}
-
- virtual bool is_profiling() const = 0;
- virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) = 0;
- virtual void profiling_start() = 0;
- virtual void profiling_end() = 0;
- virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) = 0;
-
- ScriptDebugger();
- virtual ~ScriptDebugger() { singleton = NULL; }
-};
-
#endif
diff --git a/core/variant.cpp b/core/variant.cpp
index 550974363b..b82602a5a4 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -31,6 +31,7 @@
#include "variant.h"
#include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
#include "core/math/math_funcs.h"
#include "core/print_string.h"
@@ -2221,7 +2222,7 @@ Variant::operator RID() const {
return RID();
} else if (type == OBJECT && _get_obj().obj) {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton()) {
+ if (EngineDebugger::is_active()) {
ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, RID(), "Invalid pointer (object was freed).");
};
#endif
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 99cfc7ed3c..f00aad33fc 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -33,10 +33,10 @@
#include "core/color_names.inc"
#include "core/core_string_names.h"
#include "core/crypto/crypto_core.h"
+#include "core/debugger/engine_debugger.h"
#include "core/io/compression.h"
#include "core/object.h"
#include "core/os/os.h"
-#include "core/script_language.h"
typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args);
typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args);
@@ -1213,7 +1213,7 @@ void Variant::call_ptr(const StringName &p_method, const Variant **p_args, int p
return;
}
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 36d1278929..88f6ce19a2 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -31,8 +31,8 @@
#include "variant.h"
#include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
#include "core/object.h"
-#include "core/script_language.h"
#define CASE_TYPE_ALL(PREFIX, OP) \
CASE_TYPE(PREFIX, OP, INT) \
@@ -1739,7 +1739,7 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool
#ifdef DEBUG_ENABLED
if (!_get_obj().obj) {
break;
- } else if (ScriptDebugger::get_singleton() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ } else if (EngineDebugger::is_active() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
break;
}
@@ -1941,7 +1941,7 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const {
return "Instance base is null.";
} else {
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
if (r_valid)
*r_valid = false;
return "Attempted use of stray pointer object.";
@@ -2556,7 +2556,7 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
if (obj) {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
WARN_PRINT("Attempted use of previously freed pointer object.");
valid = false;
@@ -3011,7 +3011,7 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
valid = false;
return "Attempted get on previously freed instance.";
}
@@ -3076,7 +3076,7 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const {
bool valid = false;
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
if (r_valid) {
*r_valid = false;
}
@@ -3405,7 +3405,7 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
if (obj) {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
WARN_PRINT("Attempted get_property list on previously freed instance.");
return;
}
@@ -3490,7 +3490,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
valid = false;
return false;
}
@@ -3676,7 +3676,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
valid = false;
return false;
}
@@ -3855,7 +3855,7 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
return Variant();
}
#ifdef DEBUG_ENABLED
- if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
r_valid = false;
return Variant();
}