Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/debugger/engine_debugger.cpp
10277 views
1
/**************************************************************************/
2
/* engine_debugger.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "engine_debugger.h"
32
33
#include "core/debugger/local_debugger.h"
34
#include "core/debugger/remote_debugger.h"
35
#include "core/debugger/remote_debugger_peer.h"
36
#include "core/debugger/script_debugger.h"
37
#include "core/os/os.h"
38
39
void (*EngineDebugger::allow_focus_steal_fn)();
40
41
void EngineDebugger::register_profiler(const StringName &p_name, const Profiler &p_func) {
42
ERR_FAIL_COND_MSG(profilers.has(p_name), vformat("Profiler already registered: '%s'.", p_name));
43
profilers.insert(p_name, p_func);
44
}
45
46
void EngineDebugger::unregister_profiler(const StringName &p_name) {
47
ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Profiler not registered: '%s'.", p_name));
48
Profiler &p = profilers[p_name];
49
if (p.active && p.toggle) {
50
p.toggle(p.data, false, Array());
51
p.active = false;
52
}
53
profilers.erase(p_name);
54
}
55
56
void EngineDebugger::register_message_capture(const StringName &p_name, Capture p_func) {
57
ERR_FAIL_COND_MSG(captures.has(p_name), vformat("Capture already registered: '%s'.", p_name));
58
captures.insert(p_name, p_func);
59
}
60
61
void EngineDebugger::unregister_message_capture(const StringName &p_name) {
62
ERR_FAIL_COND_MSG(!captures.has(p_name), vformat("Capture not registered: '%s'.", p_name));
63
captures.erase(p_name);
64
}
65
66
void EngineDebugger::register_uri_handler(const String &p_protocol, CreatePeerFunc p_func) {
67
ERR_FAIL_COND_MSG(protocols.has(p_protocol), vformat("Protocol handler already registered: '%s'.", p_protocol));
68
protocols.insert(p_protocol, p_func);
69
}
70
71
void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
72
ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't change profiler state, no profiler: '%s'.", p_name));
73
Profiler &p = profilers[p_name];
74
if (p.toggle) {
75
p.toggle(p.data, p_enabled, p_opts);
76
}
77
p.active = p_enabled;
78
}
79
80
void EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
81
ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't add frame data, no profiler: '%s'.", p_name));
82
Profiler &p = profilers[p_name];
83
if (p.add) {
84
p.add(p.data, p_data);
85
}
86
}
87
88
bool EngineDebugger::is_profiling(const StringName &p_name) {
89
return profilers.has(p_name) && profilers[p_name].active;
90
}
91
92
bool EngineDebugger::has_profiler(const StringName &p_name) {
93
return profilers.has(p_name);
94
}
95
96
bool EngineDebugger::has_capture(const StringName &p_name) {
97
return captures.has(p_name);
98
}
99
100
Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured) {
101
r_captured = false;
102
ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, vformat("Capture not registered: '%s'.", p_name));
103
const Capture &cap = captures[p_name];
104
return cap.capture(cap.data, p_msg, p_args, r_captured);
105
}
106
107
void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time) {
108
frame_time = USEC_TO_SEC(p_frame_ticks);
109
process_time = USEC_TO_SEC(p_process_ticks);
110
physics_time = USEC_TO_SEC(p_physics_ticks);
111
physics_frame_time = p_physics_frame_time;
112
// Notify tick to running profilers
113
for (KeyValue<StringName, Profiler> &E : profilers) {
114
Profiler &p = E.value;
115
if (!p.active || !p.tick) {
116
continue;
117
}
118
p.tick(p.data, frame_time, process_time, physics_time, physics_frame_time);
119
}
120
singleton->poll_events(true);
121
}
122
123
void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, bool p_ignore_error_breaks, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()) {
124
register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create); // TCP is the default protocol. Platforms/modules can add more.
125
if (p_uri.is_empty()) {
126
return;
127
}
128
if (p_uri == "local://") {
129
singleton = memnew(LocalDebugger);
130
script_debugger = memnew(ScriptDebugger);
131
// Tell the OS that we want to handle termination signals.
132
OS::get_singleton()->initialize_debugging();
133
} else if (p_uri.contains("://")) {
134
const String proto = p_uri.substr(0, p_uri.find("://") + 3);
135
if (!protocols.has(proto)) {
136
return;
137
}
138
RemoteDebuggerPeer *peer = protocols[proto](p_uri);
139
if (!peer) {
140
return;
141
}
142
singleton = memnew(RemoteDebugger(Ref<RemoteDebuggerPeer>(peer)));
143
script_debugger = memnew(ScriptDebugger);
144
// Notify editor of our pid (to allow focus stealing).
145
Array msg = { OS::get_singleton()->get_process_id() };
146
singleton->send_message("set_pid", msg);
147
}
148
if (!singleton) {
149
return;
150
}
151
152
// There is a debugger, parse breakpoints.
153
ScriptDebugger *singleton_script_debugger = singleton->get_script_debugger();
154
singleton_script_debugger->set_skip_breakpoints(p_skip_breakpoints);
155
singleton_script_debugger->set_ignore_error_breaks(p_ignore_error_breaks);
156
157
for (int i = 0; i < p_breakpoints.size(); i++) {
158
const String &bp = p_breakpoints[i];
159
int sp = bp.rfind_char(':');
160
ERR_CONTINUE_MSG(sp == -1, vformat("Invalid breakpoint: '%s', expected file:line format.", bp));
161
162
singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1).to_int(), bp.substr(0, sp));
163
}
164
165
allow_focus_steal_fn = p_allow_focus_steal_fn;
166
}
167
168
void EngineDebugger::deinitialize() {
169
if (singleton) {
170
// Stop all profilers
171
for (const KeyValue<StringName, Profiler> &E : profilers) {
172
if (E.value.active) {
173
singleton->profiler_enable(E.key, false);
174
}
175
}
176
177
// Flush any remaining message
178
singleton->poll_events(false);
179
180
memdelete(singleton);
181
singleton = nullptr;
182
}
183
184
// Clear profilers/captures/protocol handlers.
185
profilers.clear();
186
captures.clear();
187
protocols.clear();
188
}
189
190
EngineDebugger::~EngineDebugger() {
191
if (script_debugger) {
192
memdelete(script_debugger);
193
}
194
script_debugger = nullptr;
195
singleton = nullptr;
196
}
197
198