mirror of
https://github.com/godotengine/godot.git
synced 2024-10-23 05:31:54 +00:00
Merge pull request #36751 from Faless/debugger/threads_and_profilers
ScriptDebugger refactor, threading, profilers.
This commit is contained in:
commit
478337c412
|
@ -165,6 +165,7 @@ SConscript('os/SCsub')
|
|||
SConscript('math/SCsub')
|
||||
SConscript('crypto/SCsub')
|
||||
SConscript('io/SCsub')
|
||||
SConscript('debugger/SCsub')
|
||||
SConscript('bind/SCsub')
|
||||
|
||||
|
||||
|
|
5
core/debugger/SCsub
Normal file
5
core/debugger/SCsub
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import('env')
|
||||
|
||||
env.add_source_files(env.core_sources, "*.cpp")
|
329
core/debugger/debugger_marshalls.cpp
Normal file
329
core/debugger/debugger_marshalls.cpp
Normal file
|
@ -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;
|
||||
}
|
175
core/debugger/debugger_marshalls.h
Normal file
175
core/debugger/debugger_marshalls.h
Normal file
|
@ -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
|
186
core/debugger/engine_debugger.cpp
Normal file
186
core/debugger/engine_debugger.cpp
Normal file
|
@ -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;
|
||||
}
|
130
core/debugger/engine_debugger.h
Normal file
130
core/debugger/engine_debugger.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*************************************************************************/
|
||||
/* script_debugger_local.h */
|
||||
/* local_debugger.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
|
@ -28,40 +28,33 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef SCRIPT_DEBUGGER_LOCAL_H
|
||||
#define SCRIPT_DEBUGGER_LOCAL_H
|
||||
#ifndef LOCAL_DEBUGGER_H
|
||||
#define LOCAL_DEBUGGER_H
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/list.h"
|
||||
#include "core/script_language.h"
|
||||
|
||||
class ScriptDebuggerLocal : public ScriptDebugger {
|
||||
class LocalDebugger : public EngineDebugger {
|
||||
|
||||
private:
|
||||
struct ScriptsProfiler;
|
||||
|
||||
ScriptsProfiler *scripts_profiler = NULL;
|
||||
|
||||
bool profiling;
|
||||
float frame_time, idle_time, physics_time, physics_frame_time;
|
||||
uint64_t idle_accum;
|
||||
String target_function;
|
||||
Map<String, String> options;
|
||||
|
||||
Vector<ScriptLanguage::ProfilingInfo> pinfo;
|
||||
|
||||
Pair<String, int> to_breakpoint(const String &p_line);
|
||||
void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix);
|
||||
|
||||
public:
|
||||
void debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint);
|
||||
virtual void send_message(const String &p_message, const Array &p_args);
|
||||
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
|
||||
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);
|
||||
|
||||
virtual bool is_profiling() const { return profiling; }
|
||||
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) {}
|
||||
|
||||
virtual void idle_poll();
|
||||
|
||||
virtual void profiling_start();
|
||||
virtual void profiling_end();
|
||||
virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
|
||||
|
||||
ScriptDebuggerLocal();
|
||||
LocalDebugger();
|
||||
~LocalDebugger();
|
||||
};
|
||||
|
||||
#endif // SCRIPT_DEBUGGER_LOCAL_H
|
||||
#endif // LOCAL_DEBUGGER_H
|
935
core/debugger/remote_debugger.cpp
Normal file
935
core/debugger/remote_debugger.cpp
Normal file
|
@ -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);
|
||||
}
|
114
core/debugger/remote_debugger.h
Normal file
114
core/debugger/remote_debugger.h
Normal file
|
@ -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
|
242
core/debugger/remote_debugger_peer.cpp
Normal file
242
core/debugger/remote_debugger_peer.cpp
Normal file
|
@ -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");
|
||||
}
|
94
core/debugger/remote_debugger_peer.h
Normal file
94
core/debugger/remote_debugger_peer.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*************************************************************************/
|
||||
/* remote_debugger_peer.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef REMOTE_DEBUGGER_PEER_H
|
||||
#define REMOTE_DEBUGGER_PEER_H
|
||||
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/reference.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
class RemoteDebuggerPeer : public Reference {
|
||||
protected:
|
||||
int max_queued_messages = 4096;
|
||||
|
||||
public:
|
||||
static Ref<RemoteDebuggerPeer> create_from_uri(const String p_uri);
|
||||
virtual bool is_peer_connected() = 0;
|
||||
virtual bool has_message() = 0;
|
||||
virtual Error put_message(const Array &p_arr) = 0;
|
||||
virtual Array get_message() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual void poll() = 0;
|
||||
virtual int get_max_message_size() const = 0;
|
||||
|
||||
RemoteDebuggerPeer();
|
||||
};
|
||||
|
||||
class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer {
|
||||
private:
|
||||
Ref<StreamPeerTCP> tcp_client;
|
||||
Mutex mutex;
|
||||
Thread *thread = NULL;
|
||||
List<Array> in_queue;
|
||||
List<Array> out_queue;
|
||||
int out_left = 0;
|
||||
int out_pos = 0;
|
||||
Vector<uint8_t> out_buf;
|
||||
int in_left = 0;
|
||||
int in_pos = 0;
|
||||
Vector<uint8_t> in_buf;
|
||||
bool connected = false;
|
||||
bool running = false;
|
||||
|
||||
static void _thread_func(void *p_ud);
|
||||
|
||||
void _poll();
|
||||
void _write_out();
|
||||
void _read_in();
|
||||
|
||||
public:
|
||||
Error connect_to_host(const String &p_host, uint16_t p_port);
|
||||
|
||||
void poll();
|
||||
bool is_peer_connected();
|
||||
bool has_message();
|
||||
Array get_message();
|
||||
Error put_message(const Array &p_arr);
|
||||
int get_max_message_size() const;
|
||||
void close();
|
||||
|
||||
RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>());
|
||||
~RemoteDebuggerPeerTCP();
|
||||
};
|
||||
|
||||
#endif // REMOTE_DEBUGGER_PEER_H
|
123
core/debugger/script_debugger.cpp
Normal file
123
core/debugger/script_debugger.cpp
Normal file
|
@ -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;
|
||||
}
|
80
core/debugger/script_debugger.h
Normal file
80
core/debugger/script_debugger.h
Normal file
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -288,6 +288,11 @@ void StreamPeerTCP::disconnect_from_host() {
|
|||
peer_port = 0;
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::poll(NetSocket::PollType p_type, int timeout) {
|
||||
ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
|
||||
return _sock->poll(p_type, timeout);
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
|
||||
|
||||
int total;
|
||||
|
|
|
@ -78,6 +78,9 @@ public:
|
|||
|
||||
void set_no_delay(bool p_enabled);
|
||||
|
||||
// Poll functions (wait or check for writable, readable)
|
||||
Error poll(NetSocket::PollType p_type, int timeout = 0);
|
||||
|
||||
// Read/Write from StreamPeer
|
||||
Error put_data(const uint8_t *p_data, int p_bytes);
|
||||
Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,316 +0,0 @@
|
|||
/*************************************************************************/
|
||||
/* script_debugger_remote.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef SCRIPT_DEBUGGER_REMOTE_H
|
||||
#define SCRIPT_DEBUGGER_REMOTE_H
|
||||
|
||||
#include "core/io/packet_peer.h"
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/list.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/script_language.h"
|
||||
|
||||
class ScriptDebuggerRemote : public ScriptDebugger {
|
||||
|
||||
public:
|
||||
class ResourceInfo {
|
||||
public:
|
||||
String path;
|
||||
String format;
|
||||
String type;
|
||||
RID id;
|
||||
int vram;
|
||||
bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
|
||||
ResourceInfo() {
|
||||
vram = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceUsage {
|
||||
public:
|
||||
List<ResourceInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class FrameInfo {
|
||||
public:
|
||||
StringName name;
|
||||
float self_time;
|
||||
float total_time;
|
||||
|
||||
FrameInfo() {
|
||||
self_time = 0;
|
||||
total_time = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class FrameFunction {
|
||||
public:
|
||||
int sig_id;
|
||||
int call_count;
|
||||
StringName name;
|
||||
float self_time;
|
||||
float total_time;
|
||||
|
||||
FrameFunction() {
|
||||
sig_id = -1;
|
||||
call_count = 0;
|
||||
self_time = 0;
|
||||
total_time = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class ScriptStackVariable {
|
||||
public:
|
||||
String name;
|
||||
Variant value;
|
||||
int type;
|
||||
ScriptStackVariable() {
|
||||
type = -1;
|
||||
}
|
||||
|
||||
Array serialize(int max_size = 1 << 20); // 1 MiB default.
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class ScriptStackDump {
|
||||
public:
|
||||
List<ScriptLanguage::StackInfo> frames;
|
||||
ScriptStackDump() {}
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class Message {
|
||||
|
||||
public:
|
||||
String message;
|
||||
Array data;
|
||||
|
||||
Message() {}
|
||||
};
|
||||
|
||||
class OutputError {
|
||||
public:
|
||||
int hr;
|
||||
int min;
|
||||
int sec;
|
||||
int msec;
|
||||
String source_file;
|
||||
String source_func;
|
||||
int source_line;
|
||||
String error;
|
||||
String error_descr;
|
||||
bool warning;
|
||||
Vector<ScriptLanguage::StackInfo> callstack;
|
||||
|
||||
OutputError() {
|
||||
hr = -1;
|
||||
min = -1;
|
||||
sec = -1;
|
||||
msec = -1;
|
||||
source_line = -1;
|
||||
warning = false;
|
||||
}
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
struct FrameData {
|
||||
|
||||
StringName name;
|
||||
Array data;
|
||||
};
|
||||
|
||||
class ProfilerSignature {
|
||||
public:
|
||||
StringName name;
|
||||
int id;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
|
||||
ProfilerSignature() {
|
||||
id = -1;
|
||||
};
|
||||
};
|
||||
|
||||
class ProfilerFrame {
|
||||
public:
|
||||
int frame_number;
|
||||
float frame_time;
|
||||
float idle_time;
|
||||
float physics_time;
|
||||
float physics_frame_time;
|
||||
float script_time;
|
||||
|
||||
Vector<FrameData> frames_data;
|
||||
Vector<FrameFunction> frame_functions;
|
||||
|
||||
ProfilerFrame() {
|
||||
frame_number = 0;
|
||||
frame_time = 0;
|
||||
idle_time = 0;
|
||||
physics_time = 0;
|
||||
physics_frame_time = 0;
|
||||
}
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
};
|
||||
|
||||
class NetworkProfilerFrame {
|
||||
public:
|
||||
Vector<MultiplayerAPI::ProfilingInfo> infos;
|
||||
|
||||
Array serialize();
|
||||
bool deserialize(const Array &p_arr);
|
||||
|
||||
NetworkProfilerFrame(){};
|
||||
};
|
||||
|
||||
protected:
|
||||
struct ProfileInfoSort {
|
||||
|
||||
bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
|
||||
return A->total_time < B->total_time;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<ScriptLanguage::ProfilingInfo> profile_info;
|
||||
Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs;
|
||||
Vector<MultiplayerAPI::ProfilingInfo> network_profile_info;
|
||||
|
||||
Map<StringName, int> profiler_function_signature_map;
|
||||
float frame_time, idle_time, physics_time, physics_frame_time;
|
||||
|
||||
bool profiling;
|
||||
bool visual_profiling;
|
||||
bool network_profiling;
|
||||
int max_frame_functions;
|
||||
bool skip_profile_frame;
|
||||
bool reload_all_scripts;
|
||||
|
||||
Ref<StreamPeerTCP> tcp_client;
|
||||
Ref<PacketPeerStream> packet_peer_stream;
|
||||
|
||||
uint64_t last_perf_time;
|
||||
uint64_t last_net_prof_time;
|
||||
uint64_t last_net_bandwidth_time;
|
||||
Object *performance;
|
||||
bool requested_quit;
|
||||
Mutex mutex;
|
||||
|
||||
List<String> output_strings;
|
||||
List<Message> messages;
|
||||
int max_messages_per_frame;
|
||||
int n_messages_dropped;
|
||||
List<OutputError> errors;
|
||||
int max_errors_per_second;
|
||||
int max_warnings_per_second;
|
||||
int n_errors_dropped;
|
||||
int n_warnings_dropped;
|
||||
|
||||
int max_cps;
|
||||
int char_count;
|
||||
int err_count;
|
||||
int warn_count;
|
||||
uint64_t last_msec;
|
||||
uint64_t msec_count;
|
||||
|
||||
bool locking; //hack to avoid a deadloop
|
||||
static void _print_handler(void *p_this, const String &p_string, bool p_error);
|
||||
|
||||
PrintHandlerList phl;
|
||||
|
||||
void _get_output();
|
||||
void _poll_events();
|
||||
uint32_t poll_every;
|
||||
|
||||
void _parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script = NULL);
|
||||
|
||||
void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
|
||||
|
||||
void _send_object_id(ObjectID p_id);
|
||||
void _send_video_memory();
|
||||
|
||||
Ref<MultiplayerAPI> multiplayer;
|
||||
|
||||
ErrorHandlerList eh;
|
||||
static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
|
||||
|
||||
void _put_msg(String p_message, Array p_data);
|
||||
void _send_profiling_data(bool p_for_frame);
|
||||
void _send_network_profiling_data();
|
||||
void _send_network_bandwidth_usage();
|
||||
|
||||
Vector<FrameData> profile_frame_data;
|
||||
|
||||
bool skip_breakpoints;
|
||||
|
||||
public:
|
||||
typedef void (*ResourceUsageFunc)(ResourceUsage *);
|
||||
typedef Error (*ParseMessageFunc)(const String &p_name, const Array &p_msg); // Returns true if something was found (stopping propagation).
|
||||
|
||||
static ResourceUsageFunc resource_usage_func;
|
||||
static ParseMessageFunc scene_tree_parse_func; // Could be made into list, extensible...
|
||||
|
||||
Error connect_to_host(const String &p_host, uint16_t p_port);
|
||||
bool is_peer_connected();
|
||||
virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
|
||||
virtual void idle_poll();
|
||||
virtual void line_poll();
|
||||
|
||||
virtual bool is_remote() const { return true; }
|
||||
virtual void request_quit();
|
||||
|
||||
virtual void send_message(const String &p_message, const Array &p_args);
|
||||
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
|
||||
|
||||
virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
|
||||
|
||||
virtual bool is_profiling() const;
|
||||
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data);
|
||||
|
||||
virtual void profiling_start();
|
||||
virtual void profiling_end();
|
||||
virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
|
||||
|
||||
virtual void set_skip_breakpoints(bool p_skip_breakpoints);
|
||||
|
||||
ScriptDebuggerRemote();
|
||||
~ScriptDebuggerRemote();
|
||||
};
|
||||
|
||||
#endif // SCRIPT_DEBUGGER_REMOTE_H
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -824,17 +824,17 @@
|
|||
</member>
|
||||
<member name="mono/unhandled_exception_policy" type="int" setter="" getter="" default="0">
|
||||
</member>
|
||||
<member name="network/limits/debugger_stdout/max_chars_per_second" type="int" setter="" getter="" default="2048">
|
||||
<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="2048">
|
||||
Maximum amount of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
||||
</member>
|
||||
<member name="network/limits/debugger_stdout/max_errors_per_second" type="int" setter="" getter="" default="100">
|
||||
Maximum number of errors allowed to be sent as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
||||
<member name="network/limits/debugger/max_errors_per_second" type="int" setter="" getter="" default="100">
|
||||
Maximum number of errors allowed to be sent from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
||||
</member>
|
||||
<member name="network/limits/debugger_stdout/max_messages_per_frame" type="int" setter="" getter="" default="10">
|
||||
Maximum amount of messages allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
||||
<member name="network/limits/debugger/max_queued_messages" type="int" setter="" getter="" default="10">
|
||||
Maximum amount of messages in the debugger queue. Over this value, content is dropped. This helps to limit the debugger memory usage.
|
||||
</member>
|
||||
<member name="network/limits/debugger_stdout/max_warnings_per_second" type="int" setter="" getter="" default="100">
|
||||
Maximum number of warnings allowed to be sent as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
||||
<member name="network/limits/debugger/max_warnings_per_second" type="int" setter="" getter="" default="100">
|
||||
Maximum number of warnings allowed to be sent from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
||||
</member>
|
||||
<member name="network/limits/packet_peer_stream/max_buffer_po2" type="int" setter="" getter="" default="16">
|
||||
Default size of packet peer stream for deserializing Godot data. Over this size, data is dropped.
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#ifdef UNIX_ENABLED
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
#include "core/os/thread_dummy.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "drivers/unix/dir_access_unix.h"
|
||||
|
@ -94,16 +96,16 @@ void OS_Unix::debug_break() {
|
|||
};
|
||||
|
||||
static void handle_interrupt(int sig) {
|
||||
if (ScriptDebugger::get_singleton() == NULL)
|
||||
if (!EngineDebugger::is_active())
|
||||
return;
|
||||
|
||||
ScriptDebugger::get_singleton()->set_depth(-1);
|
||||
ScriptDebugger::get_singleton()->set_lines_left(1);
|
||||
EngineDebugger::get_script_debugger()->set_depth(-1);
|
||||
EngineDebugger::get_script_debugger()->set_lines_left(1);
|
||||
}
|
||||
|
||||
void OS_Unix::initialize_debugging() {
|
||||
|
||||
if (ScriptDebugger::get_singleton() != NULL) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = handle_interrupt;
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
|
||||
#include "editor_debugger_inspector.h"
|
||||
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
|
||||
|
@ -226,7 +226,7 @@ Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
|
|||
|
||||
void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
|
||||
|
||||
ScriptDebuggerRemote::ScriptStackVariable var;
|
||||
DebuggerMarshalls::ScriptStackVariable var;
|
||||
var.deserialize(p_array);
|
||||
String n = var.name;
|
||||
Variant v = var.value;
|
||||
|
|
|
@ -31,9 +31,12 @@
|
|||
#include "editor_debugger_node.h"
|
||||
|
||||
#include "editor/debugger/editor_debugger_tree.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
|
||||
template <typename Func>
|
||||
void _for_all(TabContainer *p_node, const Func &p_func) {
|
||||
|
@ -49,7 +52,6 @@ EditorDebuggerNode *EditorDebuggerNode::singleton = NULL;
|
|||
EditorDebuggerNode::EditorDebuggerNode() {
|
||||
if (!singleton)
|
||||
singleton = this;
|
||||
server.instance();
|
||||
|
||||
add_constant_override("margin_left", -EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_LEFT));
|
||||
add_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_RIGHT));
|
||||
|
@ -179,10 +181,9 @@ Error EditorDebuggerNode::start() {
|
|||
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
|
||||
}
|
||||
|
||||
int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
|
||||
const Error err = server->listen(remote_port);
|
||||
server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create_default());
|
||||
const Error err = server->start();
|
||||
if (err != OK) {
|
||||
EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR);
|
||||
return err;
|
||||
}
|
||||
set_process(true);
|
||||
|
@ -191,9 +192,10 @@ Error EditorDebuggerNode::start() {
|
|||
}
|
||||
|
||||
void EditorDebuggerNode::stop() {
|
||||
if (server->is_listening()) {
|
||||
if (server.is_valid()) {
|
||||
server->stop();
|
||||
EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);
|
||||
server.unref();
|
||||
}
|
||||
// Also close all debugging sessions.
|
||||
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
|
||||
|
@ -231,9 +233,15 @@ void EditorDebuggerNode::_notification(int p_what) {
|
|||
break;
|
||||
}
|
||||
|
||||
if (p_what != NOTIFICATION_PROCESS || !server->is_listening())
|
||||
if (p_what != NOTIFICATION_PROCESS || !server.is_valid())
|
||||
return;
|
||||
|
||||
if (!server.is_valid() || !server->is_active()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
server->poll();
|
||||
|
||||
// Errors and warnings
|
||||
int error_count = 0;
|
||||
int warning_count = 0;
|
||||
|
@ -293,9 +301,8 @@ void EditorDebuggerNode::_notification(int p_what) {
|
|||
if (tabs->get_tab_count() <= 4) { // Max 4 debugging sessions active.
|
||||
debugger = _add_debugger();
|
||||
} else {
|
||||
// We already have too many sessions, disconnecting new clients to prevent it from hanging.
|
||||
// (Not keeping a reference to the connection will disconnect it)
|
||||
server->take_connection();
|
||||
// We already have too many sessions, disconnecting new clients to prevent them from hanging.
|
||||
server->take_connection()->close();
|
||||
return; // Can't add, stop here.
|
||||
}
|
||||
}
|
||||
|
@ -462,6 +469,26 @@ void EditorDebuggerNode::reload_scripts() {
|
|||
});
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::debug_next() {
|
||||
get_default_debugger()->debug_next();
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::debug_step() {
|
||||
get_default_debugger()->debug_step();
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::debug_break() {
|
||||
get_default_debugger()->debug_break();
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::debug_continue() {
|
||||
get_default_debugger()->debug_continue();
|
||||
}
|
||||
|
||||
String EditorDebuggerNode::get_var_value(const String &p_var) const {
|
||||
return get_default_debugger()->get_var_value(p_var);
|
||||
}
|
||||
|
||||
// LiveEdit/Inspector
|
||||
void EditorDebuggerNode::request_remote_tree() {
|
||||
get_current_debugger()->request_remote_tree();
|
||||
|
|
|
@ -31,17 +31,30 @@
|
|||
#ifndef EDITOR_DEBUGGER_NODE_H
|
||||
#define EDITOR_DEBUGGER_NODE_H
|
||||
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "editor/debugger/editor_debugger_server.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
|
||||
class Button;
|
||||
class EditorDebuggerTree;
|
||||
class EditorDebuggerRemoteObject;
|
||||
class MenuButton;
|
||||
class ScriptEditorDebugger;
|
||||
class TabContainer;
|
||||
|
||||
class EditorDebuggerNode : public MarginContainer {
|
||||
|
||||
GDCLASS(EditorDebuggerNode, MarginContainer);
|
||||
|
||||
public:
|
||||
enum CameraOverride {
|
||||
OVERRIDE_NONE,
|
||||
OVERRIDE_2D,
|
||||
OVERRIDE_3D_1, // 3D Viewport 1
|
||||
OVERRIDE_3D_2, // 3D Viewport 2
|
||||
OVERRIDE_3D_3, // 3D Viewport 3
|
||||
OVERRIDE_3D_4 // 3D Viewport 4
|
||||
};
|
||||
|
||||
private:
|
||||
enum Options {
|
||||
DEBUG_NEXT,
|
||||
|
@ -71,7 +84,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
Ref<TCP_Server> server = NULL;
|
||||
Ref<EditorDebuggerServer> server;
|
||||
TabContainer *tabs = NULL;
|
||||
Button *debugger_button = NULL;
|
||||
MenuButton *script_menu = NULL;
|
||||
|
@ -87,7 +100,7 @@ private:
|
|||
bool auto_switch_remote_scene_tree = false;
|
||||
bool debug_with_external_editor = false;
|
||||
bool hide_on_stop = true;
|
||||
ScriptEditorDebugger::CameraOverride camera_override = ScriptEditorDebugger::OVERRIDE_NONE;
|
||||
CameraOverride camera_override = OVERRIDE_NONE;
|
||||
Map<Breakpoint, bool> breakpoints;
|
||||
|
||||
ScriptEditorDebugger *_add_debugger();
|
||||
|
@ -130,10 +143,10 @@ public:
|
|||
ScriptEditorDebugger *get_default_debugger() const;
|
||||
ScriptEditorDebugger *get_debugger(int p_debugger) const;
|
||||
|
||||
void debug_next() { get_default_debugger()->debug_next(); }
|
||||
void debug_step() { get_default_debugger()->debug_step(); }
|
||||
void debug_break() { get_default_debugger()->debug_break(); }
|
||||
void debug_continue() { get_default_debugger()->debug_continue(); }
|
||||
void debug_next();
|
||||
void debug_step();
|
||||
void debug_break();
|
||||
void debug_continue();
|
||||
|
||||
void set_script_debug_button(MenuButton *p_button);
|
||||
|
||||
|
@ -141,7 +154,7 @@ public:
|
|||
debugger_button = p_button;
|
||||
}
|
||||
|
||||
String get_var_value(const String &p_var) const { return get_default_debugger()->get_var_value(p_var); }
|
||||
String get_var_value(const String &p_var) const;
|
||||
Ref<Script> get_dump_stack_script() const { return stack_script; } // Why do we need this?
|
||||
|
||||
bool get_debug_with_external_editor() { return debug_with_external_editor; }
|
||||
|
@ -167,8 +180,8 @@ public:
|
|||
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
|
||||
|
||||
// Camera
|
||||
void set_camera_override(ScriptEditorDebugger::CameraOverride p_override) { camera_override = p_override; }
|
||||
ScriptEditorDebugger::CameraOverride get_camera_override() { return camera_override; }
|
||||
void set_camera_override(CameraOverride p_override) { camera_override = p_override; }
|
||||
CameraOverride get_camera_override() { return camera_override; }
|
||||
|
||||
Error start();
|
||||
|
||||
|
|
90
editor/debugger/editor_debugger_server.cpp
Normal file
90
editor/debugger/editor_debugger_server.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_server.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 "editor_debugger_server.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
||||
class EditorDebuggerServerTCP : public EditorDebuggerServer {
|
||||
|
||||
private:
|
||||
Ref<TCP_Server> server;
|
||||
|
||||
public:
|
||||
virtual void poll() {}
|
||||
virtual Error start();
|
||||
virtual void stop();
|
||||
virtual bool is_active() const;
|
||||
virtual bool is_connection_available() const;
|
||||
virtual Ref<RemoteDebuggerPeer> take_connection();
|
||||
|
||||
EditorDebuggerServerTCP();
|
||||
};
|
||||
|
||||
EditorDebuggerServerTCP::EditorDebuggerServerTCP() {
|
||||
server.instance();
|
||||
}
|
||||
|
||||
Error EditorDebuggerServerTCP::start() {
|
||||
int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
|
||||
const Error err = server->listen(remote_port);
|
||||
if (err != OK) {
|
||||
EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR);
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void EditorDebuggerServerTCP::stop() {
|
||||
server->stop();
|
||||
}
|
||||
|
||||
bool EditorDebuggerServerTCP::is_active() const {
|
||||
return server->is_listening();
|
||||
}
|
||||
|
||||
bool EditorDebuggerServerTCP::is_connection_available() const {
|
||||
return server->is_listening() && server->is_connection_available();
|
||||
}
|
||||
|
||||
Ref<RemoteDebuggerPeer> EditorDebuggerServerTCP::take_connection() {
|
||||
ERR_FAIL_COND_V(!is_connection_available(), Ref<RemoteDebuggerPeer>());
|
||||
return memnew(RemoteDebuggerPeerTCP(server->take_connection()));
|
||||
}
|
||||
|
||||
EditorDebuggerServer *EditorDebuggerServer::create_default() {
|
||||
return memnew(EditorDebuggerServerTCP);
|
||||
}
|
49
editor/debugger/editor_debugger_server.h
Normal file
49
editor/debugger/editor_debugger_server.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*************************************************************************/
|
||||
/* editor_debugger_server.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 EDITOR_DEBUGGER_CONNECTION_H
|
||||
#define EDITOR_DEBUGGER_CONNECTION_H
|
||||
|
||||
#include "core/debugger/remote_debugger_peer.h"
|
||||
#include "core/reference.h"
|
||||
|
||||
class EditorDebuggerServer : public Reference {
|
||||
|
||||
public:
|
||||
static EditorDebuggerServer *create_default();
|
||||
virtual void poll() = 0;
|
||||
virtual Error start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual bool is_active() const = 0;
|
||||
virtual bool is_connection_available() const = 0;
|
||||
virtual Ref<RemoteDebuggerPeer> take_connection() = 0;
|
||||
};
|
||||
|
||||
#endif // EDITOR_DEBUGGER_CONNECTION_H
|
|
@ -31,8 +31,8 @@
|
|||
#include "editor_network_profiler.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "editor_scale.h"
|
||||
#include "editor_settings.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
||||
void EditorNetworkProfiler::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));
|
||||
|
@ -58,7 +58,7 @@ void EditorNetworkProfiler::_update_frame() {
|
|||
|
||||
TreeItem *root = counters_display->create_item();
|
||||
|
||||
for (Map<ObjectID, MultiplayerAPI::ProfilingInfo>::Element *E = nodes_data.front(); E; E = E->next()) {
|
||||
for (Map<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo>::Element *E = nodes_data.front(); E; E = E->next()) {
|
||||
|
||||
TreeItem *node = counters_display->create_item(root);
|
||||
|
||||
|
@ -95,7 +95,7 @@ void EditorNetworkProfiler::_clear_pressed() {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorNetworkProfiler::add_node_frame_data(const MultiplayerAPI::ProfilingInfo p_frame) {
|
||||
void EditorNetworkProfiler::add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame) {
|
||||
|
||||
if (!nodes_data.has(p_frame.node)) {
|
||||
nodes_data.insert(p_frame.node, p_frame);
|
|
@ -31,6 +31,7 @@
|
|||
#ifndef EDITORNETWORKPROFILER_H
|
||||
#define EDITORNETWORKPROFILER_H
|
||||
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/label.h"
|
||||
|
@ -50,7 +51,7 @@ private:
|
|||
|
||||
Timer *frame_delay;
|
||||
|
||||
Map<ObjectID, MultiplayerAPI::ProfilingInfo> nodes_data;
|
||||
Map<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> nodes_data;
|
||||
|
||||
void _update_frame();
|
||||
|
||||
|
@ -62,7 +63,7 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void add_node_frame_data(const MultiplayerAPI::ProfilingInfo p_frame);
|
||||
void add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame);
|
||||
void set_bandwidth(int p_incoming, int p_outgoing);
|
||||
bool is_profiling();
|
||||
|
|
@ -31,8 +31,8 @@
|
|||
#include "editor_profiler.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "editor_scale.h"
|
||||
#include "editor_settings.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
||||
void EditorProfiler::_make_metric_ptrs(Metric &m) {
|
||||
|
|
@ -31,8 +31,8 @@
|
|||
#include "editor_visual_profiler.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "editor_scale.h"
|
||||
#include "editor_settings.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
||||
void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) {
|
||||
|
|
@ -30,21 +30,22 @@
|
|||
|
||||
#include "script_editor_debugger.h"
|
||||
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "core/ustring.h"
|
||||
#include "editor/debugger/editor_network_profiler.h"
|
||||
#include "editor/debugger/editor_profiler.h"
|
||||
#include "editor/debugger/editor_visual_profiler.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_network_profiler.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_profiler.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_visual_profiler.h"
|
||||
#include "editor/plugins/canvas_item_editor_plugin.h"
|
||||
#include "editor/plugins/spatial_editor_plugin.h"
|
||||
#include "editor/property_editor.h"
|
||||
#include "main/performance.h"
|
||||
#include "scene/3d/camera.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/label.h"
|
||||
|
@ -58,12 +59,14 @@
|
|||
#include "scene/gui/tree.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
using CameraOverride = EditorDebuggerNode::CameraOverride;
|
||||
|
||||
void ScriptEditorDebugger::_put_msg(String p_message, Array p_data) {
|
||||
if (is_session_active()) {
|
||||
Array msg;
|
||||
msg.push_back(p_message);
|
||||
msg.push_back(p_data);
|
||||
ppeer->put_var(msg);
|
||||
peer->put_message(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +144,7 @@ void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) {
|
|||
Array msg;
|
||||
msg.push_back(p_id);
|
||||
msg.push_back(p_file);
|
||||
_put_msg("save_node", msg);
|
||||
_put_msg("scene:save_node", msg);
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_file_selected(const String &p_file) {
|
||||
|
@ -183,7 +186,7 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) {
|
|||
|
||||
void ScriptEditorDebugger::request_remote_tree() {
|
||||
|
||||
_put_msg("request_scene_tree", Array());
|
||||
_put_msg("scene:request_scene_tree", Array());
|
||||
}
|
||||
|
||||
const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {
|
||||
|
@ -196,7 +199,7 @@ void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String
|
|||
msg.push_back(p_obj_id);
|
||||
msg.push_back(p_prop);
|
||||
msg.push_back(p_value);
|
||||
_put_msg("set_object_property", msg);
|
||||
_put_msg("scene:set_object_property", msg);
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::request_remote_object(ObjectID p_obj_id) {
|
||||
|
@ -204,7 +207,7 @@ void ScriptEditorDebugger::request_remote_object(ObjectID p_obj_id) {
|
|||
ERR_FAIL_COND(p_obj_id.is_null());
|
||||
Array msg;
|
||||
msg.push_back(p_obj_id);
|
||||
_put_msg("inspect_object", msg);
|
||||
_put_msg("scene:inspect_object", msg);
|
||||
}
|
||||
|
||||
Object *ScriptEditorDebugger::get_remote_object(ObjectID p_id) {
|
||||
|
@ -226,7 +229,7 @@ void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const
|
|||
|
||||
void ScriptEditorDebugger::_video_mem_request() {
|
||||
|
||||
_put_msg("request_video_mem", Array());
|
||||
_put_msg("core:memory", Array());
|
||||
}
|
||||
|
||||
Size2 ScriptEditorDebugger::get_minimum_size() const {
|
||||
|
@ -267,36 +270,36 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
emit_signal("breaked", false, false);
|
||||
profiler->set_enabled(true);
|
||||
profiler->disable_seeking();
|
||||
} else if (p_msg == "message:set_pid") {
|
||||
} else if (p_msg == "set_pid") {
|
||||
|
||||
ERR_FAIL_COND(p_data.size() < 1);
|
||||
remote_pid = p_data[0];
|
||||
} else if (p_msg == "message:click_ctrl") {
|
||||
} else if (p_msg == "scene:click_ctrl") {
|
||||
|
||||
ERR_FAIL_COND(p_data.size() < 2);
|
||||
clicked_ctrl->set_text(p_data[0]);
|
||||
clicked_ctrl_type->set_text(p_data[1]);
|
||||
} else if (p_msg == "message:scene_tree") {
|
||||
} else if (p_msg == "scene:scene_tree") {
|
||||
|
||||
scene_tree->nodes.clear();
|
||||
scene_tree->deserialize(p_data);
|
||||
emit_signal("remote_tree_updated");
|
||||
_update_buttons_state();
|
||||
} else if (p_msg == "message:inspect_object") {
|
||||
} else if (p_msg == "scene:inspect_object") {
|
||||
|
||||
ObjectID id = inspector->add_object(p_data);
|
||||
if (id.is_valid())
|
||||
emit_signal("remote_object_updated", id);
|
||||
} else if (p_msg == "message:video_mem") {
|
||||
} else if (p_msg == "memory:usage") {
|
||||
|
||||
vmem_tree->clear();
|
||||
TreeItem *root = vmem_tree->create_item();
|
||||
ScriptDebuggerRemote::ResourceUsage usage;
|
||||
DebuggerMarshalls::ResourceUsage usage;
|
||||
usage.deserialize(p_data);
|
||||
|
||||
int total = 0;
|
||||
|
||||
for (List<ScriptDebuggerRemote::ResourceInfo>::Element *E = usage.infos.front(); E; E = E->next()) {
|
||||
for (List<DebuggerMarshalls::ResourceInfo>::Element *E = usage.infos.front(); E; E = E->next()) {
|
||||
|
||||
TreeItem *it = vmem_tree->create_item(root);
|
||||
String type = E->get().type;
|
||||
|
@ -316,7 +319,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
|
||||
} else if (p_msg == "stack_dump") {
|
||||
|
||||
ScriptDebuggerRemote::ScriptStackDump stack;
|
||||
DebuggerMarshalls::ScriptStackDump stack;
|
||||
stack.deserialize(p_data);
|
||||
|
||||
stack_dump->clear();
|
||||
|
@ -349,10 +352,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
|
||||
} else if (p_msg == "output") {
|
||||
ERR_FAIL_COND(p_data.size() < 1);
|
||||
String t = p_data[0];
|
||||
EditorNode::get_log()->add_message(t);
|
||||
|
||||
} else if (p_msg == "performance") {
|
||||
ERR_FAIL_COND(p_data[0].get_type() != Variant::PACKED_STRING_ARRAY);
|
||||
Vector<String> strings = p_data[0];
|
||||
EditorNode::get_log()->add_message(String("\n").join(strings));
|
||||
} else if (p_msg == "performance:profile_frame") {
|
||||
Vector<float> p;
|
||||
p.resize(p_data.size());
|
||||
for (int i = 0; i < p_data.size(); i++) {
|
||||
|
@ -385,36 +388,28 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
perf_history.push_front(p);
|
||||
perf_draw->update();
|
||||
|
||||
} else if (p_msg == "visual_profile") {
|
||||
// TODO check me.
|
||||
uint64_t frame = p_data[0];
|
||||
Vector<String> names = p_data[1];
|
||||
Vector<real_t> values = p_data[2];
|
||||
} else if (p_msg == "visual:profile_frame") {
|
||||
DebuggerMarshalls::VisualProfilerFrame frame;
|
||||
frame.deserialize(p_data);
|
||||
|
||||
EditorVisualProfiler::Metric metric;
|
||||
metric.areas.resize(names.size());
|
||||
metric.frame_number = frame;
|
||||
metric.areas.resize(frame.areas.size());
|
||||
metric.frame_number = frame.frame_number;
|
||||
metric.valid = true;
|
||||
|
||||
{
|
||||
EditorVisualProfiler::Metric::Area *areas_ptr = metric.areas.ptrw();
|
||||
int metric_count = names.size();
|
||||
|
||||
const String *rs = names.ptr();
|
||||
const real_t *rr = values.ptr();
|
||||
|
||||
for (int i = 0; i < metric_count; i++) {
|
||||
|
||||
areas_ptr[i].name = rs[i];
|
||||
areas_ptr[i].cpu_time = rr[i * 2 + 0];
|
||||
areas_ptr[i].gpu_time = rr[i * 2 + 1];
|
||||
for (int i = 0; i < frame.areas.size(); i++) {
|
||||
areas_ptr[i].name = frame.areas[i].name;
|
||||
areas_ptr[i].cpu_time = frame.areas[i].cpu_msec;
|
||||
areas_ptr[i].gpu_time = frame.areas[i].gpu_msec;
|
||||
}
|
||||
}
|
||||
visual_profiler->add_frame_metric(metric);
|
||||
|
||||
} else if (p_msg == "error") {
|
||||
|
||||
ScriptDebuggerRemote::OutputError oe;
|
||||
DebuggerMarshalls::OutputError oe;
|
||||
ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message");
|
||||
|
||||
// Format time.
|
||||
|
@ -520,15 +515,15 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
else
|
||||
error_count++;
|
||||
|
||||
} else if (p_msg == "profile_sig") {
|
||||
} else if (p_msg == "servers:function_signature") {
|
||||
// Cache a profiler signature.
|
||||
ScriptDebuggerRemote::ProfilerSignature sig;
|
||||
DebuggerMarshalls::ScriptFunctionSignature sig;
|
||||
sig.deserialize(p_data);
|
||||
profiler_signature[sig.id] = sig.name;
|
||||
|
||||
} else if (p_msg == "profile_frame" || p_msg == "profile_total") {
|
||||
} else if (p_msg == "servers:profile_frame" || p_msg == "servers:profile_total") {
|
||||
EditorProfiler::Metric metric;
|
||||
ScriptDebuggerRemote::ProfilerFrame frame;
|
||||
DebuggerMarshalls::ServersProfilerFrame frame;
|
||||
frame.deserialize(p_data);
|
||||
metric.valid = true;
|
||||
metric.frame_number = frame.frame_number;
|
||||
|
@ -536,10 +531,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
metric.idle_time = frame.idle_time;
|
||||
metric.physics_time = frame.physics_time;
|
||||
metric.physics_frame_time = frame.physics_frame_time;
|
||||
int frame_data_amount = frame.frames_data.size();
|
||||
int frame_function_amount = frame.frame_functions.size();
|
||||
|
||||
if (frame_data_amount) {
|
||||
if (frame.servers.size()) {
|
||||
EditorProfiler::Metric::Category frame_time;
|
||||
frame_time.signature = "category_frame_time";
|
||||
frame_time.name = "Frame Time";
|
||||
|
@ -573,42 +566,42 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
metric.categories.push_back(frame_time);
|
||||
}
|
||||
|
||||
for (int i = 0; i < frame_data_amount; i++) {
|
||||
for (int i = 0; i < frame.servers.size(); i++) {
|
||||
|
||||
const DebuggerMarshalls::ServerInfo &srv = frame.servers[i];
|
||||
EditorProfiler::Metric::Category c;
|
||||
String name = frame.frames_data[i].name;
|
||||
Array values = frame.frames_data[i].data;
|
||||
const String name = srv.name;
|
||||
c.name = name.capitalize();
|
||||
c.items.resize(values.size() / 2);
|
||||
c.items.resize(srv.functions.size());
|
||||
c.total_time = 0;
|
||||
c.signature = "categ::" + name;
|
||||
for (int j = 0; j < values.size(); j += 2) {
|
||||
for (int j = 0; j < srv.functions.size(); j++) {
|
||||
|
||||
EditorProfiler::Metric::Category::Item item;
|
||||
item.calls = 1;
|
||||
item.line = 0;
|
||||
item.name = values[j];
|
||||
item.self = values[j + 1];
|
||||
item.name = srv.functions[j].name;
|
||||
item.self = srv.functions[j].time;
|
||||
item.total = item.self;
|
||||
item.signature = "categ::" + name + "::" + item.name;
|
||||
item.name = item.name.capitalize();
|
||||
c.total_time += item.total;
|
||||
c.items.write[j / 2] = item;
|
||||
c.items.write[j] = item;
|
||||
}
|
||||
metric.categories.push_back(c);
|
||||
}
|
||||
|
||||
EditorProfiler::Metric::Category funcs;
|
||||
funcs.total_time = frame.script_time;
|
||||
funcs.items.resize(frame_function_amount);
|
||||
funcs.items.resize(frame.script_functions.size());
|
||||
funcs.name = "Script Functions";
|
||||
funcs.signature = "script_functions";
|
||||
for (int i = 0; i < frame_function_amount; i++) {
|
||||
for (int i = 0; i < frame.script_functions.size(); i++) {
|
||||
|
||||
int signature = frame.frame_functions[i].sig_id;
|
||||
int calls = frame.frame_functions[i].call_count;
|
||||
float total = frame.frame_functions[i].total_time;
|
||||
float self = frame.frame_functions[i].self_time;
|
||||
int signature = frame.script_functions[i].sig_id;
|
||||
int calls = frame.script_functions[i].call_count;
|
||||
float total = frame.script_functions[i].total_time;
|
||||
float self = frame.script_functions[i].self_time;
|
||||
|
||||
EditorProfiler::Metric::Category::Item item;
|
||||
if (profiler_signature.has(signature)) {
|
||||
|
@ -639,24 +632,28 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
|
|||
|
||||
metric.categories.push_back(funcs);
|
||||
|
||||
if (p_msg == "profile_frame")
|
||||
if (p_msg == "servers:profile_frame")
|
||||
profiler->add_frame_metric(metric, false);
|
||||
else
|
||||
profiler->add_frame_metric(metric, true);
|
||||
|
||||
} else if (p_msg == "network_profile") {
|
||||
ScriptDebuggerRemote::NetworkProfilerFrame frame;
|
||||
} else if (p_msg == "network:profile_frame") {
|
||||
DebuggerMarshalls::NetworkProfilerFrame frame;
|
||||
frame.deserialize(p_data);
|
||||
for (int i = 0; i < frame.infos.size(); i++) {
|
||||
network_profiler->add_node_frame_data(frame.infos[i]);
|
||||
}
|
||||
} else if (p_msg == "network_bandwidth") {
|
||||
|
||||
} else if (p_msg == "network:bandwidth") {
|
||||
ERR_FAIL_COND(p_data.size() < 2);
|
||||
network_profiler->set_bandwidth(p_data[0], p_data[1]);
|
||||
} else if (p_msg == "kill_me") {
|
||||
|
||||
} else if (p_msg == "request_quit") {
|
||||
emit_signal("stop_requested");
|
||||
_stop_and_notify();
|
||||
|
||||
} else {
|
||||
WARN_PRINT("unknown message " + p_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -776,7 +773,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||
|
||||
if (is_session_active()) {
|
||||
|
||||
if (camera_override == OVERRIDE_2D) {
|
||||
if (camera_override == CameraOverride::OVERRIDE_2D) {
|
||||
CanvasItemEditor *editor = CanvasItemEditor::get_singleton();
|
||||
|
||||
Dictionary state = editor->get_state();
|
||||
|
@ -789,10 +786,10 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||
|
||||
Array msg;
|
||||
msg.push_back(transform);
|
||||
_put_msg("override_camera_2D:transform", msg);
|
||||
_put_msg("scene:override_camera_2D:transform", msg);
|
||||
|
||||
} else if (camera_override >= OVERRIDE_3D_1) {
|
||||
int viewport_idx = camera_override - OVERRIDE_3D_1;
|
||||
} else if (camera_override >= CameraOverride::OVERRIDE_3D_1) {
|
||||
int viewport_idx = camera_override - CameraOverride::OVERRIDE_3D_1;
|
||||
SpatialEditorViewport *viewport = SpatialEditor::get_singleton()->get_editor_viewport(viewport_idx);
|
||||
Camera *const cam = viewport->get_camera();
|
||||
|
||||
|
@ -807,34 +804,15 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||
}
|
||||
msg.push_back(cam->get_znear());
|
||||
msg.push_back(cam->get_zfar());
|
||||
_put_msg("override_camera_3D:transform", msg);
|
||||
_put_msg("scene:override_camera_3D:transform", msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_session_active()) {
|
||||
_stop_and_notify();
|
||||
break;
|
||||
};
|
||||
|
||||
if (ppeer->get_available_packet_count() <= 0) {
|
||||
break;
|
||||
};
|
||||
|
||||
const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20;
|
||||
|
||||
while (ppeer->get_available_packet_count() > 0) {
|
||||
while (peer->has_message()) {
|
||||
|
||||
Variant cmd;
|
||||
Error ret = ppeer->get_var(cmd);
|
||||
if (ret != OK) {
|
||||
_stop_and_notify();
|
||||
ERR_FAIL_MSG("Error decoding variant from peer");
|
||||
}
|
||||
if (cmd.get_type() != Variant::ARRAY) {
|
||||
_stop_and_notify();
|
||||
ERR_FAIL_MSG("Invalid message format received from peer");
|
||||
}
|
||||
Array arr = cmd;
|
||||
Array arr = peer->get_message();
|
||||
if (arr.size() != 2 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::ARRAY) {
|
||||
_stop_and_notify();
|
||||
ERR_FAIL_MSG("Invalid message format received from peer");
|
||||
|
@ -844,6 +822,10 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||
if (OS::get_singleton()->get_ticks_msec() > until)
|
||||
break;
|
||||
}
|
||||
if (!is_session_active()) {
|
||||
_stop_and_notify();
|
||||
break;
|
||||
};
|
||||
} break;
|
||||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
|
||||
|
@ -875,14 +857,14 @@ void ScriptEditorDebugger::_clear_execution() {
|
|||
inspector->clear_stack_variables();
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::start(Ref<StreamPeerTCP> p_connection) {
|
||||
void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
|
||||
|
||||
error_count = 0;
|
||||
warning_count = 0;
|
||||
stop();
|
||||
|
||||
connection = p_connection;
|
||||
ppeer->set_stream_peer(connection);
|
||||
peer = p_peer;
|
||||
ERR_FAIL_COND(p_peer.is_null());
|
||||
|
||||
perf_history.clear();
|
||||
for (int i = 0; i < Performance::MONITOR_MAX; i++) {
|
||||
|
@ -893,19 +875,11 @@ void ScriptEditorDebugger::start(Ref<StreamPeerTCP> p_connection) {
|
|||
set_process(true);
|
||||
breaked = false;
|
||||
can_debug = true;
|
||||
camera_override = OVERRIDE_NONE;
|
||||
camera_override = CameraOverride::OVERRIDE_NONE;
|
||||
|
||||
tabs->set_current_tab(0);
|
||||
_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
|
||||
_update_buttons_state();
|
||||
|
||||
if (profiler->is_profiling()) {
|
||||
_profiler_activate(true);
|
||||
}
|
||||
|
||||
if (network_profiler->is_profiling()) {
|
||||
_network_profiler_activate(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_update_buttons_state() {
|
||||
|
@ -936,10 +910,10 @@ void ScriptEditorDebugger::stop() {
|
|||
_clear_execution();
|
||||
|
||||
inspector->clear_cache();
|
||||
ppeer->set_stream_peer(Ref<StreamPeer>());
|
||||
|
||||
if (connection.is_valid()) {
|
||||
connection.unref();
|
||||
if (peer.is_valid()) {
|
||||
peer->close();
|
||||
peer.unref();
|
||||
reason->set_text("");
|
||||
reason->set_tooltip("");
|
||||
}
|
||||
|
@ -952,49 +926,31 @@ void ScriptEditorDebugger::stop() {
|
|||
_update_buttons_state();
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_profiler_activate(bool p_enable) {
|
||||
void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {
|
||||
|
||||
if (p_enable) {
|
||||
profiler_signature.clear();
|
||||
Array msg;
|
||||
int max_funcs = EditorSettings::get_singleton()->get("debugger/profiler_frame_max_functions");
|
||||
max_funcs = CLAMP(max_funcs, 16, 512);
|
||||
msg.push_back(max_funcs);
|
||||
_put_msg("start_profiling", msg);
|
||||
print_verbose("Starting profiling.");
|
||||
|
||||
} else {
|
||||
_put_msg("stop_profiling", Array());
|
||||
print_verbose("Ending profiling.");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_visual_profiler_activate(bool p_enable) {
|
||||
|
||||
if (!connection.is_valid())
|
||||
return;
|
||||
|
||||
if (p_enable) {
|
||||
profiler_signature.clear();
|
||||
_put_msg("start_visual_profiling", Array());
|
||||
print_verbose("Starting visual profiling.");
|
||||
|
||||
} else {
|
||||
_put_msg("stop_visual_profiling", Array());
|
||||
print_verbose("Ending visual profiling.");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_network_profiler_activate(bool p_enable) {
|
||||
|
||||
if (p_enable) {
|
||||
profiler_signature.clear();
|
||||
_put_msg("start_network_profiling", Array());
|
||||
print_verbose("Starting network profiling.");
|
||||
|
||||
} else {
|
||||
_put_msg("stop_network_profiling", Array());
|
||||
print_verbose("Ending network profiling.");
|
||||
Array data;
|
||||
data.push_back(p_enable);
|
||||
switch (p_type) {
|
||||
case PROFILER_NETWORK:
|
||||
_put_msg("profiler:network", data);
|
||||
break;
|
||||
case PROFILER_VISUAL:
|
||||
_put_msg("profiler:visual", data);
|
||||
break;
|
||||
case PROFILER_SCRIPTS_SERVERS:
|
||||
if (p_enable) {
|
||||
// Clear old script signatures. (should we move all this into the profiler?)
|
||||
profiler_signature.clear();
|
||||
// Add max funcs options to request.
|
||||
Array opts;
|
||||
int max_funcs = EditorSettings::get_singleton()->get("debugger/profiler_frame_max_functions");
|
||||
opts.push_back(CLAMP(max_funcs, 16, 512));
|
||||
data.push_back(opts);
|
||||
}
|
||||
_put_msg("profiler:servers", data);
|
||||
break;
|
||||
default:
|
||||
ERR_FAIL_MSG("Invalid profiler type");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1045,7 +1001,7 @@ int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
|
|||
Array msg;
|
||||
msg.push_back(p_path);
|
||||
msg.push_back(last_path_id);
|
||||
_put_msg("live_node_path", msg);
|
||||
_put_msg("scene:live_node_path", msg);
|
||||
|
||||
return last_path_id;
|
||||
}
|
||||
|
@ -1063,7 +1019,7 @@ int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) {
|
|||
Array msg;
|
||||
msg.push_back(p_path);
|
||||
msg.push_back(last_path_id);
|
||||
_put_msg("live_res_path", msg);
|
||||
_put_msg("scene:live_res_path", msg);
|
||||
|
||||
return last_path_id;
|
||||
}
|
||||
|
@ -1095,7 +1051,7 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n
|
|||
//no pointers, sorry
|
||||
msg.push_back(*argptr[i]);
|
||||
}
|
||||
_put_msg("live_node_call", msg);
|
||||
_put_msg("scene:live_node_call", msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1114,7 +1070,7 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n
|
|||
//no pointers, sorry
|
||||
msg.push_back(*argptr[i]);
|
||||
}
|
||||
_put_msg("live_res_call", msg);
|
||||
_put_msg("scene:live_res_call", msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1140,7 +1096,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
|
|||
msg.push_back(pathid);
|
||||
msg.push_back(p_property);
|
||||
msg.push_back(res->get_path());
|
||||
_put_msg("live_node_prop_res", msg);
|
||||
_put_msg("scene:live_node_prop_res", msg);
|
||||
}
|
||||
} else {
|
||||
|
||||
|
@ -1148,7 +1104,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
|
|||
msg.push_back(pathid);
|
||||
msg.push_back(p_property);
|
||||
msg.push_back(p_value);
|
||||
_put_msg("live_node_prop", msg);
|
||||
_put_msg("scene:live_node_prop", msg);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1169,7 +1125,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
|
|||
msg.push_back(pathid);
|
||||
msg.push_back(p_property);
|
||||
msg.push_back(res2->get_path());
|
||||
_put_msg("live_res_prop_res", msg);
|
||||
_put_msg("scene:live_res_prop_res", msg);
|
||||
}
|
||||
} else {
|
||||
|
||||
|
@ -1177,7 +1133,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
|
|||
msg.push_back(pathid);
|
||||
msg.push_back(p_property);
|
||||
msg.push_back(p_value);
|
||||
_put_msg("live_res_prop", msg);
|
||||
_put_msg("scene:live_res_prop", msg);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1255,7 +1211,7 @@ void ScriptEditorDebugger::update_live_edit_root() {
|
|||
msg.push_back(editor->get_edited_scene()->get_filename());
|
||||
else
|
||||
msg.push_back("");
|
||||
_put_msg("live_set_root", msg);
|
||||
_put_msg("scene:live_set_root", msg);
|
||||
live_edit_root->set_text(np);
|
||||
}
|
||||
|
||||
|
@ -1266,7 +1222,7 @@ void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, cons
|
|||
msg.push_back(p_parent);
|
||||
msg.push_back(p_type);
|
||||
msg.push_back(p_name);
|
||||
_put_msg("live_create_node", msg);
|
||||
_put_msg("scene:live_create_node", msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1277,7 +1233,7 @@ void ScriptEditorDebugger::live_debug_instance_node(const NodePath &p_parent, co
|
|||
msg.push_back(p_parent);
|
||||
msg.push_back(p_path);
|
||||
msg.push_back(p_name);
|
||||
_put_msg("live_instance_node", msg);
|
||||
_put_msg("scene:live_instance_node", msg);
|
||||
}
|
||||
}
|
||||
void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {
|
||||
|
@ -1285,7 +1241,7 @@ void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {
|
|||
if (live_debug) {
|
||||
Array msg;
|
||||
msg.push_back(p_at);
|
||||
_put_msg("live_remove_node", msg);
|
||||
_put_msg("scene:live_remove_node", msg);
|
||||
}
|
||||
}
|
||||
void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {
|
||||
|
@ -1294,7 +1250,7 @@ void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at,
|
|||
Array msg;
|
||||
msg.push_back(p_at);
|
||||
msg.push_back(p_keep_id);
|
||||
_put_msg("live_remove_and_keep_node", msg);
|
||||
_put_msg("scene:live_remove_and_keep_node", msg);
|
||||
}
|
||||
}
|
||||
void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
|
||||
|
@ -1304,7 +1260,7 @@ void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath
|
|||
msg.push_back(p_id);
|
||||
msg.push_back(p_at);
|
||||
msg.push_back(p_at_pos);
|
||||
_put_msg("live_restore_node", msg);
|
||||
_put_msg("scene:live_restore_node", msg);
|
||||
}
|
||||
}
|
||||
void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {
|
||||
|
@ -1313,7 +1269,7 @@ void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const
|
|||
Array msg;
|
||||
msg.push_back(p_at);
|
||||
msg.push_back(p_new_name);
|
||||
_put_msg("live_duplicate_node", msg);
|
||||
_put_msg("scene:live_duplicate_node", msg);
|
||||
}
|
||||
}
|
||||
void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
|
||||
|
@ -1324,32 +1280,32 @@ void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const
|
|||
msg.push_back(p_new_place);
|
||||
msg.push_back(p_new_name);
|
||||
msg.push_back(p_at_pos);
|
||||
_put_msg("live_reparent_node", msg);
|
||||
_put_msg("scene:live_reparent_node", msg);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptEditorDebugger::CameraOverride ScriptEditorDebugger::get_camera_override() const {
|
||||
CameraOverride ScriptEditorDebugger::get_camera_override() const {
|
||||
return camera_override;
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
|
||||
|
||||
if (p_override == OVERRIDE_2D && camera_override != OVERRIDE_2D) {
|
||||
if (p_override == CameraOverride::OVERRIDE_2D && camera_override != CameraOverride::OVERRIDE_2D) {
|
||||
Array msg;
|
||||
msg.push_back(true);
|
||||
_put_msg("override_camera_2D:set", msg);
|
||||
} else if (p_override != OVERRIDE_2D && camera_override == OVERRIDE_2D) {
|
||||
_put_msg("scene:override_camera_2D:set", msg);
|
||||
} else if (p_override != CameraOverride::OVERRIDE_2D && camera_override == CameraOverride::OVERRIDE_2D) {
|
||||
Array msg;
|
||||
msg.push_back(false);
|
||||
_put_msg("override_camera_2D:set", msg);
|
||||
} else if (p_override >= OVERRIDE_3D_1 && camera_override < OVERRIDE_3D_1) {
|
||||
_put_msg("scene:override_camera_2D:set", msg);
|
||||
} else if (p_override >= CameraOverride::OVERRIDE_3D_1 && camera_override < CameraOverride::OVERRIDE_3D_1) {
|
||||
Array msg;
|
||||
msg.push_back(true);
|
||||
_put_msg("override_camera_3D:set", msg);
|
||||
} else if (p_override < OVERRIDE_3D_1 && camera_override >= OVERRIDE_3D_1) {
|
||||
_put_msg("scene:override_camera_3D:set", msg);
|
||||
} else if (p_override < CameraOverride::OVERRIDE_3D_1 && camera_override >= CameraOverride::OVERRIDE_3D_1) {
|
||||
Array msg;
|
||||
msg.push_back(false);
|
||||
_put_msg("override_camera_3D:set", msg);
|
||||
_put_msg("scene:override_camera_3D:set", msg);
|
||||
}
|
||||
|
||||
camera_override = p_override;
|
||||
|
@ -1423,7 +1379,6 @@ void ScriptEditorDebugger::_clear_errors_list() {
|
|||
error_tree->clear();
|
||||
error_count = 0;
|
||||
warning_count = 0;
|
||||
update_tabs();
|
||||
}
|
||||
|
||||
// Right click on specific file(s) or folder(s).
|
||||
|
@ -1502,8 +1457,6 @@ void ScriptEditorDebugger::_bind_methods() {
|
|||
|
||||
ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
||||
|
||||
ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
|
||||
ppeer->set_input_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be enough, minus 4 bytes for separator.
|
||||
editor = p_editor;
|
||||
|
||||
tabs = memnew(TabContainer);
|
||||
|
@ -1655,7 +1608,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
|||
profiler = memnew(EditorProfiler);
|
||||
profiler->set_name(TTR("Profiler"));
|
||||
tabs->add_child(profiler);
|
||||
profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate));
|
||||
profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_SCRIPTS_SERVERS));
|
||||
profiler->connect("break_request", callable_mp(this, &ScriptEditorDebugger::_profiler_seeked));
|
||||
}
|
||||
|
||||
|
@ -1663,14 +1616,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
|||
visual_profiler = memnew(EditorVisualProfiler);
|
||||
visual_profiler->set_name(TTR("Visual Profiler"));
|
||||
tabs->add_child(visual_profiler);
|
||||
visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_visual_profiler_activate));
|
||||
visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_VISUAL));
|
||||
}
|
||||
|
||||
{ //network profiler
|
||||
network_profiler = memnew(EditorNetworkProfiler);
|
||||
network_profiler->set_name(TTR("Network Profiler"));
|
||||
tabs->add_child(network_profiler);
|
||||
network_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_network_profiler_activate));
|
||||
network_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_NETWORK));
|
||||
}
|
||||
|
||||
{ //monitors
|
||||
|
@ -1824,7 +1777,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
|||
add_child(msgdialog);
|
||||
|
||||
live_debug = true;
|
||||
camera_override = OVERRIDE_NONE;
|
||||
camera_override = CameraOverride::OVERRIDE_NONE;
|
||||
last_path_id = false;
|
||||
error_count = 0;
|
||||
warning_count = 0;
|
||||
|
@ -1833,6 +1786,9 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
|||
|
||||
ScriptEditorDebugger::~ScriptEditorDebugger() {
|
||||
|
||||
ppeer->set_stream_peer(Ref<StreamPeer>());
|
||||
if (peer.is_valid()) {
|
||||
peer->close();
|
||||
peer.unref();
|
||||
}
|
||||
memdelete(scene_tree);
|
||||
}
|
||||
|
|
|
@ -31,14 +31,13 @@
|
|||
#ifndef SCRIPT_EDITOR_DEBUGGER_H
|
||||
#define SCRIPT_EDITOR_DEBUGGER_H
|
||||
|
||||
#include "core/io/packet_peer.h"
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/debugger/editor_debugger_inspector.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/property_editor.h"
|
||||
#include "scene/3d/camera.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/debugger/editor_debugger_server.h"
|
||||
#include "editor/editor_file_dialog.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
|
||||
class Tree;
|
||||
class EditorNode;
|
||||
|
@ -61,16 +60,6 @@ class ScriptEditorDebugger : public MarginContainer {
|
|||
|
||||
friend class EditorDebuggerNode;
|
||||
|
||||
public:
|
||||
enum CameraOverride {
|
||||
OVERRIDE_NONE,
|
||||
OVERRIDE_2D,
|
||||
OVERRIDE_3D_1, // 3D Viewport 1
|
||||
OVERRIDE_3D_2, // 3D Viewport 2
|
||||
OVERRIDE_3D_3, // 3D Viewport 3
|
||||
OVERRIDE_3D_4 // 3D Viewport 4
|
||||
};
|
||||
|
||||
private:
|
||||
enum MessageType {
|
||||
MESSAGE_ERROR,
|
||||
|
@ -78,6 +67,12 @@ private:
|
|||
MESSAGE_SUCCESS,
|
||||
};
|
||||
|
||||
enum ProfilerType {
|
||||
PROFILER_NETWORK,
|
||||
PROFILER_VISUAL,
|
||||
PROFILER_SCRIPTS_SERVERS
|
||||
};
|
||||
|
||||
AcceptDialog *msgdialog;
|
||||
|
||||
LineEdit *clicked_ctrl;
|
||||
|
@ -132,8 +127,7 @@ private:
|
|||
EditorDebuggerInspector *inspector;
|
||||
SceneDebuggerTree *scene_tree;
|
||||
|
||||
Ref<StreamPeerTCP> connection;
|
||||
Ref<PacketPeerStream> ppeer;
|
||||
Ref<RemoteDebuggerPeer> peer;
|
||||
|
||||
HashMap<NodePath, int> node_path_cache;
|
||||
int last_path_id;
|
||||
|
@ -151,7 +145,7 @@ private:
|
|||
|
||||
bool live_debug;
|
||||
|
||||
CameraOverride camera_override;
|
||||
EditorDebuggerNode::CameraOverride camera_override;
|
||||
|
||||
void _performance_draw();
|
||||
void _performance_select();
|
||||
|
@ -183,12 +177,9 @@ private:
|
|||
void _expand_errors_list();
|
||||
void _collapse_errors_list();
|
||||
|
||||
void _visual_profiler_activate(bool p_enable);
|
||||
void _profiler_activate(bool p_enable);
|
||||
void _profiler_activate(bool p_enable, int p_profiler);
|
||||
void _profiler_seeked();
|
||||
|
||||
void _network_profiler_activate(bool p_enable);
|
||||
|
||||
void _clear_errors_list();
|
||||
|
||||
void _error_tree_item_rmb_selected(const Vector2 &p_pos);
|
||||
|
@ -216,7 +207,7 @@ public:
|
|||
void request_remote_tree();
|
||||
const SceneDebuggerTree *get_remote_tree();
|
||||
|
||||
void start(Ref<StreamPeerTCP> p_connection);
|
||||
void start(Ref<RemoteDebuggerPeer> p_peer);
|
||||
void stop();
|
||||
|
||||
void debug_skip_breakpoints();
|
||||
|
@ -228,7 +219,7 @@ public:
|
|||
void debug_continue();
|
||||
bool is_breaked() const { return breaked; }
|
||||
bool is_debuggable() const { return can_debug; }
|
||||
bool is_session_active() { return connection.is_valid() && connection->is_connected_to_host(); };
|
||||
bool is_session_active() { return peer.is_valid() && peer->is_peer_connected(); };
|
||||
int get_remote_pid() const { return remote_pid; }
|
||||
|
||||
int get_error_count() const { return error_count; }
|
||||
|
@ -252,8 +243,8 @@ public:
|
|||
void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name);
|
||||
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
|
||||
|
||||
CameraOverride get_camera_override() const;
|
||||
void set_camera_override(CameraOverride p_override);
|
||||
EditorDebuggerNode::CameraOverride get_camera_override() const;
|
||||
void set_camera_override(EditorDebuggerNode::CameraOverride p_override);
|
||||
|
||||
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
|
||||
|
||||
|
|
|
@ -3992,7 +3992,7 @@ void CanvasItemEditor::_notification(int p_what) {
|
|||
if (!is_visible() && override_camera_button->is_pressed()) {
|
||||
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
|
||||
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
|
||||
override_camera_button->set_pressed(false);
|
||||
}
|
||||
}
|
||||
|
@ -4348,9 +4348,9 @@ void CanvasItemEditor::_button_override_camera(bool p_pressed) {
|
|||
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
if (p_pressed) {
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_2D);
|
||||
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_2D);
|
||||
} else {
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
|
||||
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_run_script.h"
|
||||
#include "editor/editor_scale.h"
|
||||
|
|
|
@ -4773,12 +4773,12 @@ void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) {
|
|||
case MENU_TOOL_OVERRIDE_CAMERA: {
|
||||
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
using Override = EditorDebuggerNode::CameraOverride;
|
||||
if (pressed) {
|
||||
using Override = ScriptEditorDebugger::CameraOverride;
|
||||
|
||||
debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
|
||||
} else {
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
|
||||
debugger->set_camera_override(Override::OVERRIDE_NONE);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
@ -4831,8 +4831,8 @@ void SpatialEditor::_update_camera_override_viewport(Object *p_viewport) {
|
|||
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
camera_override_viewport_id = current_viewport->index;
|
||||
if (debugger->get_camera_override() >= ScriptEditorDebugger::OVERRIDE_3D_1) {
|
||||
using Override = ScriptEditorDebugger::CameraOverride;
|
||||
if (debugger->get_camera_override() >= EditorDebuggerNode::OVERRIDE_3D_1) {
|
||||
using Override = EditorDebuggerNode::CameraOverride;
|
||||
|
||||
debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
|
||||
}
|
||||
|
@ -5789,7 +5789,7 @@ void SpatialEditor::_notification(int p_what) {
|
|||
if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) {
|
||||
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
|
||||
|
||||
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
|
||||
debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
|
||||
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "main.h"
|
||||
|
||||
#include "core/crypto/crypto.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/input_map.h"
|
||||
#include "core/io/file_access_network.h"
|
||||
#include "core/io/file_access_pack.h"
|
||||
|
@ -43,9 +44,6 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/register_core_types.h"
|
||||
#include "core/script_debugger_local.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "core/script_language.h"
|
||||
#include "core/translation.h"
|
||||
#include "core/version.h"
|
||||
#include "core/version_hash.gen.h"
|
||||
|
@ -96,7 +94,6 @@ static PackedData *packed_data = NULL;
|
|||
static ZipArchive *zip_packed_data = NULL;
|
||||
#endif
|
||||
static FileAccessNetworkClient *file_access_network_client = NULL;
|
||||
static ScriptDebugger *script_debugger = NULL;
|
||||
static MessageQueue *message_queue = NULL;
|
||||
|
||||
// Initialized in setup2()
|
||||
|
@ -410,8 +407,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
String audio_driver = "";
|
||||
String project_path = ".";
|
||||
bool upwards = false;
|
||||
String debug_mode;
|
||||
String debug_host;
|
||||
String debug_uri = "";
|
||||
bool skip_breakpoints = false;
|
||||
String main_pack;
|
||||
bool quiet_stdout = false;
|
||||
|
@ -784,7 +780,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
};
|
||||
|
||||
} else if (I->get() == "-d" || I->get() == "--debug") {
|
||||
debug_mode = "local";
|
||||
debug_uri = "local://";
|
||||
#if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
|
||||
} else if (I->get() == "--debug-collisions") {
|
||||
debug_collisions = true;
|
||||
|
@ -794,12 +790,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
} else if (I->get() == "--remote-debug") {
|
||||
if (I->next()) {
|
||||
|
||||
debug_mode = "remote";
|
||||
debug_host = I->next()->get();
|
||||
if (debug_host.find(":") == -1) { // wrong address
|
||||
debug_uri = I->next()->get();
|
||||
if (debug_uri.find(":") == -1) { // wrong address
|
||||
OS::get_singleton()->print("Invalid debug host address, it should be of the form <host/IP>:<port>.\n");
|
||||
goto error;
|
||||
}
|
||||
debug_uri = "tcp://" + debug_uri; // will support multiple protocols eventually.
|
||||
N = I->next()->next();
|
||||
} else {
|
||||
OS::get_singleton()->print("Missing remote debug host address, aborting.\n");
|
||||
|
@ -886,50 +882,16 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
|
||||
GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1")); // No negative and limit to 500 due to crashes
|
||||
GLOBAL_DEF("network/limits/debugger_stdout/max_chars_per_second", 2048);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater"));
|
||||
GLOBAL_DEF("network/limits/debugger_stdout/max_messages_per_frame", 10);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_messages_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_messages_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater"));
|
||||
GLOBAL_DEF("network/limits/debugger_stdout/max_errors_per_second", 100);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_errors_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_errors_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
|
||||
GLOBAL_DEF("network/limits/debugger_stdout/max_warnings_per_second", 100);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_warnings_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_warnings_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
|
||||
GLOBAL_DEF("network/limits/debugger/max_chars_per_second", 32768);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater"));
|
||||
GLOBAL_DEF("network/limits/debugger/max_queued_messages", 2048);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_queued_messages", PropertyInfo(Variant::INT, "network/limits/debugger/max_queued_messages", PROPERTY_HINT_RANGE, "0, 8192, 1, or_greater"));
|
||||
GLOBAL_DEF("network/limits/debugger/max_errors_per_second", 400);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_errors_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_errors_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
|
||||
GLOBAL_DEF("network/limits/debugger/max_warnings_per_second", 400);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_warnings_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_warnings_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
|
||||
|
||||
if (debug_mode == "remote") {
|
||||
|
||||
ScriptDebuggerRemote *sdr = memnew(ScriptDebuggerRemote);
|
||||
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);
|
||||
}
|
||||
Error derr = sdr->connect_to_host(debug_host, debug_port);
|
||||
|
||||
sdr->set_skip_breakpoints(skip_breakpoints);
|
||||
|
||||
if (derr != OK) {
|
||||
memdelete(sdr);
|
||||
} else {
|
||||
script_debugger = sdr;
|
||||
}
|
||||
} else if (debug_mode == "local") {
|
||||
|
||||
script_debugger = memnew(ScriptDebuggerLocal);
|
||||
OS::get_singleton()->initialize_debugging();
|
||||
}
|
||||
if (script_debugger) {
|
||||
//there is a debugger, parse breakpoints
|
||||
|
||||
for (int i = 0; i < breakpoints.size(); i++) {
|
||||
|
||||
String bp = 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));
|
||||
}
|
||||
}
|
||||
EngineDebugger::initialize(debug_uri, skip_breakpoints, breakpoints);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (editor) {
|
||||
|
@ -1179,6 +1141,8 @@ error:
|
|||
if (show_help)
|
||||
print_help(execpath);
|
||||
|
||||
EngineDebugger::deinitialize();
|
||||
|
||||
if (performance)
|
||||
memdelete(performance);
|
||||
if (input_map)
|
||||
|
@ -1189,8 +1153,6 @@ error:
|
|||
memdelete(globals);
|
||||
if (engine)
|
||||
memdelete(engine);
|
||||
if (script_debugger)
|
||||
memdelete(script_debugger);
|
||||
if (packed_data)
|
||||
memdelete(packed_data);
|
||||
if (file_access_network_client)
|
||||
|
@ -1401,8 +1363,10 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
|
|||
|
||||
audio_server->load_default_bus_layout();
|
||||
|
||||
if (use_debug_profiler && script_debugger) {
|
||||
script_debugger->profiling_start();
|
||||
if (use_debug_profiler && EngineDebugger::is_active()) {
|
||||
// Start the "scripts" profiler, used in local debugging.
|
||||
// We could add more, and make the CLI arg require a comma-separated list of profilers.
|
||||
EngineDebugger::get_singleton()->profiler_enable("scripts", true);
|
||||
}
|
||||
_start_success = true;
|
||||
locale = String();
|
||||
|
@ -2090,12 +2054,8 @@ bool Main::iteration() {
|
|||
|
||||
AudioServer::get_singleton()->update();
|
||||
|
||||
if (script_debugger) {
|
||||
if (script_debugger->is_profiling()) {
|
||||
script_debugger->profiling_set_frame_times(USEC_TO_SEC(frame_time), USEC_TO_SEC(idle_process_ticks), USEC_TO_SEC(physics_process_ticks), frame_slice);
|
||||
}
|
||||
script_debugger->idle_poll();
|
||||
}
|
||||
if (EngineDebugger::is_active())
|
||||
EngineDebugger::get_singleton()->iteration(frame_time, idle_process_ticks, physics_process_ticks, frame_slice);
|
||||
|
||||
frames++;
|
||||
Engine::get_singleton()->_idle_frames++;
|
||||
|
@ -2173,10 +2133,7 @@ void Main::cleanup() {
|
|||
|
||||
ERR_FAIL_COND(!_start_success);
|
||||
|
||||
if (script_debugger) {
|
||||
// Flush any remaining messages
|
||||
script_debugger->idle_poll();
|
||||
}
|
||||
EngineDebugger::deinitialize();
|
||||
|
||||
ResourceLoader::remove_custom_loaders();
|
||||
ResourceSaver::remove_custom_savers();
|
||||
|
@ -2184,14 +2141,6 @@ void Main::cleanup() {
|
|||
message_queue->flush();
|
||||
memdelete(message_queue);
|
||||
|
||||
if (script_debugger) {
|
||||
if (use_debug_profiler) {
|
||||
script_debugger->profiling_end();
|
||||
}
|
||||
|
||||
memdelete(script_debugger);
|
||||
}
|
||||
|
||||
OS::get_singleton()->delete_main_loop();
|
||||
|
||||
OS::get_singleton()->_cmdline.clear();
|
||||
|
|
|
@ -194,7 +194,7 @@ ScriptInstance *PluginScript::instance_create(Object *p_this) {
|
|||
if (!ClassDB::is_parent_class(p_this->get_class_name(), base_type)) {
|
||||
String msg = "Script inherits from native type '" + String(base_type) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'";
|
||||
// TODO: implement PluginscriptLanguage::debug_break_parse
|
||||
// if (ScriptDebugger::get_singleton()) {
|
||||
// if (EngineDebugger::is_active()) {
|
||||
// _language->debug_break_parse(get_path(), 0, msg);
|
||||
// }
|
||||
ERR_FAIL_V_MSG(NULL, msg);
|
||||
|
|
|
@ -315,7 +315,7 @@ ScriptInstance *GDScript::instance_create(Object *p_this) {
|
|||
if (top->native.is_valid()) {
|
||||
if (!ClassDB::is_parent_class(p_this->get_class_name(), top->native->get_name())) {
|
||||
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
|
||||
}
|
||||
ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instanced in object of type '" + p_this->get_class() + "'" + ".");
|
||||
|
@ -556,7 +556,7 @@ Error GDScript::reload(bool p_keep_state) {
|
|||
GDScriptParser parser;
|
||||
Error err = parser.parse(source, basedir, false, path);
|
||||
if (err) {
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_error_line(), "Parser Error: " + parser.get_error());
|
||||
}
|
||||
_err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_error_line(), ("Parse Error: " + parser.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
|
||||
|
@ -571,7 +571,7 @@ Error GDScript::reload(bool p_keep_state) {
|
|||
if (err) {
|
||||
|
||||
if (can_run) {
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
|
||||
}
|
||||
_err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
|
||||
|
@ -583,9 +583,9 @@ Error GDScript::reload(bool p_keep_state) {
|
|||
#ifdef DEBUG_ENABLED
|
||||
for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
|
||||
const GDScriptWarning &warning = E->get();
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
Vector<ScriptLanguage::StackInfo> si;
|
||||
ScriptDebugger::get_singleton()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
|
||||
EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -2201,7 +2201,7 @@ GDScriptLanguage::GDScriptLanguage() {
|
|||
int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
|
||||
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
//debugging enabled!
|
||||
|
||||
_debug_max_call_stack = dmcs;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#ifndef GDSCRIPT_H
|
||||
#define GDSCRIPT_H
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/script_language.h"
|
||||
|
@ -393,13 +395,13 @@ public:
|
|||
if (Thread::get_main_id() != Thread::get_caller_id())
|
||||
return; //no support for other threads than main for now
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
|
||||
ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() + 1);
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
|
||||
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() + 1);
|
||||
|
||||
if (_debug_call_stack_pos >= _debug_max_call_stack) {
|
||||
//stack overflow
|
||||
_debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
|
||||
ScriptDebugger::get_singleton()->debug(this);
|
||||
EngineDebugger::get_script_debugger()->debug(this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -416,13 +418,13 @@ public:
|
|||
if (Thread::get_main_id() != Thread::get_caller_id())
|
||||
return; //no support for other threads than main for now
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
|
||||
ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() - 1);
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
|
||||
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() - 1);
|
||||
|
||||
if (_debug_call_stack_pos == 0) {
|
||||
|
||||
_debug_error = "Stack Underflow (Engine Bug)";
|
||||
ScriptDebugger::get_singleton()->debug(this);
|
||||
EngineDebugger::get_script_debugger()->debug(this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1579,7 +1579,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||
codegen.stack_max = 0;
|
||||
codegen.current_line = 0;
|
||||
codegen.call_max = 0;
|
||||
codegen.debug_stack = ScriptDebugger::get_singleton() != NULL;
|
||||
codegen.debug_stack = EngineDebugger::is_active();
|
||||
Vector<StringName> argnames;
|
||||
|
||||
int stack_level = 0;
|
||||
|
@ -1765,7 +1765,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
|
|||
gdfunc->_call_size = codegen.call_max;
|
||||
gdfunc->name = func_name;
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
String signature;
|
||||
//path
|
||||
if (p_script->get_path() != String())
|
||||
|
|
|
@ -221,12 +221,12 @@ Script *GDScriptLanguage::create_script() const {
|
|||
bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
|
||||
//break because of parse error
|
||||
|
||||
if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
|
||||
_debug_parse_err_line = p_line;
|
||||
_debug_parse_err_file = p_file;
|
||||
_debug_error = p_error;
|
||||
ScriptDebugger::get_singleton()->debug(this, false, true);
|
||||
EngineDebugger::get_script_debugger()->debug(this, false, true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -235,13 +235,13 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const
|
|||
|
||||
bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {
|
||||
|
||||
if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
|
||||
_debug_parse_err_line = -1;
|
||||
_debug_parse_err_file = "";
|
||||
_debug_error = p_error;
|
||||
bool is_error_breakpoint = p_error != "Breakpoint";
|
||||
ScriptDebugger::get_singleton()->debug(this, p_allow_continue, is_error_breakpoint);
|
||||
EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, is_error_breakpoint);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -391,7 +391,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
if (ScriptDebugger::get_singleton())
|
||||
if (EngineDebugger::is_active())
|
||||
GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
|
||||
|
||||
#define GD_ERR_BREAK(m_cond) \
|
||||
|
@ -1522,7 +1522,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
OPCODE(OPCODE_BREAKPOINT) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
|
||||
}
|
||||
#endif
|
||||
|
@ -1536,26 +1536,26 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
line = _code_ptr[ip + 1];
|
||||
ip += 2;
|
||||
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
// line
|
||||
bool do_break = false;
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() > 0) {
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_depth() <= 0)
|
||||
ScriptDebugger::get_singleton()->set_lines_left(ScriptDebugger::get_singleton()->get_lines_left() - 1);
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() <= 0)
|
||||
if (EngineDebugger::get_script_debugger()->get_depth() <= 0)
|
||||
EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0)
|
||||
do_break = true;
|
||||
}
|
||||
|
||||
if (ScriptDebugger::get_singleton()->is_breakpoint(line, source))
|
||||
if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source))
|
||||
do_break = true;
|
||||
|
||||
if (do_break) {
|
||||
GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
|
||||
}
|
||||
|
||||
ScriptDebugger::get_singleton()->line_poll();
|
||||
EngineDebugger::get_singleton()->line_poll();
|
||||
}
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
@ -1622,7 +1622,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
// When it's the last resume it will postpone the exit from stack,
|
||||
// so the debugger knows which function triggered the resume of the next function (if any)
|
||||
if (!p_state || yielded) {
|
||||
if (ScriptDebugger::get_singleton())
|
||||
if (EngineDebugger::is_active())
|
||||
GDScriptLanguage::get_singleton()->exit_function();
|
||||
#endif
|
||||
|
||||
|
@ -1884,7 +1884,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
|
|||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton())
|
||||
if (EngineDebugger::is_active())
|
||||
GDScriptLanguage::get_singleton()->exit_function();
|
||||
if (state.stack_size) {
|
||||
//free stack
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include <mono/metadata/threads.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
#include "core/io/json.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/mutex.h"
|
||||
|
@ -1134,11 +1136,11 @@ void CSharpLanguage::thread_exit() {
|
|||
bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
|
||||
|
||||
// Not a parser error in our case, but it's still used for other type of errors
|
||||
if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
_debug_parse_err_line = p_line;
|
||||
_debug_parse_err_file = p_file;
|
||||
_debug_error = p_error;
|
||||
ScriptDebugger::get_singleton()->debug(this, false, true);
|
||||
EngineDebugger::get_script_debugger()->debug(this, false, true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -1147,11 +1149,11 @@ bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const S
|
|||
|
||||
bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
|
||||
|
||||
if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
_debug_parse_err_line = -1;
|
||||
_debug_parse_err_file = "";
|
||||
_debug_error = p_error;
|
||||
ScriptDebugger::get_singleton()->debug(this, p_allow_continue);
|
||||
EngineDebugger::get_script_debugger()->debug(this, p_allow_continue);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -2998,7 +3000,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
|
|||
if (native) {
|
||||
String native_name = NATIVE_GDMONOCLASS_NAME(native);
|
||||
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
|
||||
}
|
||||
ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + native_name +
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <mono/metadata/mono-gc.h>
|
||||
#include <mono/metadata/profiler.h>
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
|
@ -1183,8 +1184,8 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
|
||||
if (ScriptDebugger::get_singleton())
|
||||
ScriptDebugger::get_singleton()->idle_poll();
|
||||
if (EngineDebugger::is_active())
|
||||
EngineDebugger::get_singleton()->poll_events(false);
|
||||
#endif
|
||||
|
||||
exit(mono_environment_exitcode_get());
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
#include "gd_mono_marshal.h"
|
||||
#include "gd_mono_utils.h"
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
|
||||
#include <mono/metadata/exception.h>
|
||||
|
||||
namespace GDMonoInternals {
|
||||
|
@ -120,8 +123,8 @@ void unhandled_exception(MonoException *p_exc) {
|
|||
} else {
|
||||
#ifdef DEBUG_ENABLED
|
||||
GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
|
||||
if (ScriptDebugger::get_singleton())
|
||||
ScriptDebugger::get_singleton()->idle_poll();
|
||||
if (EngineDebugger::is_active())
|
||||
EngineDebugger::get_singleton()->poll_events(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include <mono/metadata/exception.h>
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/os.h"
|
||||
|
@ -39,7 +41,7 @@
|
|||
#include "core/reference.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#endif
|
||||
|
||||
#include "../csharp_script.h"
|
||||
|
@ -351,7 +353,7 @@ void debug_print_unhandled_exception(MonoException *p_exc) {
|
|||
|
||||
void debug_send_unhandled_exception_error(MonoException *p_exc) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!ScriptDebugger::get_singleton()) {
|
||||
if (!EngineDebugger::is_active()) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
|
||||
|
@ -410,7 +412,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
|
|||
int line = si.size() ? si[0].line : __LINE__;
|
||||
String error_msg = "Unhandled exception";
|
||||
|
||||
ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
|
||||
EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1629,7 +1629,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
|
|||
int flow_stack_pos = p_flow_stack_pos;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
VisualScriptLanguage::singleton->enter_function(this, &p_method, variant_stack, &working_mem, ¤t_node_id);
|
||||
}
|
||||
#endif
|
||||
|
@ -1766,7 +1766,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
//will re-enter later, so exiting
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
VisualScriptLanguage::singleton->exit_function();
|
||||
}
|
||||
#endif
|
||||
|
@ -1776,26 +1776,26 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
|
|||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
// line
|
||||
bool do_break = false;
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() > 0) {
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_depth() <= 0)
|
||||
ScriptDebugger::get_singleton()->set_lines_left(ScriptDebugger::get_singleton()->get_lines_left() - 1);
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() <= 0)
|
||||
if (EngineDebugger::get_script_debugger()->get_depth() <= 0)
|
||||
EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0)
|
||||
do_break = true;
|
||||
}
|
||||
|
||||
if (ScriptDebugger::get_singleton()->is_breakpoint(current_node_id, source))
|
||||
if (EngineDebugger::get_script_debugger()->is_breakpoint(current_node_id, source))
|
||||
do_break = true;
|
||||
|
||||
if (do_break) {
|
||||
VisualScriptLanguage::singleton->debug_break("Breakpoint", true);
|
||||
}
|
||||
|
||||
ScriptDebugger::get_singleton()->line_poll();
|
||||
EngineDebugger::get_singleton()->line_poll();
|
||||
}
|
||||
#endif
|
||||
int output = ret & VisualScriptNodeInstance::STEP_MASK;
|
||||
|
@ -1983,7 +1983,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
|
|||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
VisualScriptLanguage::singleton->exit_function();
|
||||
}
|
||||
#endif
|
||||
|
@ -2593,12 +2593,12 @@ void VisualScriptLanguage::add_global_constant(const StringName &p_variable, con
|
|||
bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, const String &p_error) {
|
||||
//break because of parse error
|
||||
|
||||
if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
|
||||
_debug_parse_err_node = p_node;
|
||||
_debug_parse_err_file = p_file;
|
||||
_debug_error = p_error;
|
||||
ScriptDebugger::get_singleton()->debug(this, false, true);
|
||||
EngineDebugger::get_script_debugger()->debug(this, false, true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -2607,12 +2607,12 @@ bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, c
|
|||
|
||||
bool VisualScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {
|
||||
|
||||
if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
|
||||
|
||||
_debug_parse_err_node = -1;
|
||||
_debug_parse_err_file = "";
|
||||
_debug_error = p_error;
|
||||
ScriptDebugger::get_singleton()->debug(this, p_allow_continue, true);
|
||||
EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -2837,7 +2837,7 @@ VisualScriptLanguage::VisualScriptLanguage() {
|
|||
int dmcs = GLOBAL_DEF("debug/settings/visual_script/max_call_stack", 1024);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
|
||||
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
//debugging enabled!
|
||||
_debug_max_call_stack = dmcs;
|
||||
_call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#ifndef VISUAL_SCRIPT_H
|
||||
#define VISUAL_SCRIPT_H
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/script_language.h"
|
||||
|
||||
|
@ -540,13 +542,13 @@ public:
|
|||
if (Thread::get_main_id() != Thread::get_caller_id())
|
||||
return; //no support for other threads than main for now
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
|
||||
ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() + 1);
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
|
||||
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() + 1);
|
||||
|
||||
if (_debug_call_stack_pos >= _debug_max_call_stack) {
|
||||
//stack overflow
|
||||
_debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
|
||||
ScriptDebugger::get_singleton()->debug(this);
|
||||
EngineDebugger::get_script_debugger()->debug(this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -563,13 +565,13 @@ public:
|
|||
if (Thread::get_main_id() != Thread::get_caller_id())
|
||||
return; //no support for other threads than main for now
|
||||
|
||||
if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
|
||||
ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() - 1);
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
|
||||
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() - 1);
|
||||
|
||||
if (_debug_call_stack_pos == 0) {
|
||||
|
||||
_debug_error = "Stack Underflow (Engine Bug)";
|
||||
ScriptDebugger::get_singleton()->debug(this);
|
||||
EngineDebugger::get_script_debugger()->debug(this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,9 @@
|
|||
|
||||
#include "os_windows.h"
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/debugger/script_debugger.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/script_language.h"
|
||||
#include "core/version_generated.gen.h"
|
||||
|
||||
#if defined(OPENGL_ENABLED)
|
||||
|
@ -194,13 +195,13 @@ void RedirectIOToConsole() {
|
|||
}
|
||||
|
||||
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
|
||||
if (ScriptDebugger::get_singleton() == NULL)
|
||||
if (!EngineDebugger::is_active())
|
||||
return FALSE;
|
||||
|
||||
switch (dwCtrlType) {
|
||||
case CTRL_C_EVENT:
|
||||
ScriptDebugger::get_singleton()->set_depth(-1);
|
||||
ScriptDebugger::get_singleton()->set_lines_left(1);
|
||||
EngineDebugger::get_script_debugger()->set_depth(-1);
|
||||
EngineDebugger::get_script_debugger()->set_lines_left(1);
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
|
|
|
@ -30,8 +30,9 @@
|
|||
|
||||
#include "scene_debugger.h"
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "core/script_language.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
@ -39,13 +40,16 @@
|
|||
void SceneDebugger::initialize() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
LiveEditor::singleton = memnew(LiveEditor);
|
||||
ScriptDebuggerRemote::scene_tree_parse_func = SceneDebugger::parse_message;
|
||||
EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(NULL, SceneDebugger::parse_message));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneDebugger::deinitialize() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (LiveEditor::singleton) {
|
||||
// Should be removed automatically when deiniting debugger, but just in case
|
||||
if (EngineDebugger::has_capture("scene"))
|
||||
EngineDebugger::unregister_message_capture("scene");
|
||||
memdelete(LiveEditor::singleton);
|
||||
LiveEditor::singleton = NULL;
|
||||
}
|
||||
|
@ -53,13 +57,15 @@ void SceneDebugger::deinitialize() {
|
|||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Error SceneDebugger::parse_message(const String &p_msg, const Array &p_args) {
|
||||
Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
if (!scene_tree)
|
||||
return ERR_UNCONFIGURED;
|
||||
LiveEditor *live_editor = LiveEditor::get_singleton();
|
||||
if (!live_editor)
|
||||
return ERR_UNCONFIGURED;
|
||||
|
||||
r_captured = true;
|
||||
if (p_msg == "request_scene_tree") { // Scene tree
|
||||
live_editor->_send_tree();
|
||||
|
||||
|
@ -171,7 +177,7 @@ Error SceneDebugger::parse_message(const String &p_msg, const Array &p_args) {
|
|||
ERR_FAIL_COND_V(p_args.size() < 4, ERR_INVALID_DATA);
|
||||
live_editor->_reparent_node_func(p_args[0], p_args[1], p_args[2], p_args[3]);
|
||||
} else {
|
||||
return ERR_SKIP;
|
||||
r_captured = false;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
@ -180,7 +186,6 @@ void SceneDebugger::_save_node(ObjectID id, const String &p_path) {
|
|||
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
|
||||
ERR_FAIL_COND(!node);
|
||||
|
||||
WARN_PRINT("SAVING " + itos(id) + " TO " + p_path);
|
||||
Ref<PackedScene> ps = memnew(PackedScene);
|
||||
ps->pack(node);
|
||||
ResourceSaver::save(p_path, ps);
|
||||
|
@ -193,7 +198,7 @@ void SceneDebugger::_send_object_id(ObjectID p_id, int p_max_size) {
|
|||
|
||||
Array arr;
|
||||
obj.serialize(arr);
|
||||
ScriptDebugger::get_singleton()->send_message("inspect_object", arr);
|
||||
EngineDebugger::get_singleton()->send_message("scene:inspect_object", arr);
|
||||
}
|
||||
|
||||
void SceneDebugger::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
|
||||
|
@ -216,7 +221,7 @@ void SceneDebugger::add_to_cache(const String &p_filename, Node *p_node) {
|
|||
if (!debugger)
|
||||
return;
|
||||
|
||||
if (ScriptDebugger::get_singleton() && p_filename != String()) {
|
||||
if (EngineDebugger::get_script_debugger() && p_filename != String()) {
|
||||
debugger->live_scene_edit_cache[p_filename].insert(p_node);
|
||||
}
|
||||
}
|
||||
|
@ -487,7 +492,7 @@ void LiveEditor::_send_tree() {
|
|||
// Encoded as a flat list depth fist.
|
||||
SceneDebuggerTree tree(scene_tree->root);
|
||||
tree.serialize(arr);
|
||||
ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
|
||||
EngineDebugger::get_singleton()->send_message("scene:scene_tree", arr);
|
||||
}
|
||||
|
||||
void LiveEditor::_node_path_func(const NodePath &p_path, int p_id) {
|
||||
|
|
|
@ -34,9 +34,10 @@
|
|||
#include "core/array.h"
|
||||
#include "core/object.h"
|
||||
#include "core/pair.h"
|
||||
#include "core/script_language.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
class Script;
|
||||
|
||||
class SceneDebugger {
|
||||
|
||||
public:
|
||||
|
@ -50,7 +51,7 @@ private:
|
|||
static void _send_object_id(ObjectID p_id, int p_max_size = 1 << 20);
|
||||
|
||||
public:
|
||||
static Error parse_message(const String &p_msg, const Array &p_args);
|
||||
static Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
|
||||
static void add_to_cache(const String &p_filename, Node *p_node);
|
||||
static void remove_from_cache(const String &p_filename, Node *p_node);
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "scene_tree.h"
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/message_queue.h"
|
||||
|
@ -38,7 +39,6 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/print_string.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "main/input_default.h"
|
||||
#include "node.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
|
@ -432,11 +432,11 @@ void SceneTree::input_event(const Ref<InputEvent> &p_event) {
|
|||
|
||||
call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_vp_input", ev); //special one for GUI, as controls use their own process check
|
||||
|
||||
if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_remote()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
//quit from game window using F8
|
||||
Ref<InputEventKey> k = ev;
|
||||
if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_F8) {
|
||||
ScriptDebugger::get_singleton()->request_quit();
|
||||
EngineDebugger::get_singleton()->send_message("request_quit", Array());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1737,10 +1737,6 @@ SceneTree::SceneTree() {
|
|||
last_screen_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
|
||||
_update_root_rect();
|
||||
|
||||
if (ScriptDebugger::get_singleton()) {
|
||||
ScriptDebugger::get_singleton()->set_multiplayer(multiplayer);
|
||||
}
|
||||
|
||||
root->set_physics_object_picking(GLOBAL_DEF("physics/common/enable_object_picking", true));
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "viewport.h"
|
||||
|
||||
#include "core/core_string_names.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/os/input.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
|
@ -1927,12 +1928,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
|
|||
mb->set_position(pos);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton() && gui.mouse_focus) {
|
||||
if (EngineDebugger::get_singleton() && gui.mouse_focus) {
|
||||
|
||||
Array arr;
|
||||
arr.push_back(gui.mouse_focus->get_path());
|
||||
arr.push_back(gui.mouse_focus->get_class());
|
||||
ScriptDebugger::get_singleton()->send_message("click_ctrl", arr);
|
||||
EngineDebugger::get_singleton()->send_message("scene:click_ctrl", arr);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "audio_server.h"
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
|
@ -992,7 +993,7 @@ void AudioServer::init() {
|
|||
|
||||
void AudioServer::update() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
|
||||
if (EngineDebugger::is_profiling("servers")) {
|
||||
|
||||
// Driver time includes server time + effects times
|
||||
// Server time includes effects times
|
||||
|
@ -1030,7 +1031,8 @@ void AudioServer::update() {
|
|||
values.push_back("audio_driver");
|
||||
values.push_back(USEC_TO_SEC(driver_time));
|
||||
|
||||
ScriptDebugger::get_singleton()->add_profiling_frame_data("audio_thread", values);
|
||||
values.push_front("audio_thread");
|
||||
EngineDebugger::profiler_add_frame_data("servers", values);
|
||||
}
|
||||
|
||||
// Reset profiling times
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
|
||||
#include "broad_phase_basic.h"
|
||||
#include "broad_phase_octree.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/script_language.h"
|
||||
#include "joints/cone_twist_joint_sw.h"
|
||||
#include "joints/generic_6dof_joint_sw.h"
|
||||
#include "joints/hinge_joint_sw.h"
|
||||
|
@ -1467,7 +1467,7 @@ void PhysicsServerSW::flush_queries() {
|
|||
|
||||
flushing_queries = false;
|
||||
|
||||
if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
|
||||
if (EngineDebugger::is_profiling("servers")) {
|
||||
|
||||
uint64_t total_time[SpaceSW::ELAPSED_TIME_MAX];
|
||||
static const char *time_name[SpaceSW::ELAPSED_TIME_MAX] = {
|
||||
|
@ -1498,7 +1498,8 @@ void PhysicsServerSW::flush_queries() {
|
|||
values.push_back("flush_queries");
|
||||
values.push_back(USEC_TO_SEC(OS::get_singleton()->get_ticks_usec() - time_beg));
|
||||
|
||||
ScriptDebugger::get_singleton()->add_profiling_frame_data("physics", values);
|
||||
values.push_front("physics");
|
||||
EngineDebugger::profiler_add_frame_data("server", values);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
#include "broad_phase_2d_basic.h"
|
||||
#include "broad_phase_2d_hash_grid.h"
|
||||
#include "collision_solver_2d_sw.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/script_language.h"
|
||||
|
||||
#define FLUSH_QUERY_CHECK(m_object) \
|
||||
ERR_FAIL_COND_MSG(m_object->get_space() && flushing_queries, "Can't change this state while flushing queries. Use call_deferred() or set_deferred() to change monitoring state instead.");
|
||||
|
@ -1369,7 +1369,7 @@ void Physics2DServerSW::flush_queries() {
|
|||
|
||||
flushing_queries = false;
|
||||
|
||||
if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
|
||||
if (EngineDebugger::is_profiling("servers")) {
|
||||
|
||||
uint64_t total_time[Space2DSW::ELAPSED_TIME_MAX];
|
||||
static const char *time_name[Space2DSW::ELAPSED_TIME_MAX] = {
|
||||
|
@ -1400,7 +1400,8 @@ void Physics2DServerSW::flush_queries() {
|
|||
values.push_back("flush_queries");
|
||||
values.push_back(USEC_TO_SEC(OS::get_singleton()->get_ticks_usec() - time_beg));
|
||||
|
||||
ScriptDebugger::get_singleton()->add_profiling_frame_data("physics_2d", values);
|
||||
values.push_front("physics_2d");
|
||||
EngineDebugger::profiler_add_frame_data("servers", values);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "audio_server.h"
|
||||
#include "camera/camera_feed.h"
|
||||
#include "camera_server.h"
|
||||
#include "core/script_debugger_remote.h"
|
||||
#include "navigation_2d_server.h"
|
||||
#include "navigation_server.h"
|
||||
#include "physics/physics_server_sw.h"
|
||||
|
@ -67,27 +66,6 @@
|
|||
#include "visual/shader_types.h"
|
||||
#include "visual_server.h"
|
||||
|
||||
static void _debugger_get_resource_usage(ScriptDebuggerRemote::ResourceUsage *r_usage) {
|
||||
|
||||
List<VS::TextureInfo> tinfo;
|
||||
VS::get_singleton()->texture_debug_usage(&tinfo);
|
||||
|
||||
for (List<VS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) {
|
||||
|
||||
ScriptDebuggerRemote::ResourceInfo usage;
|
||||
usage.path = E->get().path;
|
||||
usage.vram = E->get().bytes;
|
||||
usage.id = E->get().texture;
|
||||
usage.type = "Texture";
|
||||
if (E->get().depth == 0) {
|
||||
usage.format = itos(E->get().width) + "x" + itos(E->get().height) + " " + Image::get_format_name(E->get().format);
|
||||
} else {
|
||||
usage.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format);
|
||||
}
|
||||
r_usage->infos.push_back(usage);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderTypes *shader_types = NULL;
|
||||
|
||||
PhysicsServer *_createGodotPhysicsCallback() {
|
||||
|
@ -189,8 +167,6 @@ void register_server_types() {
|
|||
ClassDB::register_virtual_class<PhysicsDirectSpaceState>();
|
||||
ClassDB::register_virtual_class<PhysicsShapeQueryResult>();
|
||||
|
||||
ScriptDebuggerRemote::resource_usage_func = _debugger_get_resource_usage;
|
||||
|
||||
// Physics 2D
|
||||
GLOBAL_DEF(Physics2DServerManager::setting_property_name, "DEFAULT");
|
||||
ProjectSettings::get_singleton()->set_custom_property_info(Physics2DServerManager::setting_property_name, PropertyInfo(Variant::STRING, Physics2DServerManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT"));
|
||||
|
|
Loading…
Reference in New Issue
Block a user