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