1/**************************************************************************/
2/* remote_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 "remote_debugger.h"
32
33#include "core/config/project_settings.h"
34#include "core/debugger/debugger_marshalls.h"
35#include "core/debugger/engine_debugger.h"
36#include "core/debugger/engine_profiler.h"
37#include "core/debugger/script_debugger.h"
38#include "core/input/input.h"
39#include "core/object/script_language.h"
40#include "core/os/os.h"
41
42class RemoteDebugger::PerformanceProfiler : public EngineProfiler {
43 Object *performance = nullptr;
44 int last_perf_time = 0;
45 uint64_t last_monitor_modification_time = 0;
46
47public:
48 void toggle(bool p_enable, const Array &p_opts) {}
49 void add(const Array &p_data) {}
50 void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
51 if (!performance) {
52 return;
53 }
54
55 uint64_t pt = OS::get_singleton()->get_ticks_msec();
56 if (pt - last_perf_time < 1000) {
57 return;
58 }
59 last_perf_time = pt;
60
61 Array custom_monitor_names = performance->call("get_custom_monitor_names");
62
63 uint64_t monitor_modification_time = performance->call("get_monitor_modification_time");
64 if (monitor_modification_time > last_monitor_modification_time) {
65 last_monitor_modification_time = monitor_modification_time;
66 EngineDebugger::get_singleton()->send_message("performance:profile_names", custom_monitor_names);
67 }
68
69 int max = performance->get("MONITOR_MAX");
70 Array arr;
71 arr.resize(max + custom_monitor_names.size());
72 for (int i = 0; i < max; i++) {
73 arr[i] = performance->call("get_monitor", i);
74 }
75
76 for (int i = 0; i < custom_monitor_names.size(); i++) {
77 Variant monitor_value = performance->call("get_custom_monitor", custom_monitor_names[i]);
78 if (!monitor_value.is_num()) {
79 ERR_PRINT("Value of custom monitor '" + String(custom_monitor_names[i]) + "' is not a number");
80 arr[i + max] = Variant();
81 } else {
82 arr[i + max] = monitor_value;
83 }
84 }
85
86 EngineDebugger::get_singleton()->send_message("performance:profile_frame", arr);
87 }
88
89 explicit PerformanceProfiler(Object *p_performance) {
90 performance = p_performance;
91 }
92};
93
94Error RemoteDebugger::_put_msg(String p_message, Array p_data) {
95 Array msg;
96 msg.push_back(p_message);
97 msg.push_back(Thread::get_caller_id());
98 msg.push_back(p_data);
99 Error err = peer->put_message(msg);
100 if (err != OK) {
101 n_messages_dropped++;
102 }
103 return err;
104}
105
106void 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, bool p_editor_notify, ErrorHandlerType p_type) {
107 if (p_type == ERR_HANDLER_SCRIPT) {
108 return; //ignore script errors, those go through debugger
109 }
110
111 RemoteDebugger *rd = static_cast<RemoteDebugger *>(p_this);
112 if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive errors during flush.
113 return;
114 }
115
116 Vector<ScriptLanguage::StackInfo> si;
117
118 for (int i = 0; i < ScriptServer::get_language_count(); i++) {
119 si = ScriptServer::get_language(i)->debug_get_current_stack_info();
120 if (si.size()) {
121 break;
122 }
123 }
124
125 // send_error will lock internally.
126 rd->script_debugger->send_error(String::utf8(p_func), String::utf8(p_file), p_line, String::utf8(p_err), String::utf8(p_descr), p_editor_notify, p_type, si);
127}
128
129void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) {
130 RemoteDebugger *rd = static_cast<RemoteDebugger *>(p_this);
131
132 if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive prints during flush.
133 return;
134 }
135
136 String s = p_string;
137 int allowed_chars = MIN(MAX(rd->max_chars_per_second - rd->char_count, 0), s.length());
138
139 if (allowed_chars == 0 && s.length() > 0) {
140 return;
141 }
142
143 if (allowed_chars < s.length()) {
144 s = s.substr(0, allowed_chars);
145 }
146
147 MutexLock lock(rd->mutex);
148
149 rd->char_count += allowed_chars;
150 bool overflowed = rd->char_count >= rd->max_chars_per_second;
151 if (rd->is_peer_connected()) {
152 if (overflowed) {
153 s += "[...]";
154 }
155
156 OutputString output_string;
157 output_string.message = s;
158 if (p_error) {
159 output_string.type = MESSAGE_TYPE_ERROR;
160 } else if (p_rich) {
161 output_string.type = MESSAGE_TYPE_LOG_RICH;
162 } else {
163 output_string.type = MESSAGE_TYPE_LOG;
164 }
165 rd->output_strings.push_back(output_string);
166
167 if (overflowed) {
168 output_string.message = "[output overflow, print less text!]";
169 output_string.type = MESSAGE_TYPE_ERROR;
170 rd->output_strings.push_back(output_string);
171 }
172 }
173}
174
175RemoteDebugger::ErrorMessage RemoteDebugger::_create_overflow_error(const String &p_what, const String &p_descr) {
176 ErrorMessage oe;
177 oe.error = p_what;
178 oe.error_descr = p_descr;
179 oe.warning = false;
180 uint64_t time = OS::get_singleton()->get_ticks_msec();
181 oe.hr = time / 3600000;
182 oe.min = (time / 60000) % 60;
183 oe.sec = (time / 1000) % 60;
184 oe.msec = time % 1000;
185 return oe;
186}
187
188void RemoteDebugger::flush_output() {
189 MutexLock lock(mutex);
190 flush_thread = Thread::get_caller_id();
191 flushing = true;
192 if (!is_peer_connected()) {
193 return;
194 }
195
196 if (n_messages_dropped > 0) {
197 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.");
198 if (_put_msg("error", err_msg.serialize()) == OK) {
199 n_messages_dropped = 0;
200 }
201 }
202
203 if (output_strings.size()) {
204 // Join output strings so we generate less messages.
205 Vector<String> joined_log_strings;
206 Vector<String> strings;
207 Vector<int> types;
208 for (int i = 0; i < output_strings.size(); i++) {
209 const OutputString &output_string = output_strings[i];
210 if (output_string.type == MESSAGE_TYPE_ERROR) {
211 if (!joined_log_strings.is_empty()) {
212 strings.push_back(String("\n").join(joined_log_strings));
213 types.push_back(MESSAGE_TYPE_LOG);
214 joined_log_strings.clear();
215 }
216 strings.push_back(output_string.message);
217 types.push_back(MESSAGE_TYPE_ERROR);
218 } else if (output_string.type == MESSAGE_TYPE_LOG_RICH) {
219 if (!joined_log_strings.is_empty()) {
220 strings.push_back(String("\n").join(joined_log_strings));
221 types.push_back(MESSAGE_TYPE_LOG_RICH);
222 joined_log_strings.clear();
223 }
224 strings.push_back(output_string.message);
225 types.push_back(MESSAGE_TYPE_LOG_RICH);
226 } else {
227 joined_log_strings.push_back(output_string.message);
228 }
229 }
230
231 if (!joined_log_strings.is_empty()) {
232 strings.push_back(String("\n").join(joined_log_strings));
233 types.push_back(MESSAGE_TYPE_LOG);
234 }
235
236 Array arr;
237 arr.push_back(strings);
238 arr.push_back(types);
239 _put_msg("output", arr);
240 output_strings.clear();
241 }
242
243 while (errors.size()) {
244 ErrorMessage oe = errors.front()->get();
245 _put_msg("error", oe.serialize());
246 errors.pop_front();
247 }
248
249 // Update limits
250 uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
251
252 if (ticks - last_reset > 1000) {
253 last_reset = ticks;
254 char_count = 0;
255 err_count = 0;
256 n_errors_dropped = 0;
257 warn_count = 0;
258 n_warnings_dropped = 0;
259 }
260 flushing = false;
261}
262
263void RemoteDebugger::send_message(const String &p_message, const Array &p_args) {
264 MutexLock lock(mutex);
265 if (is_peer_connected()) {
266 _put_msg(p_message, p_args);
267 }
268}
269
270void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) {
271 ErrorMessage oe;
272 oe.error = p_err;
273 oe.error_descr = p_descr;
274 oe.source_file = p_file;
275 oe.source_line = p_line;
276 oe.source_func = p_func;
277 oe.warning = p_type == ERR_HANDLER_WARNING;
278 uint64_t time = OS::get_singleton()->get_ticks_msec();
279 oe.hr = time / 3600000;
280 oe.min = (time / 60000) % 60;
281 oe.sec = (time / 1000) % 60;
282 oe.msec = time % 1000;
283 oe.callstack.append_array(script_debugger->get_error_stack_info());
284
285 if (flushing && Thread::get_caller_id() == flush_thread) { // Can't handle recursive errors during flush.
286 return;
287 }
288
289 MutexLock lock(mutex);
290
291 if (oe.warning) {
292 warn_count++;
293 } else {
294 err_count++;
295 }
296
297 if (is_peer_connected()) {
298 if (oe.warning) {
299 if (warn_count > max_warnings_per_second) {
300 n_warnings_dropped++;
301 if (n_warnings_dropped == 1) {
302 // Only print one message about dropping per second
303 ErrorMessage overflow = _create_overflow_error("TOO_MANY_WARNINGS", "Too many warnings! Ignoring warnings for up to 1 second.");
304 errors.push_back(overflow);
305 }
306 } else {
307 errors.push_back(oe);
308 }
309 } else {
310 if (err_count > max_errors_per_second) {
311 n_errors_dropped++;
312 if (n_errors_dropped == 1) {
313 // Only print one message about dropping per second
314 ErrorMessage overflow = _create_overflow_error("TOO_MANY_ERRORS", "Too many errors! Ignoring errors for up to 1 second.");
315 errors.push_back(overflow);
316 }
317 } else {
318 errors.push_back(oe);
319 }
320 }
321 }
322}
323
324void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type) {
325 DebuggerMarshalls::ScriptStackVariable stvar;
326 List<String>::Element *E = p_names.front();
327 List<Variant>::Element *F = p_vals.front();
328 while (E) {
329 stvar.name = E->get();
330 stvar.value = F->get();
331 stvar.type = p_type;
332 send_message("stack_frame_var", stvar.serialize());
333 E = E->next();
334 F = F->next();
335 }
336}
337
338Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, bool &r_captured) {
339 const int idx = p_msg.find(":");
340 r_captured = false;
341 if (idx < 0) { // No prefix, unknown message.
342 return OK;
343 }
344 const String cap = p_msg.substr(0, idx);
345 if (!has_capture(cap)) {
346 return ERR_UNAVAILABLE; // Unknown message...
347 }
348 const String msg = p_msg.substr(idx + 1);
349 return capture_parse(cap, msg, p_data, r_captured);
350}
351
352void RemoteDebugger::_poll_messages() {
353 MutexLock mutex_lock(mutex);
354
355 peer->poll();
356 while (peer->has_message()) {
357 Array cmd = peer->get_message();
358 ERR_CONTINUE(cmd.size() != 3);
359 ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
360 ERR_CONTINUE(cmd[1].get_type() != Variant::INT);
361 ERR_CONTINUE(cmd[2].get_type() != Variant::ARRAY);
362
363 Thread::ID thread = cmd[1];
364
365 if (!messages.has(thread)) {
366 continue; // This thread is not around to receive the messages
367 }
368
369 Message msg;
370 msg.message = cmd[0];
371 msg.data = cmd[2];
372 messages[thread].push_back(msg);
373 }
374}
375
376bool RemoteDebugger::_has_messages() {
377 MutexLock mutex_lock(mutex);
378 return messages.has(Thread::get_caller_id()) && !messages[Thread::get_caller_id()].is_empty();
379}
380
381Array RemoteDebugger::_get_message() {
382 MutexLock mutex_lock(mutex);
383 ERR_FAIL_COND_V(!messages.has(Thread::get_caller_id()), Array());
384 List<Message> &message_list = messages[Thread::get_caller_id()];
385 ERR_FAIL_COND_V(message_list.is_empty(), Array());
386
387 Array msg;
388 msg.resize(2);
389 msg[0] = message_list.front()->get().message;
390 msg[1] = message_list.front()->get().data;
391 message_list.pop_front();
392 return msg;
393}
394
395void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
396 //this function is called when there is a debugger break (bug on script)
397 //or when execution is paused from editor
398
399 {
400 MutexLock lock(mutex);
401 // Tests that require mutex.
402 if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) {
403 return;
404 }
405
406 ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
407
408 if (!peer->can_block()) {
409 return; // Peer does not support blocking IO. We could at least send the error though.
410 }
411 }
412
413 ScriptLanguage *script_lang = script_debugger->get_break_language();
414 const String error_str = script_lang ? script_lang->debug_get_error() : "";
415 Array msg;
416 msg.push_back(p_can_continue);
417 msg.push_back(error_str);
418 ERR_FAIL_NULL(script_lang);
419 msg.push_back(script_lang->debug_get_stack_level_count() > 0);
420 msg.push_back(Thread::get_caller_id() == Thread::get_main_id() ? String(RTR("Main Thread")) : itos(Thread::get_caller_id()));
421 if (allow_focus_steal_fn) {
422 allow_focus_steal_fn();
423 }
424 send_message("debug_enter", msg);
425
426 Input::MouseMode mouse_mode = Input::MOUSE_MODE_VISIBLE;
427
428 if (Thread::get_caller_id() == Thread::get_main_id()) {
429 mouse_mode = Input::get_singleton()->get_mouse_mode();
430 if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
431 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
432 }
433 } else {
434 MutexLock mutex_lock(mutex);
435 messages.insert(Thread::get_caller_id(), List<Message>());
436 }
437
438 mutex.lock();
439 while (is_peer_connected()) {
440 mutex.unlock();
441 flush_output();
442
443 _poll_messages();
444
445 if (_has_messages()) {
446 Array cmd = _get_message();
447
448 ERR_CONTINUE(cmd.size() != 2);
449 ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
450 ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
451
452 String command = cmd[0];
453 Array data = cmd[1];
454
455 if (command == "step") {
456 script_debugger->set_depth(-1);
457 script_debugger->set_lines_left(1);
458 break;
459
460 } else if (command == "next") {
461 script_debugger->set_depth(0);
462 script_debugger->set_lines_left(1);
463 break;
464
465 } else if (command == "continue") {
466 script_debugger->set_depth(-1);
467 script_debugger->set_lines_left(-1);
468 break;
469
470 } else if (command == "break") {
471 ERR_PRINT("Got break when already broke!");
472 break;
473
474 } else if (command == "get_stack_dump") {
475 DebuggerMarshalls::ScriptStackDump dump;
476 int slc = script_lang->debug_get_stack_level_count();
477 for (int i = 0; i < slc; i++) {
478 ScriptLanguage::StackInfo frame;
479 frame.file = script_lang->debug_get_stack_level_source(i);
480 frame.line = script_lang->debug_get_stack_level_line(i);
481 frame.func = script_lang->debug_get_stack_level_function(i);
482 dump.frames.push_back(frame);
483 }
484 send_message("stack_dump", dump.serialize());
485
486 } else if (command == "get_stack_frame_vars") {
487 ERR_FAIL_COND(data.size() != 1);
488 ERR_FAIL_NULL(script_lang);
489 int lv = data[0];
490
491 List<String> members;
492 List<Variant> member_vals;
493 if (ScriptInstance *inst = script_lang->debug_get_stack_level_instance(lv)) {
494 members.push_back("self");
495 member_vals.push_back(inst->get_owner());
496 }
497 script_lang->debug_get_stack_level_members(lv, &members, &member_vals);
498 ERR_FAIL_COND(members.size() != member_vals.size());
499
500 List<String> locals;
501 List<Variant> local_vals;
502 script_lang->debug_get_stack_level_locals(lv, &locals, &local_vals);
503 ERR_FAIL_COND(locals.size() != local_vals.size());
504
505 List<String> globals;
506 List<Variant> globals_vals;
507 script_lang->debug_get_globals(&globals, &globals_vals);
508 ERR_FAIL_COND(globals.size() != globals_vals.size());
509
510 Array var_size;
511 var_size.push_back(local_vals.size() + member_vals.size() + globals_vals.size());
512 send_message("stack_frame_vars", var_size);
513 _send_stack_vars(locals, local_vals, 0);
514 _send_stack_vars(members, member_vals, 1);
515 _send_stack_vars(globals, globals_vals, 2);
516
517 } else if (command == "reload_scripts") {
518 reload_all_scripts = true;
519
520 } else if (command == "breakpoint") {
521 ERR_FAIL_COND(data.size() < 3);
522 bool set = data[2];
523 if (set) {
524 script_debugger->insert_breakpoint(data[1], data[0]);
525 } else {
526 script_debugger->remove_breakpoint(data[1], data[0]);
527 }
528
529 } else if (command == "set_skip_breakpoints") {
530 ERR_FAIL_COND(data.size() < 1);
531 script_debugger->set_skip_breakpoints(data[0]);
532 } else {
533 bool captured = false;
534 ERR_CONTINUE(_try_capture(command, data, captured) != OK);
535 if (!captured) {
536 WARN_PRINT("Unknown message received from debugger: " + command);
537 }
538 }
539 } else {
540 OS::get_singleton()->delay_usec(10000);
541 if (Thread::get_caller_id() == Thread::get_main_id()) {
542 // If this is a busy loop on the main thread, events still need to be processed.
543 OS::get_singleton()->process_and_drop_events();
544 }
545 }
546 }
547
548 send_message("debug_exit", Array());
549
550 if (Thread::get_caller_id() == Thread::get_main_id()) {
551 if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
552 Input::get_singleton()->set_mouse_mode(mouse_mode);
553 }
554 } else {
555 MutexLock mutex_lock(mutex);
556 messages.erase(Thread::get_caller_id());
557 }
558}
559
560void RemoteDebugger::poll_events(bool p_is_idle) {
561 if (peer.is_null()) {
562 return;
563 }
564
565 flush_output();
566
567 _poll_messages();
568
569 while (_has_messages()) {
570 Array arr = _get_message();
571
572 ERR_CONTINUE(arr.size() != 2);
573 ERR_CONTINUE(arr[0].get_type() != Variant::STRING);
574 ERR_CONTINUE(arr[1].get_type() != Variant::ARRAY);
575
576 const String cmd = arr[0];
577 const int idx = cmd.find(":");
578 bool parsed = false;
579 if (idx < 0) { // Not prefix, use scripts capture.
580 capture_parse("core", cmd, arr[1], parsed);
581 continue;
582 }
583
584 const String cap = cmd.substr(0, idx);
585 if (!has_capture(cap)) {
586 continue; // Unknown message...
587 }
588
589 const String msg = cmd.substr(idx + 1);
590 capture_parse(cap, msg, arr[1], parsed);
591 }
592
593 // Reload scripts during idle poll only.
594 if (p_is_idle && reload_all_scripts) {
595 for (int i = 0; i < ScriptServer::get_language_count(); i++) {
596 ScriptServer::get_language(i)->reload_all_scripts();
597 }
598 reload_all_scripts = false;
599 }
600}
601
602Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
603 r_captured = true;
604 if (p_cmd == "reload_scripts") {
605 reload_all_scripts = true;
606
607 } else if (p_cmd == "breakpoint") {
608 ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA);
609 bool set = p_data[2];
610 if (set) {
611 script_debugger->insert_breakpoint(p_data[1], p_data[0]);
612 } else {
613 script_debugger->remove_breakpoint(p_data[1], p_data[0]);
614 }
615
616 } else if (p_cmd == "set_skip_breakpoints") {
617 ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
618 script_debugger->set_skip_breakpoints(p_data[0]);
619 } else if (p_cmd == "break") {
620 script_debugger->debug(script_debugger->get_break_language());
621 } else {
622 r_captured = false;
623 }
624 return OK;
625}
626
627Error RemoteDebugger::_profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
628 r_captured = false;
629 ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
630 ERR_FAIL_COND_V(p_data[0].get_type() != Variant::BOOL, ERR_INVALID_DATA);
631 ERR_FAIL_COND_V(!has_profiler(p_cmd), ERR_UNAVAILABLE);
632 Array opts;
633 if (p_data.size() > 1) { // Optional profiler parameters.
634 ERR_FAIL_COND_V(p_data[1].get_type() != Variant::ARRAY, ERR_INVALID_DATA);
635 opts = p_data[1];
636 }
637 r_captured = true;
638 profiler_enable(p_cmd, p_data[0], opts);
639 return OK;
640}
641
642RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
643 peer = p_peer;
644 max_chars_per_second = GLOBAL_GET("network/limits/debugger/max_chars_per_second");
645 max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second");
646 max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second");
647
648 // Performance Profiler
649 Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
650 if (perf) {
651 performance_profiler = Ref<PerformanceProfiler>(memnew(PerformanceProfiler(perf)));
652 performance_profiler->bind("performance");
653 profiler_enable("performance", true);
654 }
655
656 // Core and profiler captures.
657 Capture core_cap(this,
658 [](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
659 return static_cast<RemoteDebugger *>(p_user)->_core_capture(p_cmd, p_data, r_captured);
660 });
661 register_message_capture("core", core_cap);
662 Capture profiler_cap(this,
663 [](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
664 return static_cast<RemoteDebugger *>(p_user)->_profiler_capture(p_cmd, p_data, r_captured);
665 });
666 register_message_capture("profiler", profiler_cap);
667
668 // Error handlers
669 phl.printfunc = _print_handler;
670 phl.userdata = this;
671 add_print_handler(&phl);
672
673 eh.errfunc = _err_handler;
674 eh.userdata = this;
675 add_error_handler(&eh);
676
677 messages.insert(Thread::get_main_id(), List<Message>());
678}
679
680RemoteDebugger::~RemoteDebugger() {
681 remove_print_handler(&phl);
682 remove_error_handler(&eh);
683}
684