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