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