summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorFabio Alessandrelli <fabio.alessandrelli@gmail.com>2020-02-27 03:30:20 +0100
committerFabio Alessandrelli <fabio.alessandrelli@gmail.com>2020-03-08 12:36:39 +0100
commitb8ddaf9c33107e01928e04ed462aa08d4017247b (patch)
tree6ab71cdcbfd372e8390baa6ac30a2974fe43d119 /core
parentd0009636df6544dd26ab7c568a0244af6a20634a (diff)
Refactor ScriptDebugger.
EngineDebugger is the new interface to access the debugger. It tries to be as agnostic as possible on the data that various subsystems can expose. It allows 2 types of interactions: - Profilers: A subsystem can register a profiler, assigning it a unique name. That name can be used to activate the profiler or add data to it. The registered profiler can be composed of up to 3 functions: - Toggle: called when the profiler is activated/deactivated. - Add: called whenever data is added to the debugger (via `EngineDebugger::profiler_add_frame_data`) - Tick: called every frame (during idle), receives frame times. - Captures: (Only relevant in remote debugger for now) A subsystem can register a capture, assigning it a unique name. When receiving a message, the remote debugger will check if it starts with `[prefix]:` and call the associated capture with name `prefix`. Port MultiplayerAPI, Servers, Scripts, Visual, Performance to the new profiler system. Port SceneDebugger and RemoteDebugger to the new capture system. The LocalDebugger also uses the new profiler system for scripts profiling.
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_peer.h)40
-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.h (renamed from core/script_debugger_local.h)79
-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/script_debugger_peer.cpp209
-rw-r--r--core/script_debugger_remote.cpp1093
-rw-r--r--core/script_debugger_remote.h315
-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
24 files changed, 2593 insertions, 2119 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_peer.h b/core/debugger/local_debugger.h
index 194ef0b900..e299df0546 100644
--- a/core/script_debugger_peer.h
+++ b/core/debugger/local_debugger.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* script_debugger_peer.h */
+/* local_debugger.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,21 +28,33 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCRIPT_DEBUGGER_PEER_H
-#define SCRIPT_DEBUGGER_PEER_H
+#ifndef LOCAL_DEBUGGER_H
+#define LOCAL_DEBUGGER_H
-#include "core/reference.h"
-#include "core/ustring.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/list.h"
+#include "core/script_language.h"
+
+class LocalDebugger : public EngineDebugger {
+
+private:
+ struct ScriptsProfiler;
+
+ ScriptsProfiler *scripts_profiler = NULL;
+
+ String target_function;
+ Map<String, String> options;
+
+ Pair<String, int> to_breakpoint(const String &p_line);
+ void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix);
-class ScriptDebuggerPeer : public Reference {
public:
- static Ref<ScriptDebuggerPeer> 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;
+ 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);
+
+ LocalDebugger();
+ ~LocalDebugger();
};
-#endif
+#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/script_debugger_local.h b/core/debugger/remote_debugger_peer.h
index 7a64400191..6fc3214a51 100644
--- a/core/script_debugger_local.h
+++ b/core/debugger/remote_debugger_peer.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* script_debugger_local.h */
+/* remote_debugger_peer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,40 +28,67 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCRIPT_DEBUGGER_LOCAL_H
-#define SCRIPT_DEBUGGER_LOCAL_H
+#ifndef REMOTE_DEBUGGER_PEER_H
+#define REMOTE_DEBUGGER_PEER_H
-#include "core/list.h"
-#include "core/script_language.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 ScriptDebuggerLocal : public ScriptDebugger {
+class RemoteDebuggerPeer : public Reference {
+protected:
+ int max_queued_messages = 4096;
- bool profiling;
- float frame_time, idle_time, physics_time, physics_frame_time;
- uint64_t idle_accum;
- String target_function;
- Map<String, String> options;
+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;
- Vector<ScriptLanguage::ProfilingInfo> pinfo;
+ RemoteDebuggerPeer();
+};
- Pair<String, int> to_breakpoint(const String &p_line);
- void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix);
+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;
-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);
+ static void _thread_func(void *p_ud);
- virtual bool is_profiling() const { return profiling; }
- virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) {}
+ void _poll();
+ void _write_out();
+ void _read_in();
- virtual void idle_poll();
+public:
+ Error connect_to_host(const String &p_host, uint16_t p_port);
- 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 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();
- ScriptDebuggerLocal();
+ RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>());
+ ~RemoteDebuggerPeerTCP();
};
-#endif // SCRIPT_DEBUGGER_LOCAL_H
+#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/script_debugger_peer.cpp b/core/script_debugger_peer.cpp
deleted file mode 100644
index a4e1de6b66..0000000000
--- a/core/script_debugger_peer.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*************************************************************************/
-/* script_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 "script_debugger_peer.h"
-
-#include "core/io/packet_peer.h"
-#include "core/io/stream_peer_tcp.h"
-#include "core/os/mutex.h"
-#include "core/os/os.h"
-#include "core/os/thread.h"
-
-class ScriptDebuggerPeerTCP : public ScriptDebuggerPeer {
-private:
- enum {
- QUEUE_MAX = 2048,
- POLL_USEC_MAX = 100,
- };
-
- Ref<StreamPeerTCP> tcp_client = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
- Ref<PacketPeerStream> packet_peer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
- Mutex mutex;
- Thread *thread = NULL;
- List<Array> in_queue;
- List<Array> out_queue;
- bool connected = false;
- bool running = false;
-
- static void _thread_func(void *p_ud);
-
- void _poll();
-
-public:
- void poll();
- Error connect_to_host(const String &p_host, uint16_t p_port);
-
- bool is_peer_connected() {
- return connected;
- }
-
- bool has_message() {
- return in_queue.size() > 0;
- }
-
- Array get_message() {
- MutexLock lock(mutex);
- ERR_FAIL_COND_V(!has_message(), Array());
- Array out = in_queue[0];
- in_queue.pop_front();
- return out;
- }
-
- Error put_message(const Array &p_arr) {
- MutexLock lock(mutex);
- if (out_queue.size() >= 2048) // XXX Should we keep track of size instead?
- return ERR_OUT_OF_MEMORY;
-
- out_queue.push_back(p_arr);
- return OK;
- }
-
- void close() {
- if (thread) {
- running = false;
- Thread::wait_to_finish(thread);
- memdelete(thread);
- thread = NULL;
- }
- MutexLock lock(mutex);
- tcp_client->disconnect_from_host();
- packet_peer->set_stream_peer(Ref<StreamPeer>());
- }
-
- ScriptDebuggerPeerTCP() {
- packet_peer->set_output_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be way more than enough, minus 4 bytes for separator.
- }
-
- ~ScriptDebuggerPeerTCP() {
- close();
- }
-};
-
-Error ScriptDebuggerPeerTCP::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->set_stream_peer(tcp_client);
- connected = true;
-#ifndef NO_THREADS
- running = true;
- thread = Thread::create(_thread_func, this);
-#endif
- return OK;
-}
-
-void ScriptDebuggerPeerTCP::_thread_func(void *p_ud) {
- ScriptDebuggerPeerTCP *peer = (ScriptDebuggerPeerTCP *)p_ud;
- while (peer->running && peer->is_peer_connected()) {
- peer->_poll();
- if (!peer->is_peer_connected())
- break;
- OS::get_singleton()->delay_usec(100);
- }
-}
-
-void ScriptDebuggerPeerTCP::poll() {
-#ifdef NO_THREADS
- _poll();
-#endif
-}
-
-void ScriptDebuggerPeerTCP::_poll() {
- MutexLock lock(mutex);
- // Poll in
- uint64_t ticks = OS::get_singleton()->get_ticks_usec();
- while (connected && packet_peer->get_available_packet_count() > 0 && in_queue.size() < QUEUE_MAX && OS::get_singleton()->get_ticks_usec() - ticks < POLL_USEC_MAX) {
- Variant var;
- const Error err = packet_peer->get_var(var);
- connected = tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
- if (err != OK) {
- ERR_PRINT("Error reading variant from peer");
- break;
- }
- ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
- in_queue.push_back(var);
- }
- // Poll out
- ticks = OS::get_singleton()->get_ticks_usec();
- while (connected && out_queue.size() > 0 && OS::get_singleton()->get_ticks_usec() - ticks < POLL_USEC_MAX) {
- Array arr = out_queue[0];
- out_queue.pop_front();
- const Error err = packet_peer->put_var(arr);
- connected = tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
- if (err != OK) {
- ERR_PRINT("Error writing variant to peer");
- break;
- }
- }
-}
-
-Ref<ScriptDebuggerPeer> ScriptDebuggerPeer::create_from_uri(const String p_uri) {
- String debug_host = p_uri;
- 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, debug_host.length()).to_int();
- debug_host = debug_host.substr(0, sep_pos);
- }
- Ref<ScriptDebuggerPeerTCP> peer = Ref<ScriptDebuggerPeer>(memnew(ScriptDebuggerPeerTCP));
- Error err = peer->connect_to_host(debug_host, debug_port);
- if (err != OK)
- return Ref<ScriptDebuggerPeer>();
- return peer;
-}
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
deleted file mode 100644
index 3a3b83367e..0000000000
--- a/core/script_debugger_remote.cpp
+++ /dev/null
@@ -1,1093 +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);
- peer->put_message(msg);
-}
-
-bool ScriptDebuggerRemote::is_peer_connected() {
- return peer->is_peer_connected();
-}
-
-void ScriptDebuggerRemote::_send_video_memory() {
-
- ResourceUsage usage;
- if (resource_usage_func)
- resource_usage_func(&usage);
-
- _put_msg("message:video_mem", usage.serialize());
-}
-
-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();
- peer->poll();
-
- _get_output();
-
- 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") {
-
- 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 (peer->has_message()) {
-
- peer->poll();
- //send over output_strings
- _get_output();
-
- 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 == "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 *ScriptDebuggerRemote::create_for_uri(const String &p_uri) {
- Ref<ScriptDebuggerPeer> peer = ScriptDebuggerPeer::create_from_uri(p_uri);
- if (peer.is_valid())
- return memnew(ScriptDebuggerRemote(peer));
- return NULL;
-}
-
-ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
-ScriptDebuggerRemote::ParseMessageFunc ScriptDebuggerRemote::scene_tree_parse_func = NULL;
-
-ScriptDebuggerRemote::ScriptDebuggerRemote(Ref<ScriptDebuggerPeer> p_peer) :
- profiling(false),
- visual_profiling(false),
- network_profiling(false),
- max_frame_functions(16),
- skip_profile_frame(false),
- reload_all_scripts(false),
- 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) {
-
- peer = p_peer;
- 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 2a30f6d7c0..0000000000
--- a/core/script_debugger_remote.h
+++ /dev/null
@@ -1,315 +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/list.h"
-#include "core/os/os.h"
-#include "core/script_debugger_peer.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<ScriptDebuggerPeer> peer;
-
- 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...
-
- static ScriptDebuggerRemote *create_for_uri(const String &p_uri);
-
- 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(Ref<ScriptDebuggerPeer> p_peer);
- ~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();
}