Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/debugger/script_editor_debugger.cpp
20836 views
1
/**************************************************************************/
2
/* script_editor_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 "script_editor_debugger.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/debugger/debugger_marshalls.h"
35
#include "core/debugger/remote_debugger.h"
36
#include "core/string/ustring.h"
37
#include "core/version.h"
38
#include "editor/debugger/editor_debugger_plugin.h"
39
#include "editor/debugger/editor_expression_evaluator.h"
40
#include "editor/debugger/editor_performance_profiler.h"
41
#include "editor/debugger/editor_profiler.h"
42
#include "editor/debugger/editor_visual_profiler.h"
43
#include "editor/docks/filesystem_dock.h"
44
#include "editor/docks/inspector_dock.h"
45
#include "editor/editor_log.h"
46
#include "editor/editor_node.h"
47
#include "editor/editor_string_names.h"
48
#include "editor/file_system/editor_file_system.h"
49
#include "editor/gui/editor_file_dialog.h"
50
#include "editor/gui/editor_toaster.h"
51
#include "editor/inspector/editor_property_name_processor.h"
52
#include "editor/scene/3d/node_3d_editor_plugin.h"
53
#include "editor/scene/canvas_item_editor_plugin.h"
54
#include "editor/settings/editor_settings.h"
55
#include "editor/themes/editor_scale.h"
56
#include "main/performance.h"
57
#include "scene/3d/camera_3d.h"
58
#include "scene/debugger/scene_debugger_object.h"
59
#include "scene/gui/button.h"
60
#include "scene/gui/dialogs.h"
61
#include "scene/gui/grid_container.h"
62
#include "scene/gui/label.h"
63
#include "scene/gui/line_edit.h"
64
#include "scene/gui/margin_container.h"
65
#include "scene/gui/separator.h"
66
#include "scene/gui/split_container.h"
67
#include "scene/gui/tab_container.h"
68
#include "scene/gui/tree.h"
69
#include "servers/debugger/servers_debugger.h"
70
#include "servers/display/display_server.h"
71
72
using CameraOverride = EditorDebuggerNode::CameraOverride;
73
74
void ScriptEditorDebugger::_put_msg(const String &p_message, const Array &p_data, uint64_t p_thread_id) {
75
ERR_FAIL_COND(p_thread_id == Thread::UNASSIGNED_ID);
76
if (is_session_active()) {
77
Array msg = { p_message, p_thread_id, p_data };
78
Error err = peer->put_message(msg);
79
ERR_FAIL_COND_MSG(err != OK, vformat("Failed to send message %d", err));
80
}
81
}
82
83
void ScriptEditorDebugger::debug_copy() {
84
String msg = reason->get_text();
85
if (msg.is_empty()) {
86
return;
87
}
88
DisplayServer::get_singleton()->clipboard_set(msg);
89
}
90
91
void ScriptEditorDebugger::debug_skip_breakpoints() {
92
skip_breakpoints_value = !skip_breakpoints_value;
93
if (skip_breakpoints_value) {
94
skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn")));
95
} else {
96
skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff")));
97
}
98
99
Array msg = { skip_breakpoints_value };
100
_put_msg("set_skip_breakpoints", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
101
}
102
103
void ScriptEditorDebugger::debug_ignore_error_breaks() {
104
ignore_error_breaks_value = !ignore_error_breaks_value;
105
if (ignore_error_breaks_value) {
106
ignore_error_breaks->set_button_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
107
} else {
108
ignore_error_breaks->set_button_icon(get_theme_icon(SNAME("Notification"), SNAME("EditorIcons")));
109
}
110
111
Array msg = { ignore_error_breaks_value };
112
_put_msg("set_ignore_error_breaks", msg);
113
}
114
115
void ScriptEditorDebugger::debug_out() {
116
ERR_FAIL_COND(!is_breaked());
117
118
_put_msg("out", Array(), debugging_thread_id);
119
_clear_execution();
120
}
121
122
void ScriptEditorDebugger::debug_next() {
123
ERR_FAIL_COND(!is_breaked());
124
125
_put_msg("next", Array(), debugging_thread_id);
126
_clear_execution();
127
}
128
129
void ScriptEditorDebugger::debug_step() {
130
ERR_FAIL_COND(!is_breaked());
131
132
_put_msg("step", Array(), debugging_thread_id);
133
_clear_execution();
134
}
135
136
void ScriptEditorDebugger::debug_break() {
137
ERR_FAIL_COND(is_breaked());
138
139
_put_msg("break", Array());
140
_mute_audio_on_break(true);
141
}
142
143
void ScriptEditorDebugger::debug_continue() {
144
ERR_FAIL_COND(!is_breaked());
145
146
// Allow focus stealing only if we actually run this client for security.
147
if (remote_pid && EditorNode::get_singleton()->has_child_process(remote_pid)) {
148
DisplayServer::get_singleton()->enable_for_stealing_focus(remote_pid);
149
}
150
151
_clear_execution();
152
_put_msg("continue", Array(), debugging_thread_id);
153
_put_msg("servers:foreground", Array());
154
_mute_audio_on_break(false);
155
}
156
157
void ScriptEditorDebugger::update_tabs() {
158
if (error_count == 0 && warning_count == 0) {
159
errors_tab->set_name(TTRC("Errors"));
160
tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), Ref<Texture2D>());
161
} else {
162
errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")");
163
if (error_count >= 1 && warning_count >= 1) {
164
tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("ErrorWarning")));
165
} else if (error_count >= 1) {
166
tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("Error")));
167
} else {
168
tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("Warning")));
169
}
170
}
171
}
172
173
void ScriptEditorDebugger::clear_style() {
174
tabs->remove_theme_style_override(SceneStringName(panel));
175
}
176
177
void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) {
178
Array msg = { p_id, p_file };
179
_put_msg("scene:save_node", msg);
180
}
181
182
void ScriptEditorDebugger::_file_selected(const String &p_file) {
183
switch (file_dialog_purpose) {
184
case SAVE_MONITORS_CSV: {
185
Error err;
186
Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);
187
188
if (err != OK) {
189
ERR_PRINT("Failed to open " + p_file);
190
return;
191
}
192
Vector<String> line;
193
line.resize(Performance::MONITOR_MAX);
194
195
// signatures
196
for (int i = 0; i < Performance::MONITOR_MAX; i++) {
197
line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i));
198
}
199
file->store_csv_line(line);
200
201
// values
202
Vector<List<float>::Element *> iterators;
203
iterators.resize(Performance::MONITOR_MAX);
204
bool continue_iteration = false;
205
for (int i = 0; i < Performance::MONITOR_MAX; i++) {
206
iterators.write[i] = performance_profiler->get_monitor_data(Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)))->back();
207
continue_iteration = continue_iteration || iterators[i];
208
}
209
while (continue_iteration) {
210
continue_iteration = false;
211
for (int i = 0; i < Performance::MONITOR_MAX; i++) {
212
if (iterators[i]) {
213
line.write[i] = String::num_real(iterators[i]->get());
214
iterators.write[i] = iterators[i]->prev();
215
} else {
216
line.write[i] = "";
217
}
218
continue_iteration = continue_iteration || iterators[i];
219
}
220
file->store_csv_line(line);
221
}
222
file->store_string("\n");
223
224
Vector<Vector<String>> profiler_data = profiler->get_data_as_csv();
225
for (int i = 0; i < profiler_data.size(); i++) {
226
file->store_csv_line(profiler_data[i]);
227
}
228
} break;
229
case SAVE_VRAM_CSV: {
230
Error err;
231
Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);
232
233
if (err != OK) {
234
ERR_PRINT("Failed to open " + p_file);
235
return;
236
}
237
238
Vector<String> headers;
239
headers.resize(vmem_tree->get_columns());
240
for (int i = 0; i < vmem_tree->get_columns(); ++i) {
241
headers.write[i] = vmem_tree->get_column_title(i);
242
}
243
file->store_csv_line(headers);
244
245
if (vmem_tree->get_root()) {
246
TreeItem *ti = vmem_tree->get_root()->get_first_child();
247
while (ti) {
248
Vector<String> values;
249
values.resize(vmem_tree->get_columns());
250
for (int i = 0; i < vmem_tree->get_columns(); ++i) {
251
values.write[i] = ti->get_text(i);
252
}
253
file->store_csv_line(values);
254
255
ti = ti->get_next();
256
}
257
}
258
} break;
259
}
260
}
261
262
void ScriptEditorDebugger::request_remote_tree() {
263
_put_msg("scene:request_scene_tree", Array());
264
}
265
266
const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {
267
return scene_tree;
268
}
269
270
void ScriptEditorDebugger::request_remote_evaluate(const String &p_expression, int p_stack_frame) {
271
Array msg = { p_expression, p_stack_frame };
272
_put_msg("evaluate", msg);
273
}
274
275
void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value, const String &p_field) {
276
Array msg = { p_obj_id, p_prop };
277
278
Ref<Resource> res = p_value;
279
if (res.is_valid() && !res->get_path().is_empty()) {
280
msg.append(res->get_path());
281
} else {
282
msg.append(p_value);
283
}
284
285
if (p_field.is_empty()) {
286
_put_msg("scene:set_object_property", msg);
287
} else {
288
msg.push_back(p_field);
289
_put_msg("scene:set_object_property_field", msg);
290
}
291
}
292
293
void ScriptEditorDebugger::request_remote_objects(const TypedArray<uint64_t> &p_obj_ids, bool p_update_selection) {
294
ERR_FAIL_COND(p_obj_ids.is_empty());
295
Array msg = { p_obj_ids.duplicate(), p_update_selection };
296
_put_msg("scene:inspect_objects", msg);
297
}
298
299
void ScriptEditorDebugger::clear_inspector(bool p_send_msg) {
300
inspector->clear_remote_inspector();
301
if (p_send_msg) {
302
_put_msg("scene:clear_selection", Array());
303
}
304
}
305
306
void ScriptEditorDebugger::_remote_object_selected(ObjectID p_id) {
307
emit_signal(SNAME("remote_objects_requested"), Array{ p_id });
308
}
309
310
void ScriptEditorDebugger::_remote_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field) {
311
for (const KeyValue<Variant, Variant> &kv : p_values) {
312
update_remote_object(ObjectID(static_cast<uint64_t>(kv.key)), p_prop, kv.value, p_field);
313
}
314
request_remote_objects(p_values.keys(), false);
315
}
316
317
void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const String &p_property) {
318
emit_signal(SNAME("remote_object_property_updated"), p_id, p_property);
319
}
320
321
void ScriptEditorDebugger::_video_mem_request() {
322
_put_msg("servers:memory", Array());
323
}
324
325
void ScriptEditorDebugger::_video_mem_export() {
326
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
327
file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
328
file_dialog->clear_filters();
329
file_dialog_purpose = SAVE_VRAM_CSV;
330
file_dialog->popup_file_dialog();
331
}
332
333
Size2 ScriptEditorDebugger::get_minimum_size() const {
334
Size2 ms = MarginContainer::get_minimum_size();
335
ms.y = MAX(ms.y, 250 * EDSCALE);
336
return ms;
337
}
338
339
void ScriptEditorDebugger::_thread_debug_enter(uint64_t p_thread_id) {
340
ERR_FAIL_COND(!threads_debugged.has(p_thread_id));
341
ThreadDebugged &td = threads_debugged[p_thread_id];
342
_set_reason_text(td.error, MESSAGE_ERROR);
343
emit_signal(SNAME("breaked"), true, td.can_debug, td.error, td.has_stackdump);
344
_mute_audio_on_break(true);
345
if (!td.error.is_empty() && EDITOR_GET("debugger/auto_switch_to_stack_trace")) {
346
tabs->set_current_tab(0);
347
}
348
inspector->clear_cache(); // Take a chance to force remote objects update.
349
_put_msg("get_stack_dump", Array(), p_thread_id);
350
}
351
352
void ScriptEditorDebugger::_select_thread(int p_index) {
353
debugging_thread_id = threads->get_item_metadata(threads->get_selected());
354
_thread_debug_enter(debugging_thread_id);
355
}
356
357
void ScriptEditorDebugger::_msg_debug_enter(uint64_t p_thread_id, const Array &p_data) {
358
ERR_FAIL_COND(p_data.size() != 4);
359
360
const Thread::ID caller_id = p_data[3];
361
362
ThreadDebugged td;
363
td.name = (caller_id == Thread::get_main_id()) ? TTR("Main Thread") : itos(caller_id);
364
td.error = p_data[1];
365
td.can_debug = p_data[0];
366
td.has_stackdump = p_data[2];
367
td.thread_id = p_thread_id;
368
static uint32_t order_inc = 0;
369
td.debug_order = order_inc++;
370
371
threads_debugged.insert(p_thread_id, td);
372
373
if (threads_debugged.size() == 1) {
374
// First thread that requests debug
375
debugging_thread_id = p_thread_id;
376
_thread_debug_enter(p_thread_id);
377
can_request_idle_draw = true;
378
if (is_move_to_foreground()) {
379
DisplayServer::get_singleton()->window_move_to_foreground();
380
}
381
profiler->set_enabled(false, false);
382
visual_profiler->set_enabled(false);
383
}
384
_update_buttons_state();
385
}
386
387
void ScriptEditorDebugger::_msg_debug_exit(uint64_t p_thread_id, const Array &p_data) {
388
threads_debugged.erase(p_thread_id);
389
if (p_thread_id == debugging_thread_id) {
390
_clear_execution();
391
if (threads_debugged.is_empty()) {
392
debugging_thread_id = Thread::UNASSIGNED_ID;
393
} else {
394
// Find next thread to debug.
395
uint32_t min_order = 0xFFFFFFFF;
396
uint64_t next_thread = Thread::UNASSIGNED_ID;
397
for (KeyValue<uint64_t, ThreadDebugged> T : threads_debugged) {
398
if (T.value.debug_order < min_order) {
399
min_order = T.value.debug_order;
400
next_thread = T.key;
401
}
402
}
403
404
debugging_thread_id = next_thread;
405
}
406
407
if (debugging_thread_id == Thread::UNASSIGNED_ID) {
408
// Nothing else to debug.
409
profiler->set_enabled(true, false);
410
profiler->disable_seeking();
411
412
visual_profiler->set_enabled(true);
413
414
_set_reason_text(TTRC("Execution resumed."), MESSAGE_SUCCESS);
415
emit_signal(SNAME("breaked"), false, false, "", false);
416
_mute_audio_on_break(false);
417
418
_update_buttons_state();
419
} else {
420
_thread_debug_enter(debugging_thread_id);
421
}
422
} else {
423
_update_buttons_state();
424
}
425
}
426
427
void ScriptEditorDebugger::_msg_set_pid(uint64_t p_thread_id, const Array &p_data) {
428
ERR_FAIL_COND(p_data.is_empty());
429
remote_pid = p_data[0];
430
// We emit the started signal after we've set the PID.
431
emit_signal(SNAME("started"));
432
}
433
434
void ScriptEditorDebugger::_msg_scene_click_ctrl(uint64_t p_thread_id, const Array &p_data) {
435
ERR_FAIL_COND(p_data.size() < 2);
436
clicked_ctrl->set_text(p_data[0]);
437
clicked_ctrl_type->set_text(p_data[1]);
438
}
439
440
void ScriptEditorDebugger::_msg_scene_scene_tree(uint64_t p_thread_id, const Array &p_data) {
441
scene_tree->nodes.clear();
442
scene_tree->deserialize(p_data);
443
emit_signal(SNAME("remote_tree_updated"));
444
_update_buttons_state();
445
}
446
447
void ScriptEditorDebugger::_msg_scene_inspect_objects(uint64_t p_thread_id, const Array &p_data) {
448
ERR_FAIL_COND(p_data.is_empty());
449
EditorDebuggerRemoteObjects *objs = inspector->set_objects(p_data);
450
if (objs && EditorDebuggerNode::get_singleton()->match_remote_selection(objs->remote_object_ids)) {
451
EditorDebuggerNode::get_singleton()->stop_waiting_inspection();
452
453
emit_signal(SNAME("remote_objects_updated"), objs);
454
}
455
}
456
457
#ifndef DISABLE_DEPRECATED
458
void ScriptEditorDebugger::_msg_scene_inspect_object(uint64_t p_thread_id, const Array &p_data) {
459
ERR_FAIL_COND(p_data.is_empty());
460
// Legacy compatibility: convert single object response to new format.
461
// p_data is [id, className, properties] - wrap it as first element of array for new handler.
462
Array wrapped_data;
463
wrapped_data.push_back(p_data);
464
_msg_scene_inspect_objects(p_thread_id, wrapped_data);
465
}
466
#endif // DISABLE_DEPRECATED
467
468
void ScriptEditorDebugger::_msg_scene_debug_mute_audio(uint64_t p_thread_id, const Array &p_data) {
469
ERR_FAIL_COND(p_data.is_empty());
470
// This is handled by SceneDebugger, we need to ignore here to not show a warning.
471
}
472
473
void ScriptEditorDebugger::_msg_servers_memory_usage(uint64_t p_thread_id, const Array &p_data) {
474
vmem_tree->clear();
475
TreeItem *root = vmem_tree->create_item();
476
ServersDebugger::ResourceUsage usage;
477
usage.deserialize(p_data);
478
479
uint64_t total = 0;
480
481
for (const ServersDebugger::ResourceInfo &E : usage.infos) {
482
TreeItem *it = vmem_tree->create_item(root);
483
String type = E.type;
484
int bytes = E.vram;
485
it->set_text(0, E.path);
486
it->set_text(1, type);
487
it->set_text(2, E.format);
488
it->set_text(3, String::humanize_size(bytes));
489
total += bytes;
490
491
// If it does not have a theme icon, just go up the inheritance tree until we find one.
492
if (!has_theme_icon(type, EditorStringName(EditorIcons))) {
493
StringName base_type = type;
494
while (base_type != "Resource" || base_type != "") {
495
base_type = ClassDB::get_parent_class(base_type);
496
if (has_theme_icon(base_type, EditorStringName(EditorIcons))) {
497
type = base_type;
498
break;
499
}
500
}
501
}
502
503
it->set_icon(0, get_editor_theme_icon(type));
504
}
505
506
vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total));
507
vmem_total->set_text(String::humanize_size(total));
508
}
509
510
void ScriptEditorDebugger::_msg_servers_drawn(uint64_t p_thread_id, const Array &p_data) {
511
can_request_idle_draw = true;
512
}
513
514
void ScriptEditorDebugger::_msg_stack_dump(uint64_t p_thread_id, const Array &p_data) {
515
DebuggerMarshalls::ScriptStackDump stack;
516
stack.deserialize(p_data);
517
518
stack_dump->clear();
519
inspector->clear_stack_variables();
520
TreeItem *r = stack_dump->create_item();
521
522
Array stack_dump_info;
523
524
int i = 0;
525
for (List<ScriptLanguage::StackInfo>::Iterator itr = stack.frames.begin(); itr != stack.frames.end(); ++itr, ++i) {
526
TreeItem *s = stack_dump->create_item(r);
527
Dictionary d;
528
d["frame"] = i;
529
d["file"] = itr->file;
530
d["function"] = itr->func;
531
d["line"] = itr->line;
532
stack_dump_info.push_back(d);
533
s->set_metadata(0, d);
534
535
String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + String(d["function"]);
536
s->set_text(0, line);
537
538
if (i == 0) {
539
s->select(0);
540
}
541
}
542
emit_signal(SNAME("stack_dump"), stack_dump_info);
543
}
544
545
void ScriptEditorDebugger::_msg_stack_frame_vars(uint64_t p_thread_id, const Array &p_data) {
546
inspector->clear_stack_variables();
547
ERR_FAIL_COND(p_data.size() != 1);
548
emit_signal(SNAME("stack_frame_vars"), p_data[0]);
549
}
550
551
void ScriptEditorDebugger::_msg_stack_frame_var(uint64_t p_thread_id, const Array &p_data) {
552
inspector->add_stack_variable(p_data);
553
emit_signal(SNAME("stack_frame_var"), p_data);
554
}
555
556
void ScriptEditorDebugger::_msg_output(uint64_t p_thread_id, const Array &p_data) {
557
ERR_FAIL_COND(p_data.size() != 2);
558
559
ERR_FAIL_COND(p_data[0].get_type() != Variant::PACKED_STRING_ARRAY);
560
Vector<String> output_strings = p_data[0];
561
562
ERR_FAIL_COND(p_data[1].get_type() != Variant::PACKED_INT32_ARRAY);
563
Vector<int> output_types = p_data[1];
564
565
ERR_FAIL_COND(output_strings.size() != output_types.size());
566
567
for (int i = 0; i < output_strings.size(); i++) {
568
RemoteDebugger::MessageType type = (RemoteDebugger::MessageType)(int)(output_types[i]);
569
EditorLog::MessageType msg_type;
570
switch (type) {
571
case RemoteDebugger::MESSAGE_TYPE_LOG: {
572
msg_type = EditorLog::MSG_TYPE_STD;
573
} break;
574
case RemoteDebugger::MESSAGE_TYPE_LOG_RICH: {
575
msg_type = EditorLog::MSG_TYPE_STD_RICH;
576
} break;
577
case RemoteDebugger::MESSAGE_TYPE_ERROR: {
578
msg_type = EditorLog::MSG_TYPE_ERROR;
579
} break;
580
default: {
581
WARN_PRINT("Unhandled script debugger message type: " + itos(type));
582
msg_type = EditorLog::MSG_TYPE_STD;
583
} break;
584
}
585
EditorNode::get_log()->add_message(output_strings[i], msg_type);
586
emit_signal(SceneStringName(output), output_strings[i], msg_type);
587
}
588
}
589
590
void ScriptEditorDebugger::_msg_performance_profile_frame(uint64_t p_thread_id, const Array &p_data) {
591
Vector<float> frame_data;
592
frame_data.resize(p_data.size());
593
for (int i = 0; i < p_data.size(); i++) {
594
frame_data.write[i] = p_data[i];
595
}
596
performance_profiler->add_profile_frame(frame_data);
597
}
598
599
void ScriptEditorDebugger::_msg_visual_hardware_info(uint64_t p_thread_id, const Array &p_data) {
600
const String cpu_name = p_data[0];
601
const String gpu_name = p_data[1];
602
visual_profiler->set_hardware_info(cpu_name, gpu_name);
603
}
604
605
void ScriptEditorDebugger::_msg_visual_profile_frame(uint64_t p_thread_id, const Array &p_data) {
606
ServersDebugger::VisualProfilerFrame frame;
607
frame.deserialize(p_data);
608
609
EditorVisualProfiler::Metric metric;
610
metric.areas.resize(frame.areas.size());
611
metric.frame_number = frame.frame_number;
612
metric.valid = true;
613
614
{
615
EditorVisualProfiler::Metric::Area *areas_ptr = metric.areas.ptrw();
616
for (int i = 0; i < frame.areas.size(); i++) {
617
areas_ptr[i].name = frame.areas[i].name;
618
areas_ptr[i].cpu_time = frame.areas[i].cpu_msec;
619
areas_ptr[i].gpu_time = frame.areas[i].gpu_msec;
620
}
621
}
622
visual_profiler->add_frame_metric(metric);
623
}
624
625
void ScriptEditorDebugger::_msg_error(uint64_t p_thread_id, const Array &p_data) {
626
DebuggerMarshalls::OutputError oe;
627
ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message");
628
629
// Format time.
630
Array time_vals = { oe.hr, oe.min, oe.sec, oe.msec };
631
bool e;
632
String time = String("%d:%02d:%02d:%03d").sprintf(time_vals, &e);
633
634
// Rest of the error data.
635
bool source_is_project_file = oe.source_file.begins_with("res://");
636
637
// Metadata to highlight error line in scripts.
638
Array source_meta = { oe.source_file, oe.source_line };
639
640
// Create error tree to display above error or warning details.
641
TreeItem *r = error_tree->get_root();
642
if (!r) {
643
r = error_tree->create_item();
644
}
645
646
// Also provide the relevant details as tooltip to quickly check without
647
// uncollapsing the tree.
648
String tooltip = oe.warning ? TTRC("Warning:") : TTRC("Error:");
649
650
TreeItem *error = error_tree->create_item(r);
651
if (oe.warning) {
652
error->set_meta("_is_warning", true);
653
} else {
654
error->set_meta("_is_error", true);
655
}
656
error->set_collapsed(true);
657
658
error->set_icon(0, get_editor_theme_icon(oe.warning ? SNAME("Warning") : SNAME("Error")));
659
error->set_text(0, time);
660
error->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);
661
662
const Color color = get_theme_color(oe.warning ? SNAME("warning_color") : SNAME("error_color"), EditorStringName(Editor));
663
error->set_custom_color(0, color);
664
error->set_custom_color(1, color);
665
666
String error_title;
667
if (!oe.source_func.is_empty() && source_is_project_file) {
668
// If source function is inside the project file.
669
error_title += oe.source_func + ": ";
670
} else if (oe.callstack.size() > 0) {
671
// Otherwise, if available, use the script's stack in the error title.
672
error_title = _format_frame_text(&oe.callstack[0]) + ": ";
673
} else if (!oe.source_func.is_empty()) {
674
// Otherwise try to use the C++ source function.
675
error_title += oe.source_func + ": ";
676
}
677
// If we have a (custom) error message, use it as title, and add a C++ Error
678
// item with the original error condition.
679
error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr;
680
error->set_text(1, error_title);
681
error->set_autowrap_mode(1, TextServer::AUTOWRAP_WORD_SMART);
682
error->set_autowrap_trim_flags(1, 0);
683
tooltip += " " + error_title + "\n";
684
685
// Find the language of the error's source file.
686
String source_language_name = "C++"; // Default value is the old hard-coded one.
687
const String source_file_extension = oe.source_file.get_extension();
688
for (int i = 0; i < ScriptServer::get_language_count(); ++i) {
689
ScriptLanguage *script_language = ScriptServer::get_language(i);
690
if (source_file_extension == script_language->get_extension()) {
691
source_language_name = script_language->get_name();
692
break;
693
}
694
}
695
696
if (!oe.error_descr.is_empty()) {
697
// Add item for C++ error condition.
698
TreeItem *cpp_cond = error_tree->create_item(error);
699
// TRANSLATORS: %s is the name of a language, e.g. C++.
700
cpp_cond->set_text(0, "<" + vformat(TTR("%s Error"), source_language_name) + ">");
701
cpp_cond->set_text(1, oe.error);
702
cpp_cond->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);
703
tooltip += vformat(TTR("%s Error:"), source_language_name) + " " + oe.error + "\n";
704
if (source_is_project_file) {
705
cpp_cond->set_metadata(0, source_meta);
706
}
707
}
708
Vector<uint8_t> v;
709
v.resize(100);
710
711
// Source of the error.
712
String source_txt = (source_is_project_file ? oe.source_file.get_file() : oe.source_file) + ":" + itos(oe.source_line);
713
if (!oe.source_func.is_empty()) {
714
source_txt += " @ " + oe.source_func;
715
if (!oe.source_func.ends_with(")")) {
716
source_txt += "()";
717
}
718
}
719
720
TreeItem *cpp_source = error_tree->create_item(error);
721
// TRANSLATORS: %s is the name of a language, e.g. C++.
722
cpp_source->set_text(0, "<" + vformat(TTR("%s Source"), source_language_name) + ">");
723
cpp_source->set_text(1, source_txt);
724
cpp_source->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);
725
tooltip += vformat(TTR("%s Source:"), source_language_name) + " " + source_txt + "\n";
726
727
// Set metadata to highlight error line in scripts.
728
if (source_is_project_file) {
729
error->set_metadata(0, source_meta);
730
cpp_source->set_metadata(0, source_meta);
731
}
732
733
// Format stack trace.
734
// stack_items_count is the number of elements to parse, with 3 items per frame
735
// of the stack trace (script, method, line).
736
const ScriptLanguage::StackInfo *infos = oe.callstack.ptr();
737
for (unsigned int i = 0; i < (unsigned int)oe.callstack.size(); i++) {
738
TreeItem *stack_trace = error_tree->create_item(error);
739
740
Array meta = { infos[i].file, infos[i].line };
741
stack_trace->set_metadata(0, meta);
742
743
if (i == 0) {
744
stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">");
745
stack_trace->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);
746
if (!source_is_project_file) {
747
// Only override metadata if the source is not inside the project.
748
error->set_metadata(0, meta);
749
}
750
tooltip += TTR("Stack Trace:") + "\n";
751
}
752
753
String frame_txt = _format_frame_text(&infos[i]);
754
tooltip += frame_txt + "\n";
755
stack_trace->set_text(1, frame_txt);
756
}
757
758
error->set_tooltip_text(0, tooltip);
759
error->set_tooltip_text(1, tooltip);
760
761
if (warning_count == 0 && error_count == 0) {
762
expand_all_button->set_disabled(false);
763
collapse_all_button->set_disabled(false);
764
clear_button->set_disabled(false);
765
}
766
767
if (oe.warning) {
768
warning_count++;
769
} else {
770
error_count++;
771
}
772
}
773
774
void ScriptEditorDebugger::_msg_servers_function_signature(uint64_t p_thread_id, const Array &p_data) {
775
// Cache a profiler signature.
776
ServersDebugger::ScriptFunctionSignature sig;
777
sig.deserialize(p_data);
778
profiler_signature[sig.id] = sig.name;
779
}
780
781
void ScriptEditorDebugger::_msg_servers_profile_common(const Array &p_data, const bool p_final) {
782
EditorProfiler::Metric metric;
783
ServersDebugger::ServersProfilerFrame frame;
784
frame.deserialize(p_data);
785
metric.valid = true;
786
metric.frame_number = frame.frame_number;
787
metric.frame_time = frame.frame_time;
788
metric.process_time = frame.process_time;
789
metric.physics_time = frame.physics_time;
790
metric.physics_frame_time = frame.physics_frame_time;
791
792
if (frame.servers.size()) {
793
EditorProfiler::Metric::Category frame_time;
794
frame_time.signature = "category_frame_time";
795
frame_time.name = "Frame Time";
796
frame_time.total_time = metric.frame_time;
797
798
EditorProfiler::Metric::Category::Item item;
799
item.calls = 1;
800
item.line = 0;
801
802
item.name = "Physics Time";
803
item.total = metric.physics_time;
804
item.self = item.total;
805
item.signature = "physics_time";
806
807
frame_time.items.push_back(item);
808
809
item.name = "Process Time";
810
item.total = metric.process_time;
811
item.self = item.total;
812
item.signature = "process_time";
813
814
frame_time.items.push_back(item);
815
816
item.name = "Physics Frame Time";
817
item.total = metric.physics_frame_time;
818
item.self = item.total;
819
item.signature = "physics_frame_time";
820
821
frame_time.items.push_back(item);
822
823
metric.categories.push_back(frame_time);
824
}
825
826
for (const ServersDebugger::ServerInfo &srv : frame.servers) {
827
EditorProfiler::Metric::Category c;
828
const String name = srv.name;
829
c.name = EditorPropertyNameProcessor::get_singleton()->process_name(name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);
830
c.items.resize(srv.functions.size());
831
c.total_time = 0;
832
c.signature = "categ::" + name;
833
int j = 0;
834
for (List<ServersDebugger::ServerFunctionInfo>::ConstIterator itr = srv.functions.begin(); itr != srv.functions.end(); ++itr, ++j) {
835
EditorProfiler::Metric::Category::Item item;
836
item.calls = 1;
837
item.line = 0;
838
item.name = itr->name;
839
item.self = itr->time;
840
item.total = item.self;
841
item.signature = "categ::" + name + "::" + item.name;
842
item.name = EditorPropertyNameProcessor::get_singleton()->process_name(item.name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);
843
c.total_time += item.total;
844
c.items.write[j] = item;
845
}
846
metric.categories.push_back(c);
847
}
848
849
EditorProfiler::Metric::Category funcs;
850
funcs.total_time = frame.script_time;
851
funcs.items.resize(frame.script_functions.size());
852
funcs.name = "Script Functions";
853
funcs.signature = "script_functions";
854
for (int i = 0; i < frame.script_functions.size(); i++) {
855
int signature = frame.script_functions[i].sig_id;
856
int calls = frame.script_functions[i].call_count;
857
float total = frame.script_functions[i].total_time;
858
float self = frame.script_functions[i].self_time;
859
float internal = frame.script_functions[i].internal_time;
860
861
EditorProfiler::Metric::Category::Item item;
862
if (profiler_signature.has(signature)) {
863
item.signature = profiler_signature[signature];
864
865
String name = profiler_signature[signature];
866
Vector<String> strings = name.split("::");
867
if (strings.size() == 3) {
868
item.name = strings[2];
869
item.script = strings[0];
870
item.line = strings[1].to_int();
871
} else if (strings.size() == 4) { //Built-in scripts have an :: in their name
872
item.name = strings[3];
873
item.script = strings[0] + "::" + strings[1];
874
item.line = strings[2].to_int();
875
}
876
877
} else {
878
item.name = "SigErr " + itos(signature);
879
}
880
881
item.calls = calls;
882
item.self = self;
883
item.total = total;
884
item.internal = internal;
885
funcs.items.write[i] = item;
886
}
887
888
metric.categories.push_back(funcs);
889
890
profiler->add_frame_metric(metric, p_final);
891
}
892
893
void ScriptEditorDebugger::_msg_servers_profile_frame(uint64_t p_thread_id, const Array &p_data) {
894
_msg_servers_profile_common(p_data, false);
895
}
896
void ScriptEditorDebugger::_msg_servers_profile_total(uint64_t p_thread_id, const Array &p_data) {
897
_msg_servers_profile_common(p_data, true);
898
}
899
900
void ScriptEditorDebugger::_msg_request_quit(uint64_t p_thread_id, const Array &p_data) {
901
emit_signal(SNAME("stop_requested"));
902
_stop_and_notify();
903
}
904
905
void ScriptEditorDebugger::_msg_remote_objects_selected(uint64_t p_thread_id, const Array &p_data) {
906
ERR_FAIL_COND(p_data.is_empty());
907
EditorDebuggerRemoteObjects *objs = inspector->set_objects(p_data);
908
if (objs) {
909
EditorDebuggerNode::get_singleton()->stop_waiting_inspection();
910
911
emit_signal(SNAME("remote_objects_updated"), objs);
912
emit_signal(SNAME("remote_tree_select_requested"), objs->remote_object_ids.duplicate());
913
}
914
}
915
916
void ScriptEditorDebugger::_msg_remote_nothing_selected(uint64_t p_thread_id, const Array &p_data) {
917
EditorDebuggerNode::get_singleton()->stop_waiting_inspection();
918
919
emit_signal(SNAME("remote_tree_clear_selection_requested"));
920
}
921
922
void ScriptEditorDebugger::_msg_remote_selection_invalidated(uint64_t p_thread_id, const Array &p_data) {
923
ERR_FAIL_COND(p_data.is_empty());
924
inspector->invalidate_selection_from_cache(p_data[0]);
925
}
926
927
void ScriptEditorDebugger::_msg_show_selection_limit_warning(uint64_t p_thread_id, const Array &p_data) {
928
EditorToaster::get_singleton()->popup_str(vformat(TTR("Some remote nodes were not selected, as the configured maximum selection is %d. This can be changed at \"debugger/max_node_selection\" in the Editor Settings."), EDITOR_GET("debugger/max_node_selection")), EditorToaster::SEVERITY_WARNING);
929
}
930
931
void ScriptEditorDebugger::_msg_performance_profile_names(uint64_t p_thread_id, const Array &p_data) {
932
ERR_FAIL_COND(p_data.size() != 2);
933
Array name_data = p_data[0];
934
Array type_data = p_data[1];
935
936
Vector<StringName> monitors;
937
monitors.resize(name_data.size());
938
for (int i = 0; i < name_data.size(); i++) {
939
ERR_FAIL_COND(name_data[i].get_type() != Variant::STRING_NAME);
940
monitors.set(i, name_data[i]);
941
}
942
943
PackedInt32Array types;
944
types.resize(type_data.size());
945
for (int i = 0; i < type_data.size(); i++) {
946
ERR_FAIL_COND(type_data[i].get_type() != Variant::INT);
947
types.set(i, type_data[i]);
948
}
949
950
performance_profiler->update_monitors(monitors, types);
951
}
952
953
void ScriptEditorDebugger::_msg_filesystem_update_file(uint64_t p_thread_id, const Array &p_data) {
954
ERR_FAIL_COND(p_data.is_empty());
955
if (EditorFileSystem::get_singleton()) {
956
EditorFileSystem::get_singleton()->update_file(p_data[0]);
957
}
958
}
959
960
void ScriptEditorDebugger::_msg_evaluation_return(uint64_t p_thread_id, const Array &p_data) {
961
expression_evaluator->add_value(p_data);
962
}
963
964
void ScriptEditorDebugger::_msg_window_title(uint64_t p_thread_id, const Array &p_data) {
965
ERR_FAIL_COND(p_data.size() != 1);
966
emit_signal(SNAME("remote_window_title_changed"), p_data[0]);
967
}
968
969
void ScriptEditorDebugger::_msg_embed_suspend_toggle(uint64_t p_thread_id, const Array &p_data) {
970
emit_signal(SNAME("embed_shortcut_requested"), EMBED_SUSPEND_TOGGLE);
971
}
972
973
void ScriptEditorDebugger::_msg_embed_next_frame(uint64_t p_thread_id, const Array &p_data) {
974
emit_signal(SNAME("embed_shortcut_requested"), EMBED_NEXT_FRAME);
975
}
976
977
void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data) {
978
emit_signal(SNAME("debug_data"), p_msg, p_data);
979
980
ParseMessageFunc *fn_ptr = parse_message_handlers.getptr(p_msg);
981
if (fn_ptr) {
982
(this->**fn_ptr)(p_thread_id, p_data);
983
} else {
984
int colon_index = p_msg.find_char(':');
985
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
986
987
bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data);
988
if (!parsed) {
989
WARN_PRINT("Unknown message: " + p_msg);
990
}
991
}
992
}
993
994
HashMap<String, ScriptEditorDebugger::ParseMessageFunc> ScriptEditorDebugger::parse_message_handlers;
995
996
void ScriptEditorDebugger::_init_parse_message_handlers() {
997
parse_message_handlers["debug_enter"] = &ScriptEditorDebugger::_msg_debug_enter;
998
parse_message_handlers["debug_exit"] = &ScriptEditorDebugger::_msg_debug_exit;
999
parse_message_handlers["set_pid"] = &ScriptEditorDebugger::_msg_set_pid;
1000
parse_message_handlers["scene:click_ctrl"] = &ScriptEditorDebugger::_msg_scene_click_ctrl;
1001
parse_message_handlers["scene:scene_tree"] = &ScriptEditorDebugger::_msg_scene_scene_tree;
1002
parse_message_handlers["scene:inspect_objects"] = &ScriptEditorDebugger::_msg_scene_inspect_objects;
1003
#ifndef DISABLE_DEPRECATED
1004
parse_message_handlers["scene:inspect_object"] = &ScriptEditorDebugger::_msg_scene_inspect_object;
1005
#endif // DISABLE_DEPRECATED
1006
parse_message_handlers["scene:debug_mute_audio"] = &ScriptEditorDebugger::_msg_scene_debug_mute_audio;
1007
parse_message_handlers["servers:memory_usage"] = &ScriptEditorDebugger::_msg_servers_memory_usage;
1008
parse_message_handlers["servers:drawn"] = &ScriptEditorDebugger::_msg_servers_drawn;
1009
parse_message_handlers["stack_dump"] = &ScriptEditorDebugger::_msg_stack_dump;
1010
parse_message_handlers["stack_frame_vars"] = &ScriptEditorDebugger::_msg_stack_frame_vars;
1011
parse_message_handlers["stack_frame_var"] = &ScriptEditorDebugger::_msg_stack_frame_var;
1012
parse_message_handlers["output"] = &ScriptEditorDebugger::_msg_output;
1013
parse_message_handlers["performance:profile_frame"] = &ScriptEditorDebugger::_msg_performance_profile_frame;
1014
parse_message_handlers["visual:hardware_info"] = &ScriptEditorDebugger::_msg_visual_hardware_info;
1015
parse_message_handlers["visual:profile_frame"] = &ScriptEditorDebugger::_msg_visual_profile_frame;
1016
parse_message_handlers["error"] = &ScriptEditorDebugger::_msg_error;
1017
parse_message_handlers["servers:function_signature"] = &ScriptEditorDebugger::_msg_servers_function_signature;
1018
parse_message_handlers["servers:profile_frame"] = &ScriptEditorDebugger::_msg_servers_profile_frame;
1019
parse_message_handlers["servers:profile_total"] = &ScriptEditorDebugger::_msg_servers_profile_total;
1020
parse_message_handlers["request_quit"] = &ScriptEditorDebugger::_msg_request_quit;
1021
parse_message_handlers["remote_objects_selected"] = &ScriptEditorDebugger::_msg_remote_objects_selected;
1022
parse_message_handlers["remote_nothing_selected"] = &ScriptEditorDebugger::_msg_remote_nothing_selected;
1023
parse_message_handlers["remote_selection_invalidated"] = &ScriptEditorDebugger::_msg_remote_selection_invalidated;
1024
parse_message_handlers["show_selection_limit_warning"] = &ScriptEditorDebugger::_msg_show_selection_limit_warning;
1025
parse_message_handlers["performance:profile_names"] = &ScriptEditorDebugger::_msg_performance_profile_names;
1026
parse_message_handlers["filesystem:update_file"] = &ScriptEditorDebugger::_msg_filesystem_update_file;
1027
parse_message_handlers["evaluation_return"] = &ScriptEditorDebugger::_msg_evaluation_return;
1028
parse_message_handlers["window:title"] = &ScriptEditorDebugger::_msg_window_title;
1029
parse_message_handlers["request_embed_suspend_toggle"] = &ScriptEditorDebugger::_msg_embed_suspend_toggle;
1030
parse_message_handlers["request_embed_next_frame"] = &ScriptEditorDebugger::_msg_embed_next_frame;
1031
}
1032
1033
void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType p_type) {
1034
switch (p_type) {
1035
case MESSAGE_ERROR:
1036
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
1037
break;
1038
case MESSAGE_WARNING:
1039
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
1040
break;
1041
default:
1042
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("success_color"), EditorStringName(Editor)));
1043
break;
1044
}
1045
1046
reason->set_text(p_reason);
1047
1048
_update_reason_content_height();
1049
1050
const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80);
1051
PackedStringArray lines;
1052
for (int i = 0; i < boundaries.size(); i += 2) {
1053
const int start = boundaries[i];
1054
const int end = boundaries[i + 1];
1055
lines.append(p_reason.substr(start, end - start));
1056
}
1057
1058
reason->set_tooltip_text(String("\n").join(lines));
1059
}
1060
1061
void ScriptEditorDebugger::_update_reason_content_height() {
1062
float margin_height = 0;
1063
const Ref<StyleBox> style = reason->get_theme_stylebox(CoreStringName(normal));
1064
if (style.is_valid()) {
1065
margin_height += style->get_content_margin(SIDE_TOP) + style->get_content_margin(SIDE_BOTTOM);
1066
}
1067
1068
const float content_height = margin_height + reason->get_content_height();
1069
1070
float content_max_height = margin_height;
1071
for (int i = 0; i < 3; i++) {
1072
if (i >= reason->get_line_count()) {
1073
break;
1074
}
1075
content_max_height += reason->get_line_height(i);
1076
}
1077
1078
reason->set_custom_minimum_size(Size2(0, CLAMP(content_height, 0, content_max_height)));
1079
}
1080
1081
void ScriptEditorDebugger::_notification(int p_what) {
1082
switch (p_what) {
1083
case NOTIFICATION_POSTINITIALIZE: {
1084
connect("started", callable_mp(expression_evaluator, &EditorExpressionEvaluator::on_start));
1085
} break;
1086
1087
case NOTIFICATION_TRANSLATION_CHANGED: {
1088
if (is_ready()) {
1089
for (TreeItem *file_item = breakpoints_tree->get_root()->get_first_child(); file_item; file_item = file_item->get_next()) {
1090
for (TreeItem *breakpoint_item = file_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {
1091
int line = breakpoint_item->get_meta("line");
1092
breakpoint_item->set_text(0, vformat(TTR("Line %d"), line));
1093
}
1094
}
1095
update_tabs();
1096
}
1097
} break;
1098
1099
case NOTIFICATION_THEME_CHANGED: {
1100
tabs->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));
1101
1102
skip_breakpoints->set_button_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff")));
1103
ignore_error_breaks->set_button_icon(get_editor_theme_icon(ignore_error_breaks_value ? SNAME("NotificationDisabled") : SNAME("Notification")));
1104
ignore_error_breaks->add_theme_color_override("icon_normal_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
1105
ignore_error_breaks->add_theme_color_override("icon_hover_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
1106
ignore_error_breaks->add_theme_color_override("icon_pressed_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
1107
ignore_error_breaks->add_theme_color_override("icon_focus_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
1108
copy->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy")));
1109
step->set_button_icon(get_editor_theme_icon(SNAME("DebugStep")));
1110
next->set_button_icon(get_editor_theme_icon(SNAME("DebugNext")));
1111
out->set_button_icon(get_editor_theme_icon(SNAME("DebugOut")));
1112
dobreak->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
1113
docontinue->set_button_icon(get_editor_theme_icon(SNAME("DebugContinue")));
1114
vmem_notice_icon->set_texture(get_editor_theme_icon(SNAME("NodeInfo")));
1115
vmem_refresh->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
1116
vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));
1117
vmem_item_menu->set_item_icon(VMEM_MENU_SHOW_IN_FILESYSTEM, get_editor_theme_icon(SNAME("ShowInFileSystem")));
1118
vmem_item_menu->set_item_icon(VMEM_MENU_SHOW_IN_EXPLORER, get_editor_theme_icon(SNAME("Filesystem")));
1119
search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
1120
1121
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
1122
reason->add_theme_style_override(SNAME("normal"), get_theme_stylebox(SNAME("normal"), SNAME("Label"))); // Empty stylebox.
1123
1124
const Ref<Font> source_font = get_theme_font(SNAME("output_source"), EditorStringName(EditorFonts));
1125
if (source_font.is_valid()) {
1126
error_tree->add_theme_font_override("font", source_font);
1127
}
1128
const int font_size = get_theme_font_size(SNAME("output_source_size"), EditorStringName(EditorFonts));
1129
error_tree->add_theme_font_size_override("font_size", font_size);
1130
1131
TreeItem *error_root = error_tree->get_root();
1132
if (error_root) {
1133
TreeItem *error = error_root->get_first_child();
1134
while (error) {
1135
if (error->has_meta("_is_warning")) {
1136
error->set_icon(0, get_editor_theme_icon(SNAME("Warning")));
1137
error->set_custom_color(0, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
1138
error->set_custom_color(1, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
1139
} else if (error->has_meta("_is_error")) {
1140
error->set_icon(0, get_editor_theme_icon(SNAME("Error")));
1141
error->set_custom_color(0, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
1142
error->set_custom_color(1, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
1143
}
1144
1145
error = error->get_next();
1146
}
1147
}
1148
} break;
1149
1150
case NOTIFICATION_PROCESS: {
1151
if (is_session_active()) {
1152
peer->poll();
1153
1154
if (camera_override == CameraOverride::OVERRIDE_EDITORS) {
1155
// CanvasItem Editor
1156
{
1157
Dictionary state = CanvasItemEditor::get_singleton()->get_state();
1158
float zoom = state["zoom"];
1159
Point2 offset = state["ofs"];
1160
Transform2D transform;
1161
1162
transform.scale_basis(Size2(zoom, zoom));
1163
transform.columns[2] = -offset * zoom;
1164
1165
Array msg = { transform };
1166
_put_msg("scene:transform_camera_2d", msg);
1167
}
1168
1169
// Node3D Editor
1170
{
1171
Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_last_used_viewport();
1172
const Camera3D *cam = viewport->get_camera_3d();
1173
1174
Array msg = { cam->get_camera_transform() };
1175
if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {
1176
msg.push_back(false);
1177
msg.push_back(cam->get_size());
1178
} else {
1179
msg.push_back(true);
1180
msg.push_back(cam->get_fov());
1181
}
1182
msg.push_back(cam->get_near());
1183
msg.push_back(cam->get_far());
1184
_put_msg("scene:transform_camera_3d", msg);
1185
}
1186
}
1187
1188
if (is_breaked() && can_request_idle_draw) {
1189
_put_msg("servers:draw", Array());
1190
can_request_idle_draw = false;
1191
}
1192
}
1193
1194
const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20;
1195
1196
while (peer.is_valid() && peer->has_message()) {
1197
Array arr = peer->get_message();
1198
if (arr.size() != 3 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::INT || arr[2].get_type() != Variant::ARRAY) {
1199
_stop_and_notify();
1200
ERR_FAIL_MSG("Invalid message format received from peer");
1201
}
1202
1203
_parse_message(arr[0], arr[1], arr[2]);
1204
1205
if (OS::get_singleton()->get_ticks_msec() > until) {
1206
break;
1207
}
1208
}
1209
if (!is_session_active()) {
1210
_stop_and_notify();
1211
break;
1212
};
1213
} break;
1214
}
1215
}
1216
1217
void ScriptEditorDebugger::_clear_execution() {
1218
TreeItem *ti = stack_dump->get_selected();
1219
if (!ti) {
1220
return;
1221
}
1222
1223
Dictionary d = ti->get_metadata(0);
1224
1225
stack_script = ResourceLoader::load(d["file"]);
1226
emit_signal(SNAME("clear_execution"), stack_script);
1227
stack_script.unref();
1228
stack_dump->clear();
1229
inspector->clear_stack_variables();
1230
}
1231
1232
void ScriptEditorDebugger::_set_breakpoint(const String &p_file, const int &p_line, const bool &p_enabled) {
1233
Ref<Script> scr = ResourceLoader::load(p_file);
1234
emit_signal(SNAME("set_breakpoint"), scr, p_line - 1, p_enabled);
1235
scr.unref();
1236
}
1237
1238
void ScriptEditorDebugger::_clear_breakpoints() {
1239
emit_signal(SNAME("clear_breakpoints"));
1240
}
1241
1242
void ScriptEditorDebugger::_breakpoint_tree_clicked() {
1243
TreeItem *selected = breakpoints_tree->get_selected();
1244
if (selected->has_meta("line")) {
1245
emit_signal(SNAME("breakpoint_selected"), selected->get_parent()->get_text(0), int(selected->get_meta("line")));
1246
}
1247
}
1248
1249
String ScriptEditorDebugger::_format_frame_text(const ScriptLanguage::StackInfo *info) {
1250
String text = info->file.get_file() + ":" + itos(info->line) + " @ " + info->func;
1251
if (!text.ends_with(")")) {
1252
text += "()";
1253
}
1254
return text;
1255
}
1256
1257
void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
1258
_clear_errors_list();
1259
stop();
1260
1261
profiler->set_enabled(true, true);
1262
visual_profiler->set_enabled(true);
1263
1264
peer = p_peer;
1265
ERR_FAIL_COND(p_peer.is_null());
1266
1267
performance_profiler->reset();
1268
1269
set_process(true);
1270
camera_override = CameraOverride::OVERRIDE_NONE;
1271
1272
_set_reason_text(TTRC("Debug session started."), MESSAGE_SUCCESS);
1273
_update_buttons_state();
1274
1275
Array quit_keys = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
1276
_put_msg("scene:setup_scene", quit_keys);
1277
1278
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) {
1279
profiler->set_profiling(true);
1280
}
1281
1282
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) {
1283
visual_profiler->set_profiling(true);
1284
}
1285
}
1286
1287
void ScriptEditorDebugger::_update_buttons_state() {
1288
const bool active = is_session_active();
1289
const bool has_editor_tree = active && editor_remote_tree && editor_remote_tree->get_selected();
1290
vmem_refresh->set_disabled(!active);
1291
step->set_disabled(!active || !is_breaked() || !is_debuggable());
1292
next->set_disabled(!active || !is_breaked() || !is_debuggable());
1293
out->set_disabled(!active || !is_breaked() || !is_debuggable());
1294
copy->set_disabled(!active || !is_breaked());
1295
docontinue->set_disabled(!active || !is_breaked());
1296
dobreak->set_disabled(!active || is_breaked());
1297
le_clear->set_disabled(!active);
1298
le_set->set_disabled(!has_editor_tree);
1299
1300
thread_list_updating = true;
1301
LocalVector<ThreadDebugged *> threadss;
1302
for (KeyValue<uint64_t, ThreadDebugged> &I : threads_debugged) {
1303
threadss.push_back(&I.value);
1304
}
1305
threads->set_disabled(threadss.is_empty());
1306
1307
threadss.sort_custom<ThreadSort>();
1308
threads->clear();
1309
int32_t selected_index = -1;
1310
for (uint32_t i = 0; i < threadss.size(); i++) {
1311
if (debugging_thread_id == threadss[i]->thread_id) {
1312
selected_index = i;
1313
}
1314
threads->add_item(threadss[i]->name);
1315
threads->set_item_metadata(threads->get_item_count() - 1, threadss[i]->thread_id);
1316
}
1317
if (selected_index != -1) {
1318
threads->select(selected_index);
1319
}
1320
1321
thread_list_updating = false;
1322
}
1323
1324
void ScriptEditorDebugger::_stop_and_notify() {
1325
stop();
1326
emit_signal(SNAME("stopped"));
1327
_set_reason_text(TTRC("Debug session closed."), MESSAGE_WARNING);
1328
}
1329
1330
void ScriptEditorDebugger::stop() {
1331
set_process(false);
1332
threads_debugged.clear();
1333
debugging_thread_id = Thread::UNASSIGNED_ID;
1334
remote_pid = 0;
1335
_clear_execution();
1336
1337
inspector->clear_cache();
1338
1339
if (peer.is_valid()) {
1340
peer->close();
1341
peer.unref();
1342
reason->set_text("");
1343
reason->set_tooltip_text("");
1344
reason->set_custom_minimum_size(Size2(0, 0));
1345
}
1346
1347
node_path_cache.clear();
1348
res_path_cache.clear();
1349
profiler_signature.clear();
1350
1351
profiler->set_enabled(false, false);
1352
profiler->set_profiling(false);
1353
1354
visual_profiler->set_enabled(false);
1355
visual_profiler->set_profiling(false);
1356
1357
inspector->edit(nullptr);
1358
_update_buttons_state();
1359
}
1360
1361
void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {
1362
Array msg_data = { p_enable };
1363
switch (p_type) {
1364
case PROFILER_VISUAL:
1365
_put_msg("profiler:visual", msg_data);
1366
break;
1367
case PROFILER_SCRIPTS_SERVERS:
1368
if (p_enable) {
1369
// Clear old script signatures. (should we move all this into the profiler?)
1370
profiler_signature.clear();
1371
// Add max funcs options to request.
1372
int max_funcs = EDITOR_GET("debugger/profiler_frame_max_functions");
1373
bool include_native = EDITOR_GET("debugger/profile_native_calls");
1374
Array opts = { CLAMP(max_funcs, 16, 512), include_native };
1375
msg_data.push_back(opts);
1376
}
1377
_put_msg("profiler:servers", msg_data);
1378
break;
1379
default:
1380
ERR_FAIL_MSG("Invalid profiler type");
1381
}
1382
}
1383
1384
void ScriptEditorDebugger::_profiler_seeked() {
1385
if (is_breaked()) {
1386
return;
1387
}
1388
debug_break();
1389
}
1390
1391
void ScriptEditorDebugger::_stack_dump_frame_selected() {
1392
emit_signal(SNAME("stack_frame_selected"));
1393
1394
int frame = get_stack_script_frame();
1395
1396
if (!request_stack_dump(frame)) {
1397
inspector->edit(nullptr);
1398
}
1399
}
1400
1401
void ScriptEditorDebugger::_export_csv() {
1402
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
1403
file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1404
file_dialog_purpose = SAVE_MONITORS_CSV;
1405
file_dialog->popup_file_dialog();
1406
}
1407
1408
String ScriptEditorDebugger::get_var_value(const String &p_var) const {
1409
if (!is_breaked()) {
1410
return String();
1411
}
1412
return inspector->get_stack_variable(p_var);
1413
}
1414
1415
void ScriptEditorDebugger::_resources_reimported(const PackedStringArray &p_resources) {
1416
Array msg = { p_resources };
1417
_put_msg("scene:reload_cached_files", msg);
1418
}
1419
1420
int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
1421
const int *r = node_path_cache.getptr(p_path);
1422
if (r) {
1423
return *r;
1424
}
1425
1426
last_path_id++;
1427
1428
node_path_cache[p_path] = last_path_id;
1429
Array msg = { p_path, last_path_id };
1430
_put_msg("scene:live_node_path", msg);
1431
1432
return last_path_id;
1433
}
1434
1435
int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) {
1436
HashMap<String, int>::Iterator E = res_path_cache.find(p_path);
1437
1438
if (E) {
1439
return E->value;
1440
}
1441
1442
last_path_id++;
1443
1444
res_path_cache[p_path] = last_path_id;
1445
Array msg = { p_path, last_path_id };
1446
_put_msg("scene:live_res_path", msg);
1447
1448
return last_path_id;
1449
}
1450
1451
void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {
1452
if (!p_base || !live_debug || !is_session_active() || !EditorNode::get_singleton()->get_edited_scene()) {
1453
return;
1454
}
1455
1456
Node *node = Object::cast_to<Node>(p_base);
1457
1458
for (int i = 0; i < p_argcount; i++) {
1459
//no pointers, sorry
1460
if (p_args[i]->get_type() == Variant::OBJECT || p_args[i]->get_type() == Variant::RID) {
1461
return;
1462
}
1463
}
1464
1465
if (node) {
1466
NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node);
1467
int pathid = _get_node_path_cache(path);
1468
1469
Array msg = { pathid, p_name };
1470
for (int i = 0; i < p_argcount; i++) {
1471
//no pointers, sorry
1472
msg.push_back(*p_args[i]);
1473
}
1474
_put_msg("scene:live_node_call", msg);
1475
1476
return;
1477
}
1478
1479
Resource *res = Object::cast_to<Resource>(p_base);
1480
1481
if (res && !res->get_path().is_empty()) {
1482
String respath = res->get_path();
1483
int pathid = _get_res_path_cache(respath);
1484
1485
Array msg = { pathid, p_name };
1486
for (int i = 0; i < p_argcount; i++) {
1487
//no pointers, sorry
1488
msg.push_back(*p_args[i]);
1489
}
1490
_put_msg("scene:live_res_call", msg);
1491
1492
return;
1493
}
1494
}
1495
1496
void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p_property, const Variant &p_value) {
1497
if (!p_base || !live_debug || !EditorNode::get_singleton()->get_edited_scene()) {
1498
return;
1499
}
1500
1501
Node *node = Object::cast_to<Node>(p_base);
1502
1503
if (node) {
1504
NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node);
1505
int pathid = _get_node_path_cache(path);
1506
1507
if (p_value.is_ref_counted()) {
1508
Ref<Resource> res = p_value;
1509
if (res.is_valid() && !res->get_path().is_empty()) {
1510
Array msg = { pathid, p_property, res->get_path() };
1511
_put_msg("scene:live_node_prop_res", msg);
1512
}
1513
} else {
1514
Array msg = { pathid, p_property, p_value };
1515
_put_msg("scene:live_node_prop", msg);
1516
}
1517
1518
return;
1519
}
1520
1521
Resource *res = Object::cast_to<Resource>(p_base);
1522
1523
if (res && !res->get_path().is_empty()) {
1524
String respath = res->get_path();
1525
int pathid = _get_res_path_cache(respath);
1526
1527
if (p_value.is_ref_counted()) {
1528
Ref<Resource> res2 = p_value;
1529
if (res2.is_valid() && !res2->get_path().is_empty()) {
1530
Array msg = { pathid, p_property, res2->get_path() };
1531
_put_msg("scene:live_res_prop_res", msg);
1532
}
1533
} else {
1534
Array msg = { pathid, p_property, p_value };
1535
_put_msg("scene:live_res_prop", msg);
1536
}
1537
1538
return;
1539
}
1540
}
1541
1542
bool ScriptEditorDebugger::is_move_to_foreground() const {
1543
return move_to_foreground;
1544
}
1545
1546
void ScriptEditorDebugger::set_move_to_foreground(const bool &p_move_to_foreground) {
1547
move_to_foreground = p_move_to_foreground;
1548
}
1549
1550
String ScriptEditorDebugger::get_stack_script_file() const {
1551
TreeItem *ti = stack_dump->get_selected();
1552
if (!ti) {
1553
return "";
1554
}
1555
Dictionary d = ti->get_metadata(0);
1556
return d["file"];
1557
}
1558
1559
int ScriptEditorDebugger::get_stack_script_line() const {
1560
TreeItem *ti = stack_dump->get_selected();
1561
if (!ti) {
1562
return -1;
1563
}
1564
Dictionary d = ti->get_metadata(0);
1565
return d["line"];
1566
}
1567
1568
int ScriptEditorDebugger::get_stack_script_frame() const {
1569
TreeItem *ti = stack_dump->get_selected();
1570
if (!ti) {
1571
return -1;
1572
}
1573
Dictionary d = ti->get_metadata(0);
1574
return d["frame"];
1575
}
1576
1577
bool ScriptEditorDebugger::request_stack_dump(const int &p_frame) {
1578
ERR_FAIL_COND_V(!is_session_active() || p_frame < 0, false);
1579
1580
Array msg = { p_frame };
1581
_put_msg("get_stack_frame_vars", msg, debugging_thread_id);
1582
return true;
1583
}
1584
1585
void ScriptEditorDebugger::set_live_debugging(bool p_enable) {
1586
live_debug = p_enable;
1587
}
1588
1589
void ScriptEditorDebugger::_live_edit_set() {
1590
if (!is_session_active() || !editor_remote_tree) {
1591
return;
1592
}
1593
1594
TreeItem *ti = editor_remote_tree->get_selected();
1595
if (!ti) {
1596
return;
1597
}
1598
1599
String path;
1600
1601
while (ti) {
1602
String lp = ti->get_text(0);
1603
path = "/" + lp + path;
1604
ti = ti->get_parent();
1605
}
1606
1607
NodePath np = path;
1608
1609
EditorNode::get_editor_data().set_edited_scene_live_edit_root(np);
1610
1611
update_live_edit_root();
1612
}
1613
1614
void ScriptEditorDebugger::_live_edit_clear() {
1615
NodePath np = NodePath("/root");
1616
EditorNode::get_editor_data().set_edited_scene_live_edit_root(np);
1617
1618
update_live_edit_root();
1619
}
1620
1621
void ScriptEditorDebugger::update_live_edit_root() {
1622
NodePath np = EditorNode::get_editor_data().get_edited_scene_live_edit_root();
1623
1624
Array msg = { np };
1625
if (EditorNode::get_singleton()->get_edited_scene()) {
1626
msg.push_back(EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path());
1627
} else {
1628
msg.push_back("");
1629
}
1630
_put_msg("scene:live_set_root", msg);
1631
live_edit_root->set_text(String(np));
1632
}
1633
1634
void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {
1635
if (live_debug) {
1636
Array msg = { p_parent, p_type, p_name };
1637
_put_msg("scene:live_create_node", msg);
1638
}
1639
}
1640
1641
void ScriptEditorDebugger::live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name) {
1642
if (live_debug) {
1643
Array msg = { p_parent, p_path, p_name };
1644
_put_msg("scene:live_instantiate_node", msg);
1645
}
1646
}
1647
1648
void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {
1649
if (live_debug) {
1650
Array msg = { p_at };
1651
_put_msg("scene:live_remove_node", msg);
1652
}
1653
}
1654
1655
void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {
1656
if (live_debug) {
1657
Array msg = { p_at, p_keep_id };
1658
_put_msg("scene:live_remove_and_keep_node", msg);
1659
}
1660
}
1661
1662
void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
1663
if (live_debug) {
1664
Array msg = { p_id, p_at, p_at_pos };
1665
_put_msg("scene:live_restore_node", msg);
1666
}
1667
}
1668
1669
void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {
1670
if (live_debug) {
1671
Array msg = { p_at, p_new_name };
1672
_put_msg("scene:live_duplicate_node", msg);
1673
}
1674
}
1675
1676
void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
1677
if (live_debug) {
1678
Array msg = { p_at, p_new_place, p_new_name, p_at_pos };
1679
_put_msg("scene:live_reparent_node", msg);
1680
}
1681
}
1682
1683
bool ScriptEditorDebugger::get_debug_mute_audio() const {
1684
return debug_mute_audio;
1685
}
1686
1687
void ScriptEditorDebugger::_send_debug_mute_audio_msg(bool p_mute) {
1688
Array msg = { p_mute };
1689
_put_msg("scene:debug_mute_audio", msg);
1690
}
1691
1692
void ScriptEditorDebugger::set_debug_mute_audio(bool p_mute) {
1693
// Send the message if we want to mute the audio or if it isn't muted already due to a break.
1694
if (p_mute || !audio_muted_on_break) {
1695
_send_debug_mute_audio_msg(p_mute);
1696
}
1697
debug_mute_audio = p_mute;
1698
}
1699
1700
void ScriptEditorDebugger::_mute_audio_on_break(bool p_mute) {
1701
// Send the message if we want to mute the audio on a break or if it isn't muted already.
1702
if (p_mute || !debug_mute_audio) {
1703
_send_debug_mute_audio_msg(p_mute);
1704
}
1705
audio_muted_on_break = p_mute;
1706
}
1707
1708
CameraOverride ScriptEditorDebugger::get_camera_override() const {
1709
return camera_override;
1710
}
1711
1712
void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
1713
Array msg = {
1714
p_override != CameraOverride::OVERRIDE_NONE,
1715
p_override == CameraOverride::OVERRIDE_EDITORS
1716
};
1717
_put_msg("scene:override_cameras", msg);
1718
1719
camera_override = p_override;
1720
}
1721
1722
void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
1723
Array msg = { p_path, p_line, p_enabled };
1724
_put_msg("breakpoint", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
1725
1726
TreeItem *path_item = breakpoints_tree->search_item_text(p_path);
1727
if (path_item == nullptr) {
1728
if (!p_enabled) {
1729
return;
1730
}
1731
path_item = breakpoints_tree->create_item();
1732
path_item->set_text(0, p_path);
1733
}
1734
1735
int idx = 0;
1736
TreeItem *breakpoint_item;
1737
for (breakpoint_item = path_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {
1738
if ((int)breakpoint_item->get_meta("line") < p_line) {
1739
idx++;
1740
continue;
1741
}
1742
1743
if ((int)breakpoint_item->get_meta("line") == p_line) {
1744
break;
1745
}
1746
}
1747
1748
if (breakpoint_item == nullptr) {
1749
if (!p_enabled) {
1750
return;
1751
}
1752
breakpoint_item = breakpoints_tree->create_item(path_item, idx);
1753
breakpoint_item->set_meta("line", p_line);
1754
breakpoint_item->set_text(0, vformat(TTR("Line %d"), p_line));
1755
return;
1756
}
1757
1758
if (!p_enabled) {
1759
path_item->remove_child(breakpoint_item);
1760
if (path_item->get_first_child() == nullptr) {
1761
breakpoints_tree->get_root()->remove_child(path_item);
1762
}
1763
}
1764
}
1765
1766
void ScriptEditorDebugger::reload_all_scripts() {
1767
_put_msg("reload_all_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
1768
}
1769
1770
void ScriptEditorDebugger::reload_scripts(const Vector<String> &p_script_paths) {
1771
_put_msg("reload_scripts", Variant(p_script_paths).operator Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
1772
}
1773
1774
bool ScriptEditorDebugger::is_skip_breakpoints() const {
1775
return skip_breakpoints_value;
1776
}
1777
1778
bool ScriptEditorDebugger::is_ignore_error_breaks() const {
1779
return ignore_error_breaks_value;
1780
}
1781
1782
void ScriptEditorDebugger::_error_activated() {
1783
TreeItem *selected = error_tree->get_selected();
1784
1785
if (!selected) {
1786
return;
1787
}
1788
1789
TreeItem *ci = selected->get_first_child();
1790
if (ci) {
1791
selected->set_collapsed(!selected->is_collapsed());
1792
}
1793
}
1794
1795
void ScriptEditorDebugger::_error_selected() {
1796
TreeItem *selected = error_tree->get_selected();
1797
1798
if (!selected) {
1799
return;
1800
}
1801
1802
Array meta = selected->get_metadata(0);
1803
if (meta.is_empty()) {
1804
return;
1805
}
1806
1807
emit_signal(SNAME("error_selected"), String(meta[0]), int(meta[1]));
1808
}
1809
1810
void ScriptEditorDebugger::_expand_errors_list() {
1811
TreeItem *root = error_tree->get_root();
1812
if (!root) {
1813
return;
1814
}
1815
1816
TreeItem *item = root->get_first_child();
1817
while (item) {
1818
item->set_collapsed(false);
1819
item = item->get_next();
1820
}
1821
}
1822
1823
void ScriptEditorDebugger::_collapse_errors_list() {
1824
TreeItem *root = error_tree->get_root();
1825
if (!root) {
1826
return;
1827
}
1828
1829
TreeItem *item = root->get_first_child();
1830
while (item) {
1831
item->set_collapsed(true);
1832
item = item->get_next();
1833
}
1834
}
1835
1836
void ScriptEditorDebugger::_vmem_item_activated() {
1837
TreeItem *selected = vmem_tree->get_selected();
1838
if (!selected) {
1839
return;
1840
}
1841
const String path = selected->get_text(0);
1842
if (path.is_empty() || !FileAccess::exists(path)) {
1843
return;
1844
}
1845
FileSystemDock::get_singleton()->navigate_to_path(path);
1846
}
1847
1848
void ScriptEditorDebugger::_vmem_tree_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {
1849
if (p_button != MouseButton::RIGHT) {
1850
return;
1851
}
1852
1853
TreeItem *item = vmem_tree->get_selected();
1854
if (!item) {
1855
return;
1856
}
1857
1858
String path = item->get_text(0);
1859
if (path.is_empty() || !FileAccess::exists(path)) {
1860
return;
1861
}
1862
1863
vmem_item_menu->set_position(vmem_tree->get_screen_position() + p_pos);
1864
vmem_item_menu->popup();
1865
}
1866
1867
void ScriptEditorDebugger::_vmem_item_menu_id_pressed(int p_option) {
1868
TreeItem *item = vmem_tree->get_selected();
1869
if (!item) {
1870
return;
1871
}
1872
1873
String path = item->get_text(0);
1874
switch (p_option) {
1875
case VMEM_MENU_SHOW_IN_FILESYSTEM: {
1876
FileSystemDock::get_singleton()->navigate_to_path(path);
1877
} break;
1878
case VMEM_MENU_SHOW_IN_EXPLORER: {
1879
OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(path), true);
1880
} break;
1881
case VMEM_MENU_OWNERS: {
1882
FileSystemDock::get_owners_dialog()->show(path);
1883
} break;
1884
}
1885
}
1886
1887
void ScriptEditorDebugger::_clear_errors_list() {
1888
error_tree->clear();
1889
error_count = 0;
1890
warning_count = 0;
1891
emit_signal(SNAME("errors_cleared"));
1892
update_tabs();
1893
1894
expand_all_button->set_disabled(true);
1895
collapse_all_button->set_disabled(true);
1896
clear_button->set_disabled(true);
1897
}
1898
1899
void ScriptEditorDebugger::_breakpoints_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {
1900
if (p_button != MouseButton::RIGHT) {
1901
return;
1902
}
1903
1904
breakpoints_menu->clear();
1905
breakpoints_menu->set_size(Size2(1, 1));
1906
1907
const TreeItem *selected = breakpoints_tree->get_selected();
1908
String file = selected->get_text(0);
1909
if (selected->has_meta("line")) {
1910
breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTRC("Delete Breakpoint"), ACTION_DELETE_BREAKPOINT);
1911
file = selected->get_parent()->get_text(0);
1912
}
1913
breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete All Breakpoints in:") + " " + file, ACTION_DELETE_BREAKPOINTS_IN_FILE);
1914
breakpoints_menu->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_DISABLED);
1915
breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTRC("Delete All Breakpoints"), ACTION_DELETE_ALL_BREAKPOINTS);
1916
1917
breakpoints_menu->set_position(get_screen_position() + get_local_mouse_position());
1918
breakpoints_menu->popup();
1919
}
1920
1921
// Right click on specific file(s) or folder(s).
1922
void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {
1923
if (p_button != MouseButton::RIGHT) {
1924
return;
1925
}
1926
1927
item_menu->clear();
1928
item_menu->reset_size();
1929
1930
if (error_tree->is_anything_selected()) {
1931
item_menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCopy")), TTRC("Copy Error"), ACTION_COPY_ERROR);
1932
item_menu->add_icon_item(get_editor_theme_icon(SNAME("ExternalLink")), TTRC("Open C++ Source on GitHub"), ACTION_OPEN_SOURCE);
1933
}
1934
1935
if (item_menu->get_item_count() > 0) {
1936
item_menu->set_position(error_tree->get_screen_position() + p_pos);
1937
item_menu->popup();
1938
}
1939
}
1940
1941
void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {
1942
switch (p_option) {
1943
case ACTION_COPY_ERROR: {
1944
TreeItem *ti = error_tree->get_selected();
1945
while (ti->get_parent() != error_tree->get_root()) {
1946
ti = ti->get_parent();
1947
}
1948
1949
String type;
1950
1951
if (ti->has_meta("_is_warning")) {
1952
type = "W ";
1953
} else if (ti->has_meta("_is_error")) {
1954
type = "E ";
1955
}
1956
1957
String text = ti->get_text(0) + " ";
1958
int rpad_len = text.length();
1959
1960
text = type + text + ti->get_text(1) + "\n";
1961
TreeItem *ci = ti->get_first_child();
1962
while (ci) {
1963
text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n";
1964
ci = ci->get_next();
1965
}
1966
1967
DisplayServer::get_singleton()->clipboard_set(text);
1968
} break;
1969
1970
case ACTION_OPEN_SOURCE: {
1971
TreeItem *ti = error_tree->get_selected();
1972
while (ti->get_parent() != error_tree->get_root()) {
1973
ti = ti->get_parent();
1974
}
1975
1976
// Find the child with the "C++ Source".
1977
// It's not at a fixed position as "C++ Error" may come first.
1978
TreeItem *ci = ti->get_first_child();
1979
const String cpp_source = "<" + TTR("C++ Source") + ">";
1980
while (ci) {
1981
if (ci->get_text(0) == cpp_source) {
1982
break;
1983
}
1984
ci = ci->get_next();
1985
}
1986
1987
if (!ci) {
1988
WARN_PRINT_ED("No C++ source reference is available for this error.");
1989
return;
1990
}
1991
1992
// Parse back the `file:line @ method()` string.
1993
const Vector<String> file_line_number = ci->get_text(1).get_slicec('@', 0).strip_edges().split(":");
1994
ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report).");
1995
const String &file = file_line_number[0];
1996
const int line_number = file_line_number[1].to_int();
1997
1998
// Construct a GitHub repository URL and open it in the user's default web browser.
1999
// If the commit hash is available, use it for greater accuracy. Otherwise fall back to tagged release.
2000
String git_ref = String(GODOT_VERSION_HASH).is_empty() ? String(GODOT_VERSION_NUMBER) + "-stable" : String(GODOT_VERSION_HASH);
2001
OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d",
2002
git_ref, file, line_number));
2003
} break;
2004
case ACTION_DELETE_BREAKPOINT: {
2005
const TreeItem *selected = breakpoints_tree->get_selected();
2006
_set_breakpoint(selected->get_parent()->get_text(0), selected->get_meta("line"), false);
2007
} break;
2008
case ACTION_DELETE_BREAKPOINTS_IN_FILE: {
2009
TreeItem *file_item = breakpoints_tree->get_selected();
2010
if (file_item->has_meta("line")) {
2011
file_item = file_item->get_parent();
2012
}
2013
2014
// Store first else we will be removing as we loop.
2015
List<int> lines;
2016
for (TreeItem *breakpoint_item = file_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {
2017
lines.push_back(breakpoint_item->get_meta("line"));
2018
}
2019
2020
for (const int &line : lines) {
2021
_set_breakpoint(file_item->get_text(0), line, false);
2022
}
2023
} break;
2024
case ACTION_DELETE_ALL_BREAKPOINTS: {
2025
_clear_breakpoints();
2026
} break;
2027
}
2028
}
2029
2030
void ScriptEditorDebugger::_tab_changed(int p_tab) {
2031
if (tabs->get_tab_title(p_tab) == "Video RAM") {
2032
// "Video RAM" tab was clicked, refresh the data it's displaying when entering the tab.
2033
_video_mem_request();
2034
}
2035
}
2036
2037
void ScriptEditorDebugger::_bind_methods() {
2038
ClassDB::bind_method(D_METHOD("live_debug_create_node"), &ScriptEditorDebugger::live_debug_create_node);
2039
ClassDB::bind_method(D_METHOD("live_debug_instantiate_node"), &ScriptEditorDebugger::live_debug_instantiate_node);
2040
ClassDB::bind_method(D_METHOD("live_debug_remove_node"), &ScriptEditorDebugger::live_debug_remove_node);
2041
ClassDB::bind_method(D_METHOD("live_debug_remove_and_keep_node"), &ScriptEditorDebugger::live_debug_remove_and_keep_node);
2042
ClassDB::bind_method(D_METHOD("live_debug_restore_node"), &ScriptEditorDebugger::live_debug_restore_node);
2043
ClassDB::bind_method(D_METHOD("live_debug_duplicate_node"), &ScriptEditorDebugger::live_debug_duplicate_node);
2044
ClassDB::bind_method(D_METHOD("live_debug_reparent_node"), &ScriptEditorDebugger::live_debug_reparent_node);
2045
ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value", "field"), &ScriptEditorDebugger::update_remote_object);
2046
2047
ADD_SIGNAL(MethodInfo("started"));
2048
ADD_SIGNAL(MethodInfo("stopped"));
2049
ADD_SIGNAL(MethodInfo("stop_requested"));
2050
ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));
2051
ADD_SIGNAL(MethodInfo("error_selected", PropertyInfo(Variant::INT, "error")));
2052
ADD_SIGNAL(MethodInfo("breakpoint_selected", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));
2053
ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));
2054
ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));
2055
ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"), PropertyInfo(Variant::STRING, "reason"), PropertyInfo(Variant::BOOL, "has_stackdump")));
2056
ADD_SIGNAL(MethodInfo("remote_objects_requested", PropertyInfo(Variant::ARRAY, "ids")));
2057
ADD_SIGNAL(MethodInfo("remote_objects_updated", PropertyInfo(Variant::OBJECT, "remote_objects")));
2058
ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
2059
ADD_SIGNAL(MethodInfo("remote_window_title_changed", PropertyInfo(Variant::STRING, "title")));
2060
ADD_SIGNAL(MethodInfo("remote_tree_updated"));
2061
ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::ARRAY, "ids")));
2062
ADD_SIGNAL(MethodInfo("remote_tree_clear_selection_requested"));
2063
ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level")));
2064
ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump")));
2065
ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars")));
2066
ADD_SIGNAL(MethodInfo("stack_frame_var", PropertyInfo(Variant::ARRAY, "data")));
2067
ADD_SIGNAL(MethodInfo("debug_data", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::ARRAY, "data")));
2068
ADD_SIGNAL(MethodInfo("set_breakpoint", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));
2069
ADD_SIGNAL(MethodInfo("clear_breakpoints"));
2070
ADD_SIGNAL(MethodInfo("errors_cleared"));
2071
ADD_SIGNAL(MethodInfo("embed_shortcut_requested", PropertyInfo(Variant::INT, "embed_shortcut_action")));
2072
}
2073
2074
void ScriptEditorDebugger::add_debugger_tab(Control *p_control) {
2075
tabs->add_child(p_control);
2076
}
2077
2078
void ScriptEditorDebugger::remove_debugger_tab(Control *p_control) {
2079
int idx = tabs->get_tab_idx_from_control(p_control);
2080
ERR_FAIL_COND(idx < 0);
2081
p_control->queue_free();
2082
}
2083
2084
int ScriptEditorDebugger::get_current_debugger_tab() const {
2085
return tabs->get_current_tab();
2086
}
2087
2088
void ScriptEditorDebugger::switch_to_debugger(int p_debugger_tab_idx) {
2089
tabs->set_current_tab(p_debugger_tab_idx);
2090
}
2091
2092
void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) {
2093
_put_msg(p_message, p_args);
2094
}
2095
2096
void ScriptEditorDebugger::toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data) {
2097
Array msg_data = { p_enable, p_data };
2098
_put_msg("profiler:" + p_profiler, msg_data);
2099
}
2100
2101
ScriptEditorDebugger::ScriptEditorDebugger() {
2102
if (unlikely(parse_message_handlers.is_empty())) {
2103
_init_parse_message_handlers();
2104
}
2105
2106
tabs = memnew(TabContainer);
2107
add_child(tabs);
2108
tabs->connect("tab_changed", callable_mp(this, &ScriptEditorDebugger::_tab_changed));
2109
2110
InspectorDock::get_inspector_singleton()->connect("object_id_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));
2111
EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &ScriptEditorDebugger::_resources_reimported));
2112
2113
{ // Debugger.
2114
VBoxContainer *vbc = memnew(VBoxContainer);
2115
vbc->set_name(TTRC("Stack Trace"));
2116
Control *dbg = vbc;
2117
2118
HBoxContainer *hbc = memnew(HBoxContainer);
2119
vbc->add_child(hbc);
2120
2121
reason = memnew(RichTextLabel);
2122
reason->set_focus_mode(FOCUS_ACCESSIBILITY);
2123
reason->set_selection_enabled(true);
2124
reason->set_context_menu_enabled(true);
2125
reason->set_h_size_flags(SIZE_EXPAND_FILL);
2126
reason->set_v_size_flags(SIZE_SHRINK_CENTER);
2127
reason->connect(SceneStringName(resized), callable_mp(this, &ScriptEditorDebugger::_update_reason_content_height));
2128
hbc->add_child(reason);
2129
2130
hbc->add_child(memnew(VSeparator));
2131
2132
skip_breakpoints = memnew(Button);
2133
skip_breakpoints->set_theme_type_variation(SceneStringName(FlatButton));
2134
hbc->add_child(skip_breakpoints);
2135
skip_breakpoints->set_tooltip_text(TTRC("Skip Breakpoints"));
2136
skip_breakpoints->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints));
2137
2138
ignore_error_breaks = memnew(Button);
2139
ignore_error_breaks->set_theme_type_variation(SceneStringName(FlatButton));
2140
ignore_error_breaks->set_tooltip_text(TTRC("Ignore Error Breaks"));
2141
hbc->add_child(ignore_error_breaks);
2142
ignore_error_breaks->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_ignore_error_breaks));
2143
2144
hbc->add_child(memnew(VSeparator));
2145
2146
copy = memnew(Button);
2147
copy->set_theme_type_variation(SceneStringName(FlatButton));
2148
hbc->add_child(copy);
2149
copy->set_tooltip_text(TTRC("Copy Error"));
2150
copy->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_copy));
2151
2152
hbc->add_child(memnew(VSeparator));
2153
2154
step = memnew(Button);
2155
step->set_theme_type_variation(SceneStringName(FlatButton));
2156
hbc->add_child(step);
2157
step->set_tooltip_text(TTRC("Step Into"));
2158
step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into"));
2159
step->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_step));
2160
2161
next = memnew(Button);
2162
next->set_theme_type_variation(SceneStringName(FlatButton));
2163
hbc->add_child(next);
2164
next->set_tooltip_text(TTRC("Step Over"));
2165
next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over"));
2166
next->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_next));
2167
2168
out = memnew(Button);
2169
out->set_theme_type_variation(SceneStringName(FlatButton));
2170
hbc->add_child(out);
2171
out->set_tooltip_text(TTRC("Step Out"));
2172
out->set_shortcut(ED_GET_SHORTCUT("debugger/step_out"));
2173
out->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_out));
2174
2175
hbc->add_child(memnew(VSeparator));
2176
2177
dobreak = memnew(Button);
2178
dobreak->set_theme_type_variation(SceneStringName(FlatButton));
2179
hbc->add_child(dobreak);
2180
dobreak->set_tooltip_text(TTRC("Break"));
2181
dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break"));
2182
dobreak->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_break));
2183
2184
docontinue = memnew(Button);
2185
docontinue->set_theme_type_variation(SceneStringName(FlatButton));
2186
hbc->add_child(docontinue);
2187
docontinue->set_tooltip_text(TTRC("Continue"));
2188
docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));
2189
docontinue->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_continue));
2190
2191
HSplitContainer *sc = memnew(HSplitContainer);
2192
sc->set_v_size_flags(SIZE_EXPAND_FILL);
2193
vbc->add_child(sc);
2194
2195
VBoxContainer *stack_vb = memnew(VBoxContainer);
2196
stack_vb->set_h_size_flags(SIZE_EXPAND_FILL);
2197
sc->add_child(stack_vb);
2198
HBoxContainer *thread_hb = memnew(HBoxContainer);
2199
stack_vb->add_child(thread_hb);
2200
thread_hb->add_child(memnew(Label(TTRC("Thread:"))));
2201
threads = memnew(OptionButton);
2202
thread_hb->add_child(threads);
2203
threads->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
2204
threads->set_h_size_flags(SIZE_EXPAND_FILL);
2205
threads->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_select_thread));
2206
2207
stack_dump = memnew(Tree);
2208
stack_dump->set_custom_minimum_size(Size2(150, 0) * EDSCALE);
2209
stack_dump->set_allow_reselect(true);
2210
stack_dump->set_columns(1);
2211
stack_dump->set_column_titles_visible(true);
2212
stack_dump->set_column_title(0, TTRC("Stack Frames"));
2213
stack_dump->set_hide_root(true);
2214
stack_dump->set_v_size_flags(SIZE_EXPAND_FILL);
2215
stack_dump->set_theme_type_variation("TreeSecondary");
2216
stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected));
2217
stack_vb->add_child(stack_dump);
2218
2219
VBoxContainer *inspector_vbox = memnew(VBoxContainer);
2220
inspector_vbox->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
2221
inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
2222
sc->add_child(inspector_vbox);
2223
2224
HBoxContainer *tools_hb = memnew(HBoxContainer);
2225
inspector_vbox->add_child(tools_hb);
2226
2227
search = memnew(LineEdit);
2228
search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2229
search->set_placeholder(TTRC("Filter Stack Variables"));
2230
search->set_accessibility_name(TTRC("Filter Stack Variables"));
2231
search->set_clear_button_enabled(true);
2232
tools_hb->add_child(search);
2233
2234
inspector = memnew(EditorDebuggerInspector);
2235
inspector->set_h_size_flags(SIZE_EXPAND_FILL);
2236
inspector->set_v_size_flags(SIZE_EXPAND_FILL);
2237
inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);
2238
inspector->set_read_only(true);
2239
inspector->connect("object_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));
2240
inspector->connect("objects_edited", callable_mp(this, &ScriptEditorDebugger::_remote_objects_edited));
2241
inspector->connect("object_property_updated", callable_mp(this, &ScriptEditorDebugger::_remote_object_property_updated));
2242
inspector->register_text_enter(search);
2243
inspector->set_use_filter(true);
2244
inspector_vbox->add_child(inspector);
2245
2246
breakpoints_tree = memnew(Tree);
2247
breakpoints_tree->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
2248
breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL);
2249
breakpoints_tree->set_column_titles_visible(true);
2250
breakpoints_tree->set_column_title(0, TTRC("Breakpoints"));
2251
breakpoints_tree->set_allow_reselect(true);
2252
breakpoints_tree->set_allow_rmb_select(true);
2253
breakpoints_tree->set_hide_root(true);
2254
breakpoints_tree->set_theme_type_variation("TreeSecondary");
2255
breakpoints_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected));
2256
breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));
2257
breakpoints_tree->create_item();
2258
2259
sc->add_child(breakpoints_tree);
2260
tabs->add_child(dbg);
2261
2262
breakpoints_menu = memnew(PopupMenu);
2263
breakpoints_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed));
2264
breakpoints_tree->add_child(breakpoints_menu);
2265
}
2266
2267
{ // Errors.
2268
errors_tab = memnew(VBoxContainer);
2269
errors_tab->set_name(TTRC("Errors"));
2270
2271
HBoxContainer *error_hbox = memnew(HBoxContainer);
2272
errors_tab->add_child(error_hbox);
2273
2274
expand_all_button = memnew(Button);
2275
expand_all_button->set_text(TTRC("Expand All"));
2276
expand_all_button->set_disabled(true);
2277
expand_all_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_expand_errors_list));
2278
error_hbox->add_child(expand_all_button);
2279
2280
collapse_all_button = memnew(Button);
2281
collapse_all_button->set_text(TTRC("Collapse All"));
2282
collapse_all_button->set_disabled(true);
2283
collapse_all_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_collapse_errors_list));
2284
error_hbox->add_child(collapse_all_button);
2285
2286
Control *space = memnew(Control);
2287
space->set_h_size_flags(SIZE_EXPAND_FILL);
2288
error_hbox->add_child(space);
2289
2290
clear_button = memnew(Button);
2291
clear_button->set_text(TTRC("Clear"));
2292
clear_button->set_h_size_flags(0);
2293
clear_button->set_disabled(true);
2294
clear_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_clear_errors_list));
2295
error_hbox->add_child(clear_button);
2296
2297
error_tree = memnew(Tree);
2298
error_tree->set_columns(2);
2299
2300
error_tree->set_column_expand(0, false);
2301
error_tree->set_column_custom_minimum_width(0, 140);
2302
error_tree->set_column_clip_content(0, true);
2303
2304
error_tree->set_column_expand(1, true);
2305
error_tree->set_column_clip_content(1, true);
2306
2307
error_tree->set_select_mode(Tree::SELECT_ROW);
2308
error_tree->set_hide_root(true);
2309
error_tree->set_v_size_flags(SIZE_EXPAND_FILL);
2310
error_tree->set_allow_rmb_select(true);
2311
error_tree->set_allow_reselect(true);
2312
error_tree->set_theme_type_variation("TreeSecondary");
2313
error_tree->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_error_selected));
2314
error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated));
2315
error_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_error_tree_item_rmb_selected));
2316
errors_tab->add_child(error_tree);
2317
2318
item_menu = memnew(PopupMenu);
2319
item_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed));
2320
error_tree->add_child(item_menu);
2321
2322
tabs->add_child(errors_tab);
2323
}
2324
2325
{ // File dialog
2326
file_dialog = memnew(EditorFileDialog);
2327
file_dialog->connect("file_selected", callable_mp(this, &ScriptEditorDebugger::_file_selected));
2328
add_child(file_dialog);
2329
}
2330
2331
{ // Expression evaluator
2332
expression_evaluator = memnew(EditorExpressionEvaluator);
2333
expression_evaluator->set_name(TTRC("Evaluator"));
2334
expression_evaluator->set_editor_debugger(this);
2335
tabs->add_child(expression_evaluator);
2336
}
2337
2338
{ //profiler
2339
profiler = memnew(EditorProfiler);
2340
profiler->set_name(TTRC("Profiler"));
2341
tabs->add_child(profiler);
2342
profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_SCRIPTS_SERVERS));
2343
profiler->connect("break_request", callable_mp(this, &ScriptEditorDebugger::_profiler_seeked));
2344
}
2345
2346
{ //frame profiler
2347
visual_profiler = memnew(EditorVisualProfiler);
2348
visual_profiler->set_name(TTRC("Visual Profiler"));
2349
tabs->add_child(visual_profiler);
2350
visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_VISUAL));
2351
}
2352
2353
{ //monitors
2354
performance_profiler = memnew(EditorPerformanceProfiler);
2355
tabs->add_child(performance_profiler);
2356
}
2357
2358
{ //vmem inspect
2359
VBoxContainer *vmem_vb = memnew(VBoxContainer);
2360
HBoxContainer *vmem_hb = memnew(HBoxContainer);
2361
2362
Label *vmlb = memnew(Label(TTRC("List of Video Memory Usage by Resource:")));
2363
vmlb->set_theme_type_variation("HeaderSmall");
2364
vmem_hb->add_child(vmlb);
2365
2366
{ // Add notice icon.
2367
vmem_notice_icon = memnew(TextureRect);
2368
vmem_notice_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
2369
vmem_notice_icon->set_h_size_flags(SIZE_SHRINK_CENTER);
2370
vmem_notice_icon->set_visible(true);
2371
vmem_notice_icon->set_tooltip_text(TTRC(R"(Notice:
2372
This tool only reports memory allocations tracked by the engine.
2373
Therefore, total VRAM usage is inaccurate compared to what the Monitors tab or external tools can report.
2374
Instead, use the monitors tab to obtain more precise VRAM usage.
2375
2376
- Buffer Memory (e.g. GPUParticles) is not tracked.
2377
- Meshes are not tracked in the Compatibility renderer.)"));
2378
vmem_hb->add_child(vmem_notice_icon);
2379
}
2380
2381
{ // Add some space to move the rest of the controls to the right.
2382
Control *space = memnew(Control);
2383
space->set_h_size_flags(SIZE_EXPAND_FILL);
2384
vmem_hb->add_child(space);
2385
}
2386
2387
vmem_hb->add_child(memnew(Label(TTRC("Total:"))));
2388
vmem_total = memnew(LineEdit);
2389
vmem_total->set_editable(false);
2390
vmem_total->set_accessibility_name(TTRC("Video RAM Total"));
2391
vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
2392
vmem_hb->add_child(vmem_total);
2393
vmem_refresh = memnew(Button);
2394
vmem_refresh->set_accessibility_name(TTRC("Refresh Video RAM"));
2395
vmem_refresh->set_theme_type_variation(SceneStringName(FlatButton));
2396
vmem_hb->add_child(vmem_refresh);
2397
vmem_export = memnew(Button);
2398
vmem_export->set_theme_type_variation(SceneStringName(FlatButton));
2399
vmem_export->set_tooltip_text(TTRC("Export list to a CSV file"));
2400
vmem_hb->add_child(vmem_export);
2401
vmem_vb->add_child(vmem_hb);
2402
vmem_refresh->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_video_mem_request));
2403
vmem_export->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_video_mem_export));
2404
2405
MarginContainer *mc = memnew(MarginContainer);
2406
mc->set_theme_type_variation("NoBorderBottomPanel");
2407
mc->set_v_size_flags(SIZE_EXPAND_FILL);
2408
vmem_vb->add_child(mc);
2409
2410
vmem_tree = memnew(Tree);
2411
vmem_vb->set_name(TTRC("Video RAM"));
2412
vmem_tree->set_columns(4);
2413
vmem_tree->set_column_titles_visible(true);
2414
vmem_tree->set_column_title(0, TTRC("Resource Path"));
2415
vmem_tree->set_column_expand(0, true);
2416
vmem_tree->set_column_expand(1, false);
2417
vmem_tree->set_column_title(1, TTRC("Type"));
2418
vmem_tree->set_column_custom_minimum_width(1, 100 * EDSCALE);
2419
vmem_tree->set_column_expand(2, false);
2420
vmem_tree->set_column_title(2, TTRC("Format"));
2421
vmem_tree->set_column_custom_minimum_width(2, 150 * EDSCALE);
2422
vmem_tree->set_column_expand(3, false);
2423
vmem_tree->set_column_title(3, TTRC("Usage"));
2424
vmem_tree->set_column_custom_minimum_width(3, 80 * EDSCALE);
2425
vmem_tree->set_hide_root(true);
2426
vmem_tree->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_BOTTOM);
2427
mc->add_child(vmem_tree);
2428
vmem_tree->set_allow_rmb_select(true);
2429
vmem_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_vmem_item_activated));
2430
vmem_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_vmem_tree_rmb_selected));
2431
tabs->add_child(vmem_vb);
2432
2433
vmem_item_menu = memnew(PopupMenu);
2434
vmem_item_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_vmem_item_menu_id_pressed));
2435
vmem_item_menu->add_item(TTRC("Show in FileSystem"), VMEM_MENU_SHOW_IN_FILESYSTEM);
2436
vmem_item_menu->add_item(TTRC("Show in File Manager"), VMEM_MENU_SHOW_IN_EXPLORER);
2437
vmem_item_menu->add_item(TTRC("View Owners..."), VMEM_MENU_OWNERS);
2438
add_child(vmem_item_menu);
2439
}
2440
2441
{ // misc
2442
VBoxContainer *misc = memnew(VBoxContainer);
2443
misc->set_name(TTRC("Misc"));
2444
tabs->add_child(misc);
2445
2446
GridContainer *info_left = memnew(GridContainer);
2447
info_left->set_columns(2);
2448
misc->add_child(info_left);
2449
clicked_ctrl = memnew(LineEdit);
2450
clicked_ctrl->set_editable(false);
2451
clicked_ctrl->set_accessibility_name(TTRC("Clicked Control:"));
2452
clicked_ctrl->set_h_size_flags(SIZE_EXPAND_FILL);
2453
info_left->add_child(memnew(Label(TTRC("Clicked Control:"))));
2454
info_left->add_child(clicked_ctrl);
2455
clicked_ctrl_type = memnew(LineEdit);
2456
clicked_ctrl_type->set_editable(false);
2457
clicked_ctrl_type->set_accessibility_name(TTRC("Clicked Control Type:"));
2458
info_left->add_child(memnew(Label(TTRC("Clicked Control Type:"))));
2459
info_left->add_child(clicked_ctrl_type);
2460
2461
scene_tree = memnew(SceneDebuggerTree);
2462
live_edit_root = memnew(LineEdit);
2463
live_edit_root->set_editable(false);
2464
live_edit_root->set_h_size_flags(SIZE_EXPAND_FILL);
2465
live_edit_root->set_accessibility_name(TTRC("Live Edit Root:"));
2466
2467
{
2468
HBoxContainer *lehb = memnew(HBoxContainer);
2469
Label *l = memnew(Label(TTRC("Live Edit Root:")));
2470
info_left->add_child(l);
2471
lehb->add_child(live_edit_root);
2472
le_set = memnew(Button(TTRC("Set From Tree")));
2473
le_set->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_live_edit_set));
2474
lehb->add_child(le_set);
2475
le_clear = memnew(Button(TTRC("Clear")));
2476
le_clear->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_live_edit_clear));
2477
lehb->add_child(le_clear);
2478
info_left->add_child(lehb);
2479
}
2480
2481
misc->add_child(memnew(VSeparator));
2482
2483
HBoxContainer *buttons = memnew(HBoxContainer);
2484
2485
export_csv = memnew(Button(TTRC("Export measures as CSV")));
2486
export_csv->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_export_csv));
2487
buttons->add_child(export_csv);
2488
2489
misc->add_child(buttons);
2490
}
2491
2492
msgdialog = memnew(AcceptDialog);
2493
add_child(msgdialog);
2494
2495
camera_override = CameraOverride::OVERRIDE_NONE;
2496
error_count = 0;
2497
warning_count = 0;
2498
_update_buttons_state();
2499
}
2500
2501
ScriptEditorDebugger::~ScriptEditorDebugger() {
2502
if (peer.is_valid()) {
2503
peer->close();
2504
peer.unref();
2505
}
2506
memdelete(scene_tree);
2507
}
2508
2509