Path: blob/master/editor/debugger/script_editor_debugger.cpp
9903 views
/**************************************************************************/1/* script_editor_debugger.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "script_editor_debugger.h"3132#include "core/debugger/debugger_marshalls.h"33#include "core/debugger/remote_debugger.h"34#include "core/string/ustring.h"35#include "core/version.h"36#include "editor/debugger/editor_debugger_plugin.h"37#include "editor/debugger/editor_expression_evaluator.h"38#include "editor/debugger/editor_performance_profiler.h"39#include "editor/debugger/editor_profiler.h"40#include "editor/debugger/editor_visual_profiler.h"41#include "editor/docks/filesystem_dock.h"42#include "editor/docks/inspector_dock.h"43#include "editor/editor_log.h"44#include "editor/editor_node.h"45#include "editor/editor_string_names.h"46#include "editor/file_system/editor_file_system.h"47#include "editor/gui/editor_file_dialog.h"48#include "editor/gui/editor_toaster.h"49#include "editor/inspector/editor_property_name_processor.h"50#include "editor/scene/3d/node_3d_editor_plugin.h"51#include "editor/scene/canvas_item_editor_plugin.h"52#include "editor/settings/editor_settings.h"53#include "editor/themes/editor_scale.h"54#include "main/performance.h"55#include "scene/3d/camera_3d.h"56#include "scene/debugger/scene_debugger.h"57#include "scene/gui/button.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/separator.h"64#include "scene/gui/split_container.h"65#include "scene/gui/tab_container.h"66#include "scene/gui/tree.h"67#include "servers/debugger/servers_debugger.h"68#include "servers/display_server.h"6970using CameraOverride = EditorDebuggerNode::CameraOverride;7172void ScriptEditorDebugger::_put_msg(const String &p_message, const Array &p_data, uint64_t p_thread_id) {73ERR_FAIL_COND(p_thread_id == Thread::UNASSIGNED_ID);74if (is_session_active()) {75Array msg = { p_message, p_thread_id, p_data };76Error err = peer->put_message(msg);77ERR_FAIL_COND_MSG(err != OK, vformat("Failed to send message %d", err));78}79}8081void ScriptEditorDebugger::debug_copy() {82String msg = reason->get_text();83if (msg.is_empty()) {84return;85}86DisplayServer::get_singleton()->clipboard_set(msg);87}8889void ScriptEditorDebugger::debug_skip_breakpoints() {90skip_breakpoints_value = !skip_breakpoints_value;91if (skip_breakpoints_value) {92skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn")));93} else {94skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff")));95}9697Array msg = { skip_breakpoints_value };98_put_msg("set_skip_breakpoints", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);99}100101void ScriptEditorDebugger::debug_ignore_error_breaks() {102ignore_error_breaks_value = !ignore_error_breaks_value;103if (ignore_error_breaks_value) {104ignore_error_breaks->set_button_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));105} else {106ignore_error_breaks->set_button_icon(get_theme_icon(SNAME("Notification"), SNAME("EditorIcons")));107}108109Array msg = { ignore_error_breaks_value };110_put_msg("set_ignore_error_breaks", msg);111}112113void ScriptEditorDebugger::debug_next() {114ERR_FAIL_COND(!is_breaked());115116_put_msg("next", Array(), debugging_thread_id);117_clear_execution();118}119120void ScriptEditorDebugger::debug_step() {121ERR_FAIL_COND(!is_breaked());122123_put_msg("step", Array(), debugging_thread_id);124_clear_execution();125}126127void ScriptEditorDebugger::debug_break() {128ERR_FAIL_COND(is_breaked());129130_put_msg("break", Array());131}132133void ScriptEditorDebugger::debug_continue() {134ERR_FAIL_COND(!is_breaked());135136// Allow focus stealing only if we actually run this client for security.137if (remote_pid && EditorNode::get_singleton()->has_child_process(remote_pid)) {138DisplayServer::get_singleton()->enable_for_stealing_focus(remote_pid);139}140141_clear_execution();142_put_msg("continue", Array(), debugging_thread_id);143_put_msg("servers:foreground", Array());144}145146void ScriptEditorDebugger::update_tabs() {147if (error_count == 0 && warning_count == 0) {148errors_tab->set_name(TTR("Errors"));149tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), Ref<Texture2D>());150} else {151errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")");152if (error_count >= 1 && warning_count >= 1) {153tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("ErrorWarning")));154} else if (error_count >= 1) {155tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("Error")));156} else {157tabs->set_tab_icon(tabs->get_tab_idx_from_control(errors_tab), get_editor_theme_icon(SNAME("Warning")));158}159}160}161162void ScriptEditorDebugger::clear_style() {163tabs->remove_theme_style_override(SceneStringName(panel));164}165166void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) {167Array msg = { p_id, p_file };168_put_msg("scene:save_node", msg);169}170171void ScriptEditorDebugger::_file_selected(const String &p_file) {172switch (file_dialog_purpose) {173case SAVE_MONITORS_CSV: {174Error err;175Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);176177if (err != OK) {178ERR_PRINT("Failed to open " + p_file);179return;180}181Vector<String> line;182line.resize(Performance::MONITOR_MAX);183184// signatures185for (int i = 0; i < Performance::MONITOR_MAX; i++) {186line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i));187}188file->store_csv_line(line);189190// values191Vector<List<float>::Element *> iterators;192iterators.resize(Performance::MONITOR_MAX);193bool continue_iteration = false;194for (int i = 0; i < Performance::MONITOR_MAX; i++) {195iterators.write[i] = performance_profiler->get_monitor_data(Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)))->back();196continue_iteration = continue_iteration || iterators[i];197}198while (continue_iteration) {199continue_iteration = false;200for (int i = 0; i < Performance::MONITOR_MAX; i++) {201if (iterators[i]) {202line.write[i] = String::num_real(iterators[i]->get());203iterators.write[i] = iterators[i]->prev();204} else {205line.write[i] = "";206}207continue_iteration = continue_iteration || iterators[i];208}209file->store_csv_line(line);210}211file->store_string("\n");212213Vector<Vector<String>> profiler_data = profiler->get_data_as_csv();214for (int i = 0; i < profiler_data.size(); i++) {215file->store_csv_line(profiler_data[i]);216}217} break;218case SAVE_VRAM_CSV: {219Error err;220Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);221222if (err != OK) {223ERR_PRINT("Failed to open " + p_file);224return;225}226227Vector<String> headers;228headers.resize(vmem_tree->get_columns());229for (int i = 0; i < vmem_tree->get_columns(); ++i) {230headers.write[i] = vmem_tree->get_column_title(i);231}232file->store_csv_line(headers);233234if (vmem_tree->get_root()) {235TreeItem *ti = vmem_tree->get_root()->get_first_child();236while (ti) {237Vector<String> values;238values.resize(vmem_tree->get_columns());239for (int i = 0; i < vmem_tree->get_columns(); ++i) {240values.write[i] = ti->get_text(i);241}242file->store_csv_line(values);243244ti = ti->get_next();245}246}247} break;248}249}250251void ScriptEditorDebugger::request_remote_tree() {252_put_msg("scene:request_scene_tree", Array());253}254255const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {256return scene_tree;257}258259void ScriptEditorDebugger::request_remote_evaluate(const String &p_expression, int p_stack_frame) {260Array msg = { p_expression, p_stack_frame };261_put_msg("evaluate", msg);262}263264void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value, const String &p_field) {265Array msg = { p_obj_id, p_prop, p_value };266if (p_field.is_empty()) {267_put_msg("scene:set_object_property", msg);268} else {269msg.push_back(p_field);270_put_msg("scene:set_object_property_field", msg);271}272}273274void ScriptEditorDebugger::request_remote_objects(const TypedArray<uint64_t> &p_obj_ids, bool p_update_selection) {275ERR_FAIL_COND(p_obj_ids.is_empty());276Array msg = { p_obj_ids.duplicate(), p_update_selection };277_put_msg("scene:inspect_objects", msg);278}279280void ScriptEditorDebugger::clear_inspector(bool p_send_msg) {281inspector->clear_remote_inspector();282if (p_send_msg) {283_put_msg("scene:clear_selection", Array());284}285}286287void ScriptEditorDebugger::_remote_object_selected(ObjectID p_id) {288Array arr = { p_id };289emit_signal(SNAME("remote_objects_requested"), arr);290}291292void ScriptEditorDebugger::_remote_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field) {293for (const KeyValue<Variant, Variant> &kv : p_values) {294update_remote_object(ObjectID(static_cast<uint64_t>(kv.key)), p_prop, kv.value, p_field);295}296request_remote_objects(p_values.keys(), false);297}298299void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const String &p_property) {300emit_signal(SNAME("remote_object_property_updated"), p_id, p_property);301}302303void ScriptEditorDebugger::_video_mem_request() {304_put_msg("servers:memory", Array());305}306307void ScriptEditorDebugger::_video_mem_export() {308file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);309file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);310file_dialog->clear_filters();311file_dialog_purpose = SAVE_VRAM_CSV;312file_dialog->popup_file_dialog();313}314315Size2 ScriptEditorDebugger::get_minimum_size() const {316Size2 ms = MarginContainer::get_minimum_size();317ms.y = MAX(ms.y, 250 * EDSCALE);318return ms;319}320321void ScriptEditorDebugger::_thread_debug_enter(uint64_t p_thread_id) {322ERR_FAIL_COND(!threads_debugged.has(p_thread_id));323ThreadDebugged &td = threads_debugged[p_thread_id];324_set_reason_text(td.error, MESSAGE_ERROR);325emit_signal(SNAME("breaked"), true, td.can_debug, td.error, td.has_stackdump);326if (!td.error.is_empty() && EDITOR_GET("debugger/auto_switch_to_stack_trace")) {327tabs->set_current_tab(0);328}329inspector->clear_cache(); // Take a chance to force remote objects update.330_put_msg("get_stack_dump", Array(), p_thread_id);331}332333void ScriptEditorDebugger::_select_thread(int p_index) {334debugging_thread_id = threads->get_item_metadata(threads->get_selected());335_thread_debug_enter(debugging_thread_id);336}337338void ScriptEditorDebugger::_msg_debug_enter(uint64_t p_thread_id, const Array &p_data) {339ERR_FAIL_COND(p_data.size() != 4);340341const Thread::ID caller_id = p_data[3];342343ThreadDebugged td;344td.name = (caller_id == Thread::get_main_id()) ? TTR("Main Thread") : itos(caller_id);345td.error = p_data[1];346td.can_debug = p_data[0];347td.has_stackdump = p_data[2];348td.thread_id = p_thread_id;349static uint32_t order_inc = 0;350td.debug_order = order_inc++;351352threads_debugged.insert(p_thread_id, td);353354if (threads_debugged.size() == 1) {355// First thread that requests debug356debugging_thread_id = p_thread_id;357_thread_debug_enter(p_thread_id);358can_request_idle_draw = true;359if (is_move_to_foreground()) {360DisplayServer::get_singleton()->window_move_to_foreground();361}362profiler->set_enabled(false, false);363visual_profiler->set_enabled(false);364}365_update_buttons_state();366}367368void ScriptEditorDebugger::_msg_debug_exit(uint64_t p_thread_id, const Array &p_data) {369threads_debugged.erase(p_thread_id);370if (p_thread_id == debugging_thread_id) {371_clear_execution();372if (threads_debugged.is_empty()) {373debugging_thread_id = Thread::UNASSIGNED_ID;374} else {375// Find next thread to debug.376uint32_t min_order = 0xFFFFFFFF;377uint64_t next_thread = Thread::UNASSIGNED_ID;378for (KeyValue<uint64_t, ThreadDebugged> T : threads_debugged) {379if (T.value.debug_order < min_order) {380min_order = T.value.debug_order;381next_thread = T.key;382}383}384385debugging_thread_id = next_thread;386}387388if (debugging_thread_id == Thread::UNASSIGNED_ID) {389// Nothing else to debug.390profiler->set_enabled(true, false);391profiler->disable_seeking();392393visual_profiler->set_enabled(true);394395_set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS);396emit_signal(SNAME("breaked"), false, false, "", false);397398_update_buttons_state();399} else {400_thread_debug_enter(debugging_thread_id);401}402} else {403_update_buttons_state();404}405}406407void ScriptEditorDebugger::_msg_set_pid(uint64_t p_thread_id, const Array &p_data) {408ERR_FAIL_COND(p_data.is_empty());409remote_pid = p_data[0];410// We emit the started signal after we've set the PID.411emit_signal(SNAME("started"));412}413414void ScriptEditorDebugger::_msg_scene_click_ctrl(uint64_t p_thread_id, const Array &p_data) {415ERR_FAIL_COND(p_data.size() < 2);416clicked_ctrl->set_text(p_data[0]);417clicked_ctrl_type->set_text(p_data[1]);418}419420void ScriptEditorDebugger::_msg_scene_scene_tree(uint64_t p_thread_id, const Array &p_data) {421scene_tree->nodes.clear();422scene_tree->deserialize(p_data);423emit_signal(SNAME("remote_tree_updated"));424_update_buttons_state();425}426427void ScriptEditorDebugger::_msg_scene_inspect_objects(uint64_t p_thread_id, const Array &p_data) {428ERR_FAIL_COND(p_data.is_empty());429EditorDebuggerRemoteObjects *objs = inspector->set_objects(p_data);430if (objs && EditorDebuggerNode::get_singleton()->match_remote_selection(objs->remote_object_ids)) {431EditorDebuggerNode::get_singleton()->stop_waiting_inspection();432433emit_signal(SNAME("remote_objects_updated"), objs);434}435}436437#ifndef DISABLE_DEPRECATED438void ScriptEditorDebugger::_msg_scene_inspect_object(uint64_t p_thread_id, const Array &p_data) {439ERR_FAIL_COND(p_data.is_empty());440// Legacy compatibility: convert single object response to new format.441// p_data is [id, className, properties] - wrap it as first element of array for new handler.442Array wrapped_data;443wrapped_data.push_back(p_data);444_msg_scene_inspect_objects(p_thread_id, wrapped_data);445}446#endif // DISABLE_DEPRECATED447448void ScriptEditorDebugger::_msg_servers_memory_usage(uint64_t p_thread_id, const Array &p_data) {449vmem_tree->clear();450TreeItem *root = vmem_tree->create_item();451ServersDebugger::ResourceUsage usage;452usage.deserialize(p_data);453454uint64_t total = 0;455456for (const ServersDebugger::ResourceInfo &E : usage.infos) {457TreeItem *it = vmem_tree->create_item(root);458String type = E.type;459int bytes = E.vram;460it->set_text(0, E.path);461it->set_text(1, type);462it->set_text(2, E.format);463it->set_text(3, String::humanize_size(bytes));464total += bytes;465466// If it does not have a theme icon, just go up the inheritance tree until we find one.467if (!has_theme_icon(type, EditorStringName(EditorIcons))) {468StringName base_type = type;469while (base_type != "Resource" || base_type != "") {470base_type = ClassDB::get_parent_class(base_type);471if (has_theme_icon(base_type, EditorStringName(EditorIcons))) {472type = base_type;473break;474}475}476}477478it->set_icon(0, get_editor_theme_icon(type));479}480481vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total));482vmem_total->set_text(String::humanize_size(total));483}484485void ScriptEditorDebugger::_msg_servers_drawn(uint64_t p_thread_id, const Array &p_data) {486can_request_idle_draw = true;487}488489void ScriptEditorDebugger::_msg_stack_dump(uint64_t p_thread_id, const Array &p_data) {490DebuggerMarshalls::ScriptStackDump stack;491stack.deserialize(p_data);492493stack_dump->clear();494inspector->clear_stack_variables();495TreeItem *r = stack_dump->create_item();496497Array stack_dump_info;498499int i = 0;500for (List<ScriptLanguage::StackInfo>::Iterator itr = stack.frames.begin(); itr != stack.frames.end(); ++itr, ++i) {501TreeItem *s = stack_dump->create_item(r);502Dictionary d;503d["frame"] = i;504d["file"] = itr->file;505d["function"] = itr->func;506d["line"] = itr->line;507stack_dump_info.push_back(d);508s->set_metadata(0, d);509510String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + String(d["function"]);511s->set_text(0, line);512513if (i == 0) {514s->select(0);515}516}517emit_signal(SNAME("stack_dump"), stack_dump_info);518}519520void ScriptEditorDebugger::_msg_stack_frame_vars(uint64_t p_thread_id, const Array &p_data) {521inspector->clear_stack_variables();522ERR_FAIL_COND(p_data.size() != 1);523emit_signal(SNAME("stack_frame_vars"), p_data[0]);524}525526void ScriptEditorDebugger::_msg_stack_frame_var(uint64_t p_thread_id, const Array &p_data) {527inspector->add_stack_variable(p_data);528emit_signal(SNAME("stack_frame_var"), p_data);529}530531void ScriptEditorDebugger::_msg_output(uint64_t p_thread_id, const Array &p_data) {532ERR_FAIL_COND(p_data.size() != 2);533534ERR_FAIL_COND(p_data[0].get_type() != Variant::PACKED_STRING_ARRAY);535Vector<String> output_strings = p_data[0];536537ERR_FAIL_COND(p_data[1].get_type() != Variant::PACKED_INT32_ARRAY);538Vector<int> output_types = p_data[1];539540ERR_FAIL_COND(output_strings.size() != output_types.size());541542for (int i = 0; i < output_strings.size(); i++) {543RemoteDebugger::MessageType type = (RemoteDebugger::MessageType)(int)(output_types[i]);544EditorLog::MessageType msg_type;545switch (type) {546case RemoteDebugger::MESSAGE_TYPE_LOG: {547msg_type = EditorLog::MSG_TYPE_STD;548} break;549case RemoteDebugger::MESSAGE_TYPE_LOG_RICH: {550msg_type = EditorLog::MSG_TYPE_STD_RICH;551} break;552case RemoteDebugger::MESSAGE_TYPE_ERROR: {553msg_type = EditorLog::MSG_TYPE_ERROR;554} break;555default: {556WARN_PRINT("Unhandled script debugger message type: " + itos(type));557msg_type = EditorLog::MSG_TYPE_STD;558} break;559}560EditorNode::get_log()->add_message(output_strings[i], msg_type);561emit_signal(SceneStringName(output), output_strings[i], msg_type);562}563}564565void ScriptEditorDebugger::_msg_performance_profile_frame(uint64_t p_thread_id, const Array &p_data) {566Vector<float> frame_data;567frame_data.resize(p_data.size());568for (int i = 0; i < p_data.size(); i++) {569frame_data.write[i] = p_data[i];570}571performance_profiler->add_profile_frame(frame_data);572}573574void ScriptEditorDebugger::_msg_visual_hardware_info(uint64_t p_thread_id, const Array &p_data) {575const String cpu_name = p_data[0];576const String gpu_name = p_data[1];577visual_profiler->set_hardware_info(cpu_name, gpu_name);578}579580void ScriptEditorDebugger::_msg_visual_profile_frame(uint64_t p_thread_id, const Array &p_data) {581ServersDebugger::VisualProfilerFrame frame;582frame.deserialize(p_data);583584EditorVisualProfiler::Metric metric;585metric.areas.resize(frame.areas.size());586metric.frame_number = frame.frame_number;587metric.valid = true;588589{590EditorVisualProfiler::Metric::Area *areas_ptr = metric.areas.ptrw();591for (int i = 0; i < frame.areas.size(); i++) {592areas_ptr[i].name = frame.areas[i].name;593areas_ptr[i].cpu_time = frame.areas[i].cpu_msec;594areas_ptr[i].gpu_time = frame.areas[i].gpu_msec;595}596}597visual_profiler->add_frame_metric(metric);598}599600void ScriptEditorDebugger::_msg_error(uint64_t p_thread_id, const Array &p_data) {601DebuggerMarshalls::OutputError oe;602ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message");603604// Format time.605Array time_vals = { oe.hr, oe.min, oe.sec, oe.msec };606bool e;607String time = String("%d:%02d:%02d:%03d").sprintf(time_vals, &e);608609// Rest of the error data.610bool source_is_project_file = oe.source_file.begins_with("res://");611612// Metadata to highlight error line in scripts.613Array source_meta = { oe.source_file, oe.source_line };614615// Create error tree to display above error or warning details.616TreeItem *r = error_tree->get_root();617if (!r) {618r = error_tree->create_item();619}620621// Also provide the relevant details as tooltip to quickly check without622// uncollapsing the tree.623String tooltip = oe.warning ? TTR("Warning:") : TTR("Error:");624625TreeItem *error = error_tree->create_item(r);626if (oe.warning) {627error->set_meta("_is_warning", true);628} else {629error->set_meta("_is_error", true);630}631error->set_collapsed(true);632633error->set_icon(0, get_editor_theme_icon(oe.warning ? SNAME("Warning") : SNAME("Error")));634error->set_text(0, time);635error->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);636637const Color color = get_theme_color(oe.warning ? SNAME("warning_color") : SNAME("error_color"), EditorStringName(Editor));638error->set_custom_color(0, color);639error->set_custom_color(1, color);640641String error_title;642if (!oe.source_func.is_empty() && source_is_project_file) {643// If source function is inside the project file.644error_title += oe.source_func + ": ";645} else if (oe.callstack.size() > 0) {646// Otherwise, if available, use the script's stack in the error title.647error_title = _format_frame_text(&oe.callstack[0]) + ": ";648} else if (!oe.source_func.is_empty()) {649// Otherwise try to use the C++ source function.650error_title += oe.source_func + ": ";651}652// If we have a (custom) error message, use it as title, and add a C++ Error653// item with the original error condition.654error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr;655error->set_text(1, error_title);656error->set_autowrap_mode(1, TextServer::AUTOWRAP_WORD_SMART);657tooltip += " " + error_title + "\n";658659// Find the language of the error's source file.660String source_language_name = "C++"; // Default value is the old hard-coded one.661const String source_file_extension = oe.source_file.get_extension();662for (int i = 0; i < ScriptServer::get_language_count(); ++i) {663ScriptLanguage *script_language = ScriptServer::get_language(i);664if (source_file_extension == script_language->get_extension()) {665source_language_name = script_language->get_name();666break;667}668}669670if (!oe.error_descr.is_empty()) {671// Add item for C++ error condition.672TreeItem *cpp_cond = error_tree->create_item(error);673// TRANSLATORS: %s is the name of a language, e.g. C++.674cpp_cond->set_text(0, "<" + vformat(TTR("%s Error"), source_language_name) + ">");675cpp_cond->set_text(1, oe.error);676cpp_cond->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);677tooltip += vformat(TTR("%s Error:"), source_language_name) + " " + oe.error + "\n";678if (source_is_project_file) {679cpp_cond->set_metadata(0, source_meta);680}681}682Vector<uint8_t> v;683v.resize(100);684685// Source of the error.686String source_txt = (source_is_project_file ? oe.source_file.get_file() : oe.source_file) + ":" + itos(oe.source_line);687if (!oe.source_func.is_empty()) {688source_txt += " @ " + oe.source_func;689if (!oe.source_func.ends_with(")")) {690source_txt += "()";691}692}693694TreeItem *cpp_source = error_tree->create_item(error);695// TRANSLATORS: %s is the name of a language, e.g. C++.696cpp_source->set_text(0, "<" + vformat(TTR("%s Source"), source_language_name) + ">");697cpp_source->set_text(1, source_txt);698cpp_source->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);699tooltip += vformat(TTR("%s Source:"), source_language_name) + " " + source_txt + "\n";700701// Set metadata to highlight error line in scripts.702if (source_is_project_file) {703error->set_metadata(0, source_meta);704cpp_source->set_metadata(0, source_meta);705}706707// Format stack trace.708// stack_items_count is the number of elements to parse, with 3 items per frame709// of the stack trace (script, method, line).710const ScriptLanguage::StackInfo *infos = oe.callstack.ptr();711for (unsigned int i = 0; i < (unsigned int)oe.callstack.size(); i++) {712TreeItem *stack_trace = error_tree->create_item(error);713714Array meta = { infos[i].file, infos[i].line };715stack_trace->set_metadata(0, meta);716717if (i == 0) {718stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">");719stack_trace->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);720if (!source_is_project_file) {721// Only override metadata if the source is not inside the project.722error->set_metadata(0, meta);723}724tooltip += TTR("Stack Trace:") + "\n";725}726727String frame_txt = _format_frame_text(&infos[i]);728tooltip += frame_txt + "\n";729stack_trace->set_text(1, frame_txt);730}731732error->set_tooltip_text(0, tooltip);733error->set_tooltip_text(1, tooltip);734735if (warning_count == 0 && error_count == 0) {736expand_all_button->set_disabled(false);737collapse_all_button->set_disabled(false);738clear_button->set_disabled(false);739}740741if (oe.warning) {742warning_count++;743} else {744error_count++;745}746}747748void ScriptEditorDebugger::_msg_servers_function_signature(uint64_t p_thread_id, const Array &p_data) {749// Cache a profiler signature.750ServersDebugger::ScriptFunctionSignature sig;751sig.deserialize(p_data);752profiler_signature[sig.id] = sig.name;753}754755void ScriptEditorDebugger::_msg_servers_profile_common(const Array &p_data, const bool p_final) {756EditorProfiler::Metric metric;757ServersDebugger::ServersProfilerFrame frame;758frame.deserialize(p_data);759metric.valid = true;760metric.frame_number = frame.frame_number;761metric.frame_time = frame.frame_time;762metric.process_time = frame.process_time;763metric.physics_time = frame.physics_time;764metric.physics_frame_time = frame.physics_frame_time;765766if (frame.servers.size()) {767EditorProfiler::Metric::Category frame_time;768frame_time.signature = "category_frame_time";769frame_time.name = "Frame Time";770frame_time.total_time = metric.frame_time;771772EditorProfiler::Metric::Category::Item item;773item.calls = 1;774item.line = 0;775776item.name = "Physics Time";777item.total = metric.physics_time;778item.self = item.total;779item.signature = "physics_time";780781frame_time.items.push_back(item);782783item.name = "Process Time";784item.total = metric.process_time;785item.self = item.total;786item.signature = "process_time";787788frame_time.items.push_back(item);789790item.name = "Physics Frame Time";791item.total = metric.physics_frame_time;792item.self = item.total;793item.signature = "physics_frame_time";794795frame_time.items.push_back(item);796797metric.categories.push_back(frame_time);798}799800for (const ServersDebugger::ServerInfo &srv : frame.servers) {801EditorProfiler::Metric::Category c;802const String name = srv.name;803c.name = EditorPropertyNameProcessor::get_singleton()->process_name(name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);804c.items.resize(srv.functions.size());805c.total_time = 0;806c.signature = "categ::" + name;807int j = 0;808for (List<ServersDebugger::ServerFunctionInfo>::ConstIterator itr = srv.functions.begin(); itr != srv.functions.end(); ++itr, ++j) {809EditorProfiler::Metric::Category::Item item;810item.calls = 1;811item.line = 0;812item.name = itr->name;813item.self = itr->time;814item.total = item.self;815item.signature = "categ::" + name + "::" + item.name;816item.name = EditorPropertyNameProcessor::get_singleton()->process_name(item.name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);817c.total_time += item.total;818c.items.write[j] = item;819}820metric.categories.push_back(c);821}822823EditorProfiler::Metric::Category funcs;824funcs.total_time = frame.script_time;825funcs.items.resize(frame.script_functions.size());826funcs.name = "Script Functions";827funcs.signature = "script_functions";828for (int i = 0; i < frame.script_functions.size(); i++) {829int signature = frame.script_functions[i].sig_id;830int calls = frame.script_functions[i].call_count;831float total = frame.script_functions[i].total_time;832float self = frame.script_functions[i].self_time;833float internal = frame.script_functions[i].internal_time;834835EditorProfiler::Metric::Category::Item item;836if (profiler_signature.has(signature)) {837item.signature = profiler_signature[signature];838839String name = profiler_signature[signature];840Vector<String> strings = name.split("::");841if (strings.size() == 3) {842item.name = strings[2];843item.script = strings[0];844item.line = strings[1].to_int();845} else if (strings.size() == 4) { //Built-in scripts have an :: in their name846item.name = strings[3];847item.script = strings[0] + "::" + strings[1];848item.line = strings[2].to_int();849}850851} else {852item.name = "SigErr " + itos(signature);853}854855item.calls = calls;856item.self = self;857item.total = total;858item.internal = internal;859funcs.items.write[i] = item;860}861862metric.categories.push_back(funcs);863864profiler->add_frame_metric(metric, p_final);865}866867void ScriptEditorDebugger::_msg_servers_profile_frame(uint64_t p_thread_id, const Array &p_data) {868_msg_servers_profile_common(p_data, false);869}870void ScriptEditorDebugger::_msg_servers_profile_total(uint64_t p_thread_id, const Array &p_data) {871_msg_servers_profile_common(p_data, true);872}873874void ScriptEditorDebugger::_msg_request_quit(uint64_t p_thread_id, const Array &p_data) {875emit_signal(SNAME("stop_requested"));876_stop_and_notify();877}878879void ScriptEditorDebugger::_msg_remote_objects_selected(uint64_t p_thread_id, const Array &p_data) {880ERR_FAIL_COND(p_data.is_empty());881EditorDebuggerRemoteObjects *objs = inspector->set_objects(p_data);882if (objs) {883EditorDebuggerNode::get_singleton()->stop_waiting_inspection();884885emit_signal(SNAME("remote_objects_updated"), objs);886emit_signal(SNAME("remote_tree_select_requested"), objs->remote_object_ids.duplicate());887}888}889890void ScriptEditorDebugger::_msg_remote_nothing_selected(uint64_t p_thread_id, const Array &p_data) {891EditorDebuggerNode::get_singleton()->stop_waiting_inspection();892893emit_signal(SNAME("remote_tree_clear_selection_requested"));894}895896void ScriptEditorDebugger::_msg_remote_selection_invalidated(uint64_t p_thread_id, const Array &p_data) {897ERR_FAIL_COND(p_data.is_empty());898inspector->invalidate_selection_from_cache(p_data[0]);899}900901void ScriptEditorDebugger::_msg_show_selection_limit_warning(uint64_t p_thread_id, const Array &p_data) {902EditorToaster::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);903}904905void ScriptEditorDebugger::_msg_performance_profile_names(uint64_t p_thread_id, const Array &p_data) {906Vector<StringName> monitors;907monitors.resize(p_data.size());908for (int i = 0; i < p_data.size(); i++) {909ERR_FAIL_COND(p_data[i].get_type() != Variant::STRING_NAME);910monitors.set(i, p_data[i]);911}912performance_profiler->update_monitors(monitors);913}914915void ScriptEditorDebugger::_msg_filesystem_update_file(uint64_t p_thread_id, const Array &p_data) {916ERR_FAIL_COND(p_data.is_empty());917if (EditorFileSystem::get_singleton()) {918EditorFileSystem::get_singleton()->update_file(p_data[0]);919}920}921922void ScriptEditorDebugger::_msg_evaluation_return(uint64_t p_thread_id, const Array &p_data) {923expression_evaluator->add_value(p_data);924}925926void ScriptEditorDebugger::_msg_window_title(uint64_t p_thread_id, const Array &p_data) {927ERR_FAIL_COND(p_data.size() != 1);928emit_signal(SNAME("remote_window_title_changed"), p_data[0]);929}930931void ScriptEditorDebugger::_msg_embed_suspend_toggle(uint64_t p_thread_id, const Array &p_data) {932emit_signal(SNAME("embed_shortcut_requested"), EMBED_SUSPEND_TOGGLE);933}934935void ScriptEditorDebugger::_msg_embed_next_frame(uint64_t p_thread_id, const Array &p_data) {936emit_signal(SNAME("embed_shortcut_requested"), EMBED_NEXT_FRAME);937}938939void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data) {940emit_signal(SNAME("debug_data"), p_msg, p_data);941942ParseMessageFunc *fn_ptr = parse_message_handlers.getptr(p_msg);943if (fn_ptr) {944(this->**fn_ptr)(p_thread_id, p_data);945} else {946int colon_index = p_msg.find_char(':');947ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");948949bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data);950if (!parsed) {951WARN_PRINT("Unknown message: " + p_msg);952}953}954}955956HashMap<String, ScriptEditorDebugger::ParseMessageFunc> ScriptEditorDebugger::parse_message_handlers;957958void ScriptEditorDebugger::_init_parse_message_handlers() {959parse_message_handlers["debug_enter"] = &ScriptEditorDebugger::_msg_debug_enter;960parse_message_handlers["debug_exit"] = &ScriptEditorDebugger::_msg_debug_exit;961parse_message_handlers["set_pid"] = &ScriptEditorDebugger::_msg_set_pid;962parse_message_handlers["scene:click_ctrl"] = &ScriptEditorDebugger::_msg_scene_click_ctrl;963parse_message_handlers["scene:scene_tree"] = &ScriptEditorDebugger::_msg_scene_scene_tree;964parse_message_handlers["scene:inspect_objects"] = &ScriptEditorDebugger::_msg_scene_inspect_objects;965#ifndef DISABLE_DEPRECATED966parse_message_handlers["scene:inspect_object"] = &ScriptEditorDebugger::_msg_scene_inspect_object;967#endif // DISABLE_DEPRECATED968parse_message_handlers["servers:memory_usage"] = &ScriptEditorDebugger::_msg_servers_memory_usage;969parse_message_handlers["servers:drawn"] = &ScriptEditorDebugger::_msg_servers_drawn;970parse_message_handlers["stack_dump"] = &ScriptEditorDebugger::_msg_stack_dump;971parse_message_handlers["stack_frame_vars"] = &ScriptEditorDebugger::_msg_stack_frame_vars;972parse_message_handlers["stack_frame_var"] = &ScriptEditorDebugger::_msg_stack_frame_var;973parse_message_handlers["output"] = &ScriptEditorDebugger::_msg_output;974parse_message_handlers["performance:profile_frame"] = &ScriptEditorDebugger::_msg_performance_profile_frame;975parse_message_handlers["visual:hardware_info"] = &ScriptEditorDebugger::_msg_visual_hardware_info;976parse_message_handlers["visual:profile_frame"] = &ScriptEditorDebugger::_msg_visual_profile_frame;977parse_message_handlers["error"] = &ScriptEditorDebugger::_msg_error;978parse_message_handlers["servers:function_signature"] = &ScriptEditorDebugger::_msg_servers_function_signature;979parse_message_handlers["servers:profile_frame"] = &ScriptEditorDebugger::_msg_servers_profile_frame;980parse_message_handlers["servers:profile_total"] = &ScriptEditorDebugger::_msg_servers_profile_total;981parse_message_handlers["request_quit"] = &ScriptEditorDebugger::_msg_request_quit;982parse_message_handlers["remote_objects_selected"] = &ScriptEditorDebugger::_msg_remote_objects_selected;983parse_message_handlers["remote_nothing_selected"] = &ScriptEditorDebugger::_msg_remote_nothing_selected;984parse_message_handlers["remote_selection_invalidated"] = &ScriptEditorDebugger::_msg_remote_selection_invalidated;985parse_message_handlers["show_selection_limit_warning"] = &ScriptEditorDebugger::_msg_show_selection_limit_warning;986parse_message_handlers["performance:profile_names"] = &ScriptEditorDebugger::_msg_performance_profile_names;987parse_message_handlers["filesystem:update_file"] = &ScriptEditorDebugger::_msg_filesystem_update_file;988parse_message_handlers["evaluation_return"] = &ScriptEditorDebugger::_msg_evaluation_return;989parse_message_handlers["window:title"] = &ScriptEditorDebugger::_msg_window_title;990parse_message_handlers["request_embed_suspend_toggle"] = &ScriptEditorDebugger::_msg_embed_suspend_toggle;991parse_message_handlers["request_embed_next_frame"] = &ScriptEditorDebugger::_msg_embed_next_frame;992}993994void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType p_type) {995switch (p_type) {996case MESSAGE_ERROR:997reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));998break;999case MESSAGE_WARNING:1000reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));1001break;1002default:1003reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("success_color"), EditorStringName(Editor)));1004break;1005}10061007reason->set_text(p_reason);10081009_update_reason_content_height();10101011const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80);1012PackedStringArray lines;1013for (int i = 0; i < boundaries.size(); i += 2) {1014const int start = boundaries[i];1015const int end = boundaries[i + 1];1016lines.append(p_reason.substr(start, end - start));1017}10181019reason->set_tooltip_text(String("\n").join(lines));1020}10211022void ScriptEditorDebugger::_update_reason_content_height() {1023float margin_height = 0;1024const Ref<StyleBox> style = reason->get_theme_stylebox(CoreStringName(normal));1025if (style.is_valid()) {1026margin_height += style->get_content_margin(SIDE_TOP) + style->get_content_margin(SIDE_BOTTOM);1027}10281029const float content_height = margin_height + reason->get_content_height();10301031float content_max_height = margin_height;1032for (int i = 0; i < 3; i++) {1033if (i >= reason->get_line_count()) {1034break;1035}1036content_max_height += reason->get_line_height(i);1037}10381039reason->set_custom_minimum_size(Size2(0, CLAMP(content_height, 0, content_max_height)));1040}10411042void ScriptEditorDebugger::_notification(int p_what) {1043switch (p_what) {1044case NOTIFICATION_ENTER_TREE: {1045le_set->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_live_edit_set));1046le_clear->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_live_edit_clear));1047error_tree->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_error_selected));1048error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated));1049breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));1050connect("started", callable_mp(expression_evaluator, &EditorExpressionEvaluator::on_start));1051} break;10521053case NOTIFICATION_THEME_CHANGED: {1054tabs->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));10551056skip_breakpoints->set_button_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff")));1057ignore_error_breaks->set_button_icon(get_editor_theme_icon(ignore_error_breaks_value ? SNAME("NotificationDisabled") : SNAME("Notification")));1058ignore_error_breaks->add_theme_color_override("icon_normal_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1059ignore_error_breaks->add_theme_color_override("icon_hover_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1060ignore_error_breaks->add_theme_color_override("icon_pressed_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1061ignore_error_breaks->add_theme_color_override("icon_focus_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));1062copy->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy")));1063step->set_button_icon(get_editor_theme_icon(SNAME("DebugStep")));1064next->set_button_icon(get_editor_theme_icon(SNAME("DebugNext")));1065dobreak->set_button_icon(get_editor_theme_icon(SNAME("Pause")));1066docontinue->set_button_icon(get_editor_theme_icon(SNAME("DebugContinue")));1067vmem_notice_icon->set_texture(get_editor_theme_icon(SNAME("NodeInfo")));1068vmem_refresh->set_button_icon(get_editor_theme_icon(SNAME("Reload")));1069vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));1070search->set_right_icon(get_editor_theme_icon(SNAME("Search")));10711072reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));1073reason->add_theme_style_override(SNAME("normal"), get_theme_stylebox(SNAME("normal"), SNAME("Label"))); // Empty stylebox.10741075TreeItem *error_root = error_tree->get_root();1076if (error_root) {1077TreeItem *error = error_root->get_first_child();1078while (error) {1079if (error->has_meta("_is_warning")) {1080error->set_icon(0, get_editor_theme_icon(SNAME("Warning")));1081error->set_custom_color(0, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));1082error->set_custom_color(1, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));1083} else if (error->has_meta("_is_error")) {1084error->set_icon(0, get_editor_theme_icon(SNAME("Error")));1085error->set_custom_color(0, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));1086error->set_custom_color(1, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));1087}10881089error = error->get_next();1090}1091}1092} break;10931094case NOTIFICATION_PROCESS: {1095if (is_session_active()) {1096peer->poll();10971098if (camera_override == CameraOverride::OVERRIDE_EDITORS) {1099// CanvasItem Editor1100{1101Dictionary state = CanvasItemEditor::get_singleton()->get_state();1102float zoom = state["zoom"];1103Point2 offset = state["ofs"];1104Transform2D transform;11051106transform.scale_basis(Size2(zoom, zoom));1107transform.columns[2] = -offset * zoom;11081109Array msg = { transform };1110_put_msg("scene:transform_camera_2d", msg);1111}11121113// Node3D Editor1114{1115Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_last_used_viewport();1116const Camera3D *cam = viewport->get_camera_3d();11171118Array msg = { cam->get_camera_transform() };1119if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {1120msg.push_back(false);1121msg.push_back(cam->get_size());1122} else {1123msg.push_back(true);1124msg.push_back(cam->get_fov());1125}1126msg.push_back(cam->get_near());1127msg.push_back(cam->get_far());1128_put_msg("scene:transform_camera_3d", msg);1129}1130}11311132if (is_breaked() && can_request_idle_draw) {1133_put_msg("servers:draw", Array());1134can_request_idle_draw = false;1135}1136}11371138const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20;11391140while (peer.is_valid() && peer->has_message()) {1141Array arr = peer->get_message();1142if (arr.size() != 3 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::INT || arr[2].get_type() != Variant::ARRAY) {1143_stop_and_notify();1144ERR_FAIL_MSG("Invalid message format received from peer");1145}11461147_parse_message(arr[0], arr[1], arr[2]);11481149if (OS::get_singleton()->get_ticks_msec() > until) {1150break;1151}1152}1153if (!is_session_active()) {1154_stop_and_notify();1155break;1156};1157} break;1158}1159}11601161void ScriptEditorDebugger::_clear_execution() {1162TreeItem *ti = stack_dump->get_selected();1163if (!ti) {1164return;1165}11661167Dictionary d = ti->get_metadata(0);11681169stack_script = ResourceLoader::load(d["file"]);1170emit_signal(SNAME("clear_execution"), stack_script);1171stack_script.unref();1172stack_dump->clear();1173inspector->clear_stack_variables();1174}11751176void ScriptEditorDebugger::_set_breakpoint(const String &p_file, const int &p_line, const bool &p_enabled) {1177Ref<Script> scr = ResourceLoader::load(p_file);1178emit_signal(SNAME("set_breakpoint"), scr, p_line - 1, p_enabled);1179scr.unref();1180}11811182void ScriptEditorDebugger::_clear_breakpoints() {1183emit_signal(SNAME("clear_breakpoints"));1184}11851186void ScriptEditorDebugger::_breakpoint_tree_clicked() {1187TreeItem *selected = breakpoints_tree->get_selected();1188if (selected->has_meta("line")) {1189emit_signal(SNAME("breakpoint_selected"), selected->get_parent()->get_text(0), int(selected->get_meta("line")));1190}1191}11921193String ScriptEditorDebugger::_format_frame_text(const ScriptLanguage::StackInfo *info) {1194String text = info->file.get_file() + ":" + itos(info->line) + " @ " + info->func;1195if (!text.ends_with(")")) {1196text += "()";1197}1198return text;1199}12001201void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {1202_clear_errors_list();1203stop();12041205profiler->set_enabled(true, true);1206visual_profiler->set_enabled(true);12071208peer = p_peer;1209ERR_FAIL_COND(p_peer.is_null());12101211performance_profiler->reset();12121213set_process(true);1214camera_override = CameraOverride::OVERRIDE_NONE;12151216_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);1217_update_buttons_state();12181219Array quit_keys = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));1220_put_msg("scene:setup_scene", quit_keys);12211222if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) {1223profiler->set_profiling(true);1224}12251226if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) {1227visual_profiler->set_profiling(true);1228}1229}12301231void ScriptEditorDebugger::_update_buttons_state() {1232const bool active = is_session_active();1233const bool has_editor_tree = active && editor_remote_tree && editor_remote_tree->get_selected();1234vmem_refresh->set_disabled(!active);1235step->set_disabled(!active || !is_breaked() || !is_debuggable());1236next->set_disabled(!active || !is_breaked() || !is_debuggable());1237copy->set_disabled(!active || !is_breaked());1238docontinue->set_disabled(!active || !is_breaked());1239dobreak->set_disabled(!active || is_breaked());1240le_clear->set_disabled(!active);1241le_set->set_disabled(!has_editor_tree);12421243thread_list_updating = true;1244LocalVector<ThreadDebugged *> threadss;1245for (KeyValue<uint64_t, ThreadDebugged> &I : threads_debugged) {1246threadss.push_back(&I.value);1247}1248threads->set_disabled(threadss.is_empty());12491250threadss.sort_custom<ThreadSort>();1251threads->clear();1252int32_t selected_index = -1;1253for (uint32_t i = 0; i < threadss.size(); i++) {1254if (debugging_thread_id == threadss[i]->thread_id) {1255selected_index = i;1256}1257threads->add_item(threadss[i]->name);1258threads->set_item_metadata(threads->get_item_count() - 1, threadss[i]->thread_id);1259}1260if (selected_index != -1) {1261threads->select(selected_index);1262}12631264thread_list_updating = false;1265}12661267void ScriptEditorDebugger::_stop_and_notify() {1268stop();1269emit_signal(SNAME("stopped"));1270_set_reason_text(TTR("Debug session closed."), MESSAGE_WARNING);1271}12721273void ScriptEditorDebugger::stop() {1274set_process(false);1275threads_debugged.clear();1276debugging_thread_id = Thread::UNASSIGNED_ID;1277remote_pid = 0;1278_clear_execution();12791280inspector->clear_cache();12811282if (peer.is_valid()) {1283peer->close();1284peer.unref();1285reason->set_text("");1286reason->set_tooltip_text("");1287reason->set_custom_minimum_size(Size2(0, 0));1288}12891290node_path_cache.clear();1291res_path_cache.clear();1292profiler_signature.clear();12931294profiler->set_enabled(false, false);1295profiler->set_profiling(false);12961297visual_profiler->set_enabled(false);1298visual_profiler->set_profiling(false);12991300inspector->edit(nullptr);1301_update_buttons_state();1302}13031304void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {1305Array msg_data = { p_enable };1306switch (p_type) {1307case PROFILER_VISUAL:1308_put_msg("profiler:visual", msg_data);1309break;1310case PROFILER_SCRIPTS_SERVERS:1311if (p_enable) {1312// Clear old script signatures. (should we move all this into the profiler?)1313profiler_signature.clear();1314// Add max funcs options to request.1315int max_funcs = EDITOR_GET("debugger/profiler_frame_max_functions");1316bool include_native = EDITOR_GET("debugger/profile_native_calls");1317Array opts = { CLAMP(max_funcs, 16, 512), include_native };1318msg_data.push_back(opts);1319}1320_put_msg("profiler:servers", msg_data);1321break;1322default:1323ERR_FAIL_MSG("Invalid profiler type");1324}1325}13261327void ScriptEditorDebugger::_profiler_seeked() {1328if (is_breaked()) {1329return;1330}1331debug_break();1332}13331334void ScriptEditorDebugger::_stack_dump_frame_selected() {1335emit_signal(SNAME("stack_frame_selected"));13361337int frame = get_stack_script_frame();13381339if (!request_stack_dump(frame)) {1340inspector->edit(nullptr);1341}1342}13431344void ScriptEditorDebugger::_export_csv() {1345file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);1346file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);1347file_dialog_purpose = SAVE_MONITORS_CSV;1348file_dialog->popup_file_dialog();1349}13501351String ScriptEditorDebugger::get_var_value(const String &p_var) const {1352if (!is_breaked()) {1353return String();1354}1355return inspector->get_stack_variable(p_var);1356}13571358void ScriptEditorDebugger::_resources_reimported(const PackedStringArray &p_resources) {1359Array msg = { p_resources };1360_put_msg("scene:reload_cached_files", msg);1361}13621363int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {1364const int *r = node_path_cache.getptr(p_path);1365if (r) {1366return *r;1367}13681369last_path_id++;13701371node_path_cache[p_path] = last_path_id;1372Array msg = { p_path, last_path_id };1373_put_msg("scene:live_node_path", msg);13741375return last_path_id;1376}13771378int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) {1379HashMap<String, int>::Iterator E = res_path_cache.find(p_path);13801381if (E) {1382return E->value;1383}13841385last_path_id++;13861387res_path_cache[p_path] = last_path_id;1388Array msg = { p_path, last_path_id };1389_put_msg("scene:live_res_path", msg);13901391return last_path_id;1392}13931394void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {1395if (!p_base || !live_debug || !is_session_active() || !EditorNode::get_singleton()->get_edited_scene()) {1396return;1397}13981399Node *node = Object::cast_to<Node>(p_base);14001401for (int i = 0; i < p_argcount; i++) {1402//no pointers, sorry1403if (p_args[i]->get_type() == Variant::OBJECT || p_args[i]->get_type() == Variant::RID) {1404return;1405}1406}14071408if (node) {1409NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node);1410int pathid = _get_node_path_cache(path);14111412Array msg = { pathid, p_name };1413for (int i = 0; i < p_argcount; i++) {1414//no pointers, sorry1415msg.push_back(*p_args[i]);1416}1417_put_msg("scene:live_node_call", msg);14181419return;1420}14211422Resource *res = Object::cast_to<Resource>(p_base);14231424if (res && !res->get_path().is_empty()) {1425String respath = res->get_path();1426int pathid = _get_res_path_cache(respath);14271428Array msg = { pathid, p_name };1429for (int i = 0; i < p_argcount; i++) {1430//no pointers, sorry1431msg.push_back(*p_args[i]);1432}1433_put_msg("scene:live_res_call", msg);14341435return;1436}1437}14381439void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p_property, const Variant &p_value) {1440if (!p_base || !live_debug || !EditorNode::get_singleton()->get_edited_scene()) {1441return;1442}14431444Node *node = Object::cast_to<Node>(p_base);14451446if (node) {1447NodePath path = EditorNode::get_singleton()->get_edited_scene()->get_path_to(node);1448int pathid = _get_node_path_cache(path);14491450if (p_value.is_ref_counted()) {1451Ref<Resource> res = p_value;1452if (res.is_valid() && !res->get_path().is_empty()) {1453Array msg = { pathid, p_property, res->get_path() };1454_put_msg("scene:live_node_prop_res", msg);1455}1456} else {1457Array msg = { pathid, p_property, p_value };1458_put_msg("scene:live_node_prop", msg);1459}14601461return;1462}14631464Resource *res = Object::cast_to<Resource>(p_base);14651466if (res && !res->get_path().is_empty()) {1467String respath = res->get_path();1468int pathid = _get_res_path_cache(respath);14691470if (p_value.is_ref_counted()) {1471Ref<Resource> res2 = p_value;1472if (res2.is_valid() && !res2->get_path().is_empty()) {1473Array msg = { pathid, p_property, res2->get_path() };1474_put_msg("scene:live_res_prop_res", msg);1475}1476} else {1477Array msg = { pathid, p_property, p_value };1478_put_msg("scene:live_res_prop", msg);1479}14801481return;1482}1483}14841485bool ScriptEditorDebugger::is_move_to_foreground() const {1486return move_to_foreground;1487}14881489void ScriptEditorDebugger::set_move_to_foreground(const bool &p_move_to_foreground) {1490move_to_foreground = p_move_to_foreground;1491}14921493String ScriptEditorDebugger::get_stack_script_file() const {1494TreeItem *ti = stack_dump->get_selected();1495if (!ti) {1496return "";1497}1498Dictionary d = ti->get_metadata(0);1499return d["file"];1500}15011502int ScriptEditorDebugger::get_stack_script_line() const {1503TreeItem *ti = stack_dump->get_selected();1504if (!ti) {1505return -1;1506}1507Dictionary d = ti->get_metadata(0);1508return d["line"];1509}15101511int ScriptEditorDebugger::get_stack_script_frame() const {1512TreeItem *ti = stack_dump->get_selected();1513if (!ti) {1514return -1;1515}1516Dictionary d = ti->get_metadata(0);1517return d["frame"];1518}15191520bool ScriptEditorDebugger::request_stack_dump(const int &p_frame) {1521ERR_FAIL_COND_V(!is_session_active() || p_frame < 0, false);15221523Array msg = { p_frame };1524_put_msg("get_stack_frame_vars", msg, debugging_thread_id);1525return true;1526}15271528void ScriptEditorDebugger::set_live_debugging(bool p_enable) {1529live_debug = p_enable;1530}15311532void ScriptEditorDebugger::_live_edit_set() {1533if (!is_session_active() || !editor_remote_tree) {1534return;1535}15361537TreeItem *ti = editor_remote_tree->get_selected();1538if (!ti) {1539return;1540}15411542String path;15431544while (ti) {1545String lp = ti->get_text(0);1546path = "/" + lp + path;1547ti = ti->get_parent();1548}15491550NodePath np = path;15511552EditorNode::get_editor_data().set_edited_scene_live_edit_root(np);15531554update_live_edit_root();1555}15561557void ScriptEditorDebugger::_live_edit_clear() {1558NodePath np = NodePath("/root");1559EditorNode::get_editor_data().set_edited_scene_live_edit_root(np);15601561update_live_edit_root();1562}15631564void ScriptEditorDebugger::update_live_edit_root() {1565NodePath np = EditorNode::get_editor_data().get_edited_scene_live_edit_root();15661567Array msg = { np };1568if (EditorNode::get_singleton()->get_edited_scene()) {1569msg.push_back(EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path());1570} else {1571msg.push_back("");1572}1573_put_msg("scene:live_set_root", msg);1574live_edit_root->set_text(String(np));1575}15761577void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {1578if (live_debug) {1579Array msg = { p_parent, p_type, p_name };1580_put_msg("scene:live_create_node", msg);1581}1582}15831584void ScriptEditorDebugger::live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name) {1585if (live_debug) {1586Array msg = { p_parent, p_path, p_name };1587_put_msg("scene:live_instantiate_node", msg);1588}1589}15901591void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {1592if (live_debug) {1593Array msg = { p_at };1594_put_msg("scene:live_remove_node", msg);1595}1596}15971598void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {1599if (live_debug) {1600Array msg = { p_at, p_keep_id };1601_put_msg("scene:live_remove_and_keep_node", msg);1602}1603}16041605void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {1606if (live_debug) {1607Array msg = { p_id, p_at, p_at_pos };1608_put_msg("scene:live_restore_node", msg);1609}1610}16111612void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {1613if (live_debug) {1614Array msg = { p_at, p_new_name };1615_put_msg("scene:live_duplicate_node", msg);1616}1617}16181619void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {1620if (live_debug) {1621Array msg = { p_at, p_new_place, p_new_name, p_at_pos };1622_put_msg("scene:live_reparent_node", msg);1623}1624}16251626bool ScriptEditorDebugger::get_debug_mute_audio() const {1627return debug_mute_audio;1628}16291630void ScriptEditorDebugger::set_debug_mute_audio(bool p_mute) {1631Array msg = { p_mute };1632_put_msg("scene:debug_mute_audio", msg);1633debug_mute_audio = p_mute;1634}16351636CameraOverride ScriptEditorDebugger::get_camera_override() const {1637return camera_override;1638}16391640void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {1641Array msg = {1642p_override != CameraOverride::OVERRIDE_NONE,1643p_override == CameraOverride::OVERRIDE_EDITORS1644};1645_put_msg("scene:override_cameras", msg);16461647camera_override = p_override;1648}16491650void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {1651Array msg = { p_path, p_line, p_enabled };1652_put_msg("breakpoint", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);16531654TreeItem *path_item = breakpoints_tree->search_item_text(p_path);1655if (path_item == nullptr) {1656if (!p_enabled) {1657return;1658}1659path_item = breakpoints_tree->create_item();1660path_item->set_text(0, p_path);1661}16621663int idx = 0;1664TreeItem *breakpoint_item;1665for (breakpoint_item = path_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {1666if ((int)breakpoint_item->get_meta("line") < p_line) {1667idx++;1668continue;1669}16701671if ((int)breakpoint_item->get_meta("line") == p_line) {1672break;1673}1674}16751676if (breakpoint_item == nullptr) {1677if (!p_enabled) {1678return;1679}1680breakpoint_item = breakpoints_tree->create_item(path_item, idx);1681breakpoint_item->set_meta("line", p_line);1682breakpoint_item->set_text(0, vformat(TTR("Line %d"), p_line));1683return;1684}16851686if (!p_enabled) {1687path_item->remove_child(breakpoint_item);1688if (path_item->get_first_child() == nullptr) {1689breakpoints_tree->get_root()->remove_child(path_item);1690}1691}1692}16931694void ScriptEditorDebugger::reload_all_scripts() {1695_put_msg("reload_all_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);1696}16971698void ScriptEditorDebugger::reload_scripts(const Vector<String> &p_script_paths) {1699_put_msg("reload_scripts", Variant(p_script_paths).operator Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);1700}17011702bool ScriptEditorDebugger::is_skip_breakpoints() const {1703return skip_breakpoints_value;1704}17051706bool ScriptEditorDebugger::is_ignore_error_breaks() const {1707return ignore_error_breaks_value;1708}17091710void ScriptEditorDebugger::_error_activated() {1711TreeItem *selected = error_tree->get_selected();17121713if (!selected) {1714return;1715}17161717TreeItem *ci = selected->get_first_child();1718if (ci) {1719selected->set_collapsed(!selected->is_collapsed());1720}1721}17221723void ScriptEditorDebugger::_error_selected() {1724TreeItem *selected = error_tree->get_selected();17251726if (!selected) {1727return;1728}17291730Array meta = selected->get_metadata(0);1731if (meta.is_empty()) {1732return;1733}17341735emit_signal(SNAME("error_selected"), String(meta[0]), int(meta[1]));1736}17371738void ScriptEditorDebugger::_expand_errors_list() {1739TreeItem *root = error_tree->get_root();1740if (!root) {1741return;1742}17431744TreeItem *item = root->get_first_child();1745while (item) {1746item->set_collapsed(false);1747item = item->get_next();1748}1749}17501751void ScriptEditorDebugger::_collapse_errors_list() {1752TreeItem *root = error_tree->get_root();1753if (!root) {1754return;1755}17561757TreeItem *item = root->get_first_child();1758while (item) {1759item->set_collapsed(true);1760item = item->get_next();1761}1762}17631764void ScriptEditorDebugger::_vmem_item_activated() {1765TreeItem *selected = vmem_tree->get_selected();1766if (!selected) {1767return;1768}1769const String path = selected->get_text(0);1770if (path.is_empty() || !FileAccess::exists(path)) {1771return;1772}1773FileSystemDock::get_singleton()->navigate_to_path(path);1774}17751776void ScriptEditorDebugger::_clear_errors_list() {1777error_tree->clear();1778error_count = 0;1779warning_count = 0;1780emit_signal(SNAME("errors_cleared"));1781update_tabs();17821783expand_all_button->set_disabled(true);1784collapse_all_button->set_disabled(true);1785clear_button->set_disabled(true);1786}17871788void ScriptEditorDebugger::_breakpoints_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {1789if (p_button != MouseButton::RIGHT) {1790return;1791}17921793breakpoints_menu->clear();1794breakpoints_menu->set_size(Size2(1, 1));17951796const TreeItem *selected = breakpoints_tree->get_selected();1797String file = selected->get_text(0);1798if (selected->has_meta("line")) {1799breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete Breakpoint"), ACTION_DELETE_BREAKPOINT);1800file = selected->get_parent()->get_text(0);1801}1802breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete All Breakpoints in:") + " " + file, ACTION_DELETE_BREAKPOINTS_IN_FILE);1803breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete All Breakpoints"), ACTION_DELETE_ALL_BREAKPOINTS);18041805breakpoints_menu->set_position(get_screen_position() + get_local_mouse_position());1806breakpoints_menu->popup();1807}18081809// Right click on specific file(s) or folder(s).1810void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {1811if (p_button != MouseButton::RIGHT) {1812return;1813}18141815item_menu->clear();1816item_menu->reset_size();18171818if (error_tree->is_anything_selected()) {1819item_menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCopy")), TTR("Copy Error"), ACTION_COPY_ERROR);1820item_menu->add_icon_item(get_editor_theme_icon(SNAME("ExternalLink")), TTR("Open C++ Source on GitHub"), ACTION_OPEN_SOURCE);1821}18221823if (item_menu->get_item_count() > 0) {1824item_menu->set_position(error_tree->get_screen_position() + p_pos);1825item_menu->popup();1826}1827}18281829void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {1830switch (p_option) {1831case ACTION_COPY_ERROR: {1832TreeItem *ti = error_tree->get_selected();1833while (ti->get_parent() != error_tree->get_root()) {1834ti = ti->get_parent();1835}18361837String type;18381839if (ti->has_meta("_is_warning")) {1840type = "W ";1841} else if (ti->has_meta("_is_error")) {1842type = "E ";1843}18441845String text = ti->get_text(0) + " ";1846int rpad_len = text.length();18471848text = type + text + ti->get_text(1) + "\n";1849TreeItem *ci = ti->get_first_child();1850while (ci) {1851text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n";1852ci = ci->get_next();1853}18541855DisplayServer::get_singleton()->clipboard_set(text);1856} break;18571858case ACTION_OPEN_SOURCE: {1859TreeItem *ti = error_tree->get_selected();1860while (ti->get_parent() != error_tree->get_root()) {1861ti = ti->get_parent();1862}18631864// Find the child with the "C++ Source".1865// It's not at a fixed position as "C++ Error" may come first.1866TreeItem *ci = ti->get_first_child();1867const String cpp_source = "<" + TTR("C++ Source") + ">";1868while (ci) {1869if (ci->get_text(0) == cpp_source) {1870break;1871}1872ci = ci->get_next();1873}18741875if (!ci) {1876WARN_PRINT_ED("No C++ source reference is available for this error.");1877return;1878}18791880// Parse back the `file:line @ method()` string.1881const Vector<String> file_line_number = ci->get_text(1).split("@")[0].strip_edges().split(":");1882ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report).");1883const String &file = file_line_number[0];1884const int line_number = file_line_number[1].to_int();18851886// Construct a GitHub repository URL and open it in the user's default web browser.1887// If the commit hash is available, use it for greater accuracy. Otherwise fall back to tagged release.1888String git_ref = String(GODOT_VERSION_HASH).is_empty() ? String(GODOT_VERSION_NUMBER) + "-stable" : String(GODOT_VERSION_HASH);1889OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d",1890git_ref, file, line_number));1891} break;1892case ACTION_DELETE_BREAKPOINT: {1893const TreeItem *selected = breakpoints_tree->get_selected();1894_set_breakpoint(selected->get_parent()->get_text(0), selected->get_meta("line"), false);1895} break;1896case ACTION_DELETE_BREAKPOINTS_IN_FILE: {1897TreeItem *file_item = breakpoints_tree->get_selected();1898if (file_item->has_meta("line")) {1899file_item = file_item->get_parent();1900}19011902// Store first else we will be removing as we loop.1903List<int> lines;1904for (TreeItem *breakpoint_item = file_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {1905lines.push_back(breakpoint_item->get_meta("line"));1906}19071908for (const int &line : lines) {1909_set_breakpoint(file_item->get_text(0), line, false);1910}1911} break;1912case ACTION_DELETE_ALL_BREAKPOINTS: {1913_clear_breakpoints();1914} break;1915}1916}19171918void ScriptEditorDebugger::_tab_changed(int p_tab) {1919if (tabs->get_tab_title(p_tab) == TTR("Video RAM")) {1920// "Video RAM" tab was clicked, refresh the data it's displaying when entering the tab.1921_video_mem_request();1922}1923}19241925void ScriptEditorDebugger::_bind_methods() {1926ClassDB::bind_method(D_METHOD("live_debug_create_node"), &ScriptEditorDebugger::live_debug_create_node);1927ClassDB::bind_method(D_METHOD("live_debug_instantiate_node"), &ScriptEditorDebugger::live_debug_instantiate_node);1928ClassDB::bind_method(D_METHOD("live_debug_remove_node"), &ScriptEditorDebugger::live_debug_remove_node);1929ClassDB::bind_method(D_METHOD("live_debug_remove_and_keep_node"), &ScriptEditorDebugger::live_debug_remove_and_keep_node);1930ClassDB::bind_method(D_METHOD("live_debug_restore_node"), &ScriptEditorDebugger::live_debug_restore_node);1931ClassDB::bind_method(D_METHOD("live_debug_duplicate_node"), &ScriptEditorDebugger::live_debug_duplicate_node);1932ClassDB::bind_method(D_METHOD("live_debug_reparent_node"), &ScriptEditorDebugger::live_debug_reparent_node);1933ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value", "field"), &ScriptEditorDebugger::update_remote_object);19341935ADD_SIGNAL(MethodInfo("started"));1936ADD_SIGNAL(MethodInfo("stopped"));1937ADD_SIGNAL(MethodInfo("stop_requested"));1938ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));1939ADD_SIGNAL(MethodInfo("error_selected", PropertyInfo(Variant::INT, "error")));1940ADD_SIGNAL(MethodInfo("breakpoint_selected", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));1941ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));1942ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));1943ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"), PropertyInfo(Variant::STRING, "reason"), PropertyInfo(Variant::BOOL, "has_stackdump")));1944ADD_SIGNAL(MethodInfo("remote_objects_requested", PropertyInfo(Variant::ARRAY, "ids")));1945ADD_SIGNAL(MethodInfo("remote_objects_updated", PropertyInfo(Variant::OBJECT, "remote_objects")));1946ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));1947ADD_SIGNAL(MethodInfo("remote_window_title_changed", PropertyInfo(Variant::STRING, "title")));1948ADD_SIGNAL(MethodInfo("remote_tree_updated"));1949ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::ARRAY, "ids")));1950ADD_SIGNAL(MethodInfo("remote_tree_clear_selection_requested"));1951ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level")));1952ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump")));1953ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars")));1954ADD_SIGNAL(MethodInfo("stack_frame_var", PropertyInfo(Variant::ARRAY, "data")));1955ADD_SIGNAL(MethodInfo("debug_data", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::ARRAY, "data")));1956ADD_SIGNAL(MethodInfo("set_breakpoint", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));1957ADD_SIGNAL(MethodInfo("clear_breakpoints"));1958ADD_SIGNAL(MethodInfo("errors_cleared"));1959ADD_SIGNAL(MethodInfo("embed_shortcut_requested", PropertyInfo(Variant::INT, "embed_shortcut_action")));1960}19611962void ScriptEditorDebugger::add_debugger_tab(Control *p_control) {1963tabs->add_child(p_control);1964}19651966void ScriptEditorDebugger::remove_debugger_tab(Control *p_control) {1967int idx = tabs->get_tab_idx_from_control(p_control);1968ERR_FAIL_COND(idx < 0);1969p_control->queue_free();1970}19711972int ScriptEditorDebugger::get_current_debugger_tab() const {1973return tabs->get_current_tab();1974}19751976void ScriptEditorDebugger::switch_to_debugger(int p_debugger_tab_idx) {1977tabs->set_current_tab(p_debugger_tab_idx);1978}19791980void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) {1981_put_msg(p_message, p_args);1982}19831984void ScriptEditorDebugger::toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data) {1985Array msg_data = { p_enable, p_data };1986_put_msg("profiler:" + p_profiler, msg_data);1987}19881989ScriptEditorDebugger::ScriptEditorDebugger() {1990if (unlikely(parse_message_handlers.is_empty())) {1991_init_parse_message_handlers();1992}19931994tabs = memnew(TabContainer);1995add_child(tabs);1996tabs->connect("tab_changed", callable_mp(this, &ScriptEditorDebugger::_tab_changed));19971998InspectorDock::get_inspector_singleton()->connect("object_id_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));1999EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &ScriptEditorDebugger::_resources_reimported));20002001{ //debugger2002VBoxContainer *vbc = memnew(VBoxContainer);2003vbc->set_name(TTR("Stack Trace"));2004Control *dbg = vbc;20052006HBoxContainer *hbc = memnew(HBoxContainer);2007vbc->add_child(hbc);20082009reason = memnew(RichTextLabel);2010reason->set_focus_mode(FOCUS_ACCESSIBILITY);2011reason->set_selection_enabled(true);2012reason->set_context_menu_enabled(true);2013reason->set_h_size_flags(SIZE_EXPAND_FILL);2014reason->set_v_size_flags(SIZE_SHRINK_CENTER);2015reason->connect(SceneStringName(resized), callable_mp(this, &ScriptEditorDebugger::_update_reason_content_height));2016hbc->add_child(reason);20172018hbc->add_child(memnew(VSeparator));20192020skip_breakpoints = memnew(Button);2021skip_breakpoints->set_theme_type_variation(SceneStringName(FlatButton));2022hbc->add_child(skip_breakpoints);2023skip_breakpoints->set_tooltip_text(TTR("Skip Breakpoints"));2024skip_breakpoints->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints));20252026ignore_error_breaks = memnew(Button);2027ignore_error_breaks->set_theme_type_variation(SceneStringName(FlatButton));2028ignore_error_breaks->set_tooltip_text(TTR("Ignore Error Breaks"));2029hbc->add_child(ignore_error_breaks);2030ignore_error_breaks->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_ignore_error_breaks));20312032hbc->add_child(memnew(VSeparator));20332034copy = memnew(Button);2035copy->set_theme_type_variation(SceneStringName(FlatButton));2036hbc->add_child(copy);2037copy->set_tooltip_text(TTR("Copy Error"));2038copy->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_copy));20392040hbc->add_child(memnew(VSeparator));20412042step = memnew(Button);2043step->set_theme_type_variation(SceneStringName(FlatButton));2044hbc->add_child(step);2045step->set_tooltip_text(TTR("Step Into"));2046step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into"));2047step->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_step));20482049next = memnew(Button);2050next->set_theme_type_variation(SceneStringName(FlatButton));2051hbc->add_child(next);2052next->set_tooltip_text(TTR("Step Over"));2053next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over"));2054next->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_next));20552056hbc->add_child(memnew(VSeparator));20572058dobreak = memnew(Button);2059dobreak->set_theme_type_variation(SceneStringName(FlatButton));2060hbc->add_child(dobreak);2061dobreak->set_tooltip_text(TTR("Break"));2062dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break"));2063dobreak->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_break));20642065docontinue = memnew(Button);2066docontinue->set_theme_type_variation(SceneStringName(FlatButton));2067hbc->add_child(docontinue);2068docontinue->set_tooltip_text(TTR("Continue"));2069docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));2070docontinue->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_continue));20712072HSplitContainer *parent_sc = memnew(HSplitContainer);2073vbc->add_child(parent_sc);2074parent_sc->set_v_size_flags(SIZE_EXPAND_FILL);2075parent_sc->set_split_offset(500 * EDSCALE);20762077HSplitContainer *sc = memnew(HSplitContainer);2078sc->set_v_size_flags(SIZE_EXPAND_FILL);2079sc->set_h_size_flags(SIZE_EXPAND_FILL);2080parent_sc->add_child(sc);20812082VBoxContainer *stack_vb = memnew(VBoxContainer);2083stack_vb->set_h_size_flags(SIZE_EXPAND_FILL);2084sc->add_child(stack_vb);2085HBoxContainer *thread_hb = memnew(HBoxContainer);2086stack_vb->add_child(thread_hb);2087thread_hb->add_child(memnew(Label(TTR("Thread:"))));2088threads = memnew(OptionButton);2089thread_hb->add_child(threads);2090threads->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);2091threads->set_h_size_flags(SIZE_EXPAND_FILL);2092threads->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_select_thread));20932094stack_dump = memnew(Tree);2095stack_dump->set_custom_minimum_size(Size2(150, 0) * EDSCALE);2096stack_dump->set_allow_reselect(true);2097stack_dump->set_columns(1);2098stack_dump->set_column_titles_visible(true);2099stack_dump->set_column_title(0, TTR("Stack Frames"));2100stack_dump->set_hide_root(true);2101stack_dump->set_v_size_flags(SIZE_EXPAND_FILL);2102stack_dump->set_theme_type_variation("TreeSecondary");2103stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected));2104stack_vb->add_child(stack_dump);21052106VBoxContainer *inspector_vbox = memnew(VBoxContainer);2107inspector_vbox->set_custom_minimum_size(Size2(200, 0) * EDSCALE);2108inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL);2109sc->add_child(inspector_vbox);21102111HBoxContainer *tools_hb = memnew(HBoxContainer);2112inspector_vbox->add_child(tools_hb);21132114search = memnew(LineEdit);2115search->set_h_size_flags(Control::SIZE_EXPAND_FILL);2116search->set_placeholder(TTR("Filter Stack Variables"));2117search->set_accessibility_name(TTRC("Filter Stack Variables"));2118search->set_clear_button_enabled(true);2119tools_hb->add_child(search);21202121inspector = memnew(EditorDebuggerInspector);2122inspector->set_h_size_flags(SIZE_EXPAND_FILL);2123inspector->set_v_size_flags(SIZE_EXPAND_FILL);2124inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);2125inspector->set_read_only(true);2126inspector->connect("object_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));2127inspector->connect("objects_edited", callable_mp(this, &ScriptEditorDebugger::_remote_objects_edited));2128inspector->connect("object_property_updated", callable_mp(this, &ScriptEditorDebugger::_remote_object_property_updated));2129inspector->register_text_enter(search);2130inspector->set_use_filter(true);2131inspector_vbox->add_child(inspector);21322133breakpoints_tree = memnew(Tree);2134breakpoints_tree->set_custom_minimum_size(Size2(100, 0) * EDSCALE);2135breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL);2136breakpoints_tree->set_column_titles_visible(true);2137breakpoints_tree->set_column_title(0, TTR("Breakpoints"));2138breakpoints_tree->set_allow_reselect(true);2139breakpoints_tree->set_allow_rmb_select(true);2140breakpoints_tree->set_hide_root(true);2141breakpoints_tree->set_theme_type_variation("TreeSecondary");2142breakpoints_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected));2143breakpoints_tree->create_item();21442145parent_sc->add_child(breakpoints_tree);2146tabs->add_child(dbg);21472148breakpoints_menu = memnew(PopupMenu);2149breakpoints_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed));2150breakpoints_tree->add_child(breakpoints_menu);2151}21522153{ //errors2154errors_tab = memnew(VBoxContainer);2155errors_tab->set_name(TTR("Errors"));21562157HBoxContainer *error_hbox = memnew(HBoxContainer);2158errors_tab->add_child(error_hbox);21592160expand_all_button = memnew(Button);2161expand_all_button->set_text(TTR("Expand All"));2162expand_all_button->set_disabled(true);2163expand_all_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_expand_errors_list));2164error_hbox->add_child(expand_all_button);21652166collapse_all_button = memnew(Button);2167collapse_all_button->set_text(TTR("Collapse All"));2168collapse_all_button->set_disabled(true);2169collapse_all_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_collapse_errors_list));2170error_hbox->add_child(collapse_all_button);21712172Control *space = memnew(Control);2173space->set_h_size_flags(SIZE_EXPAND_FILL);2174error_hbox->add_child(space);21752176clear_button = memnew(Button);2177clear_button->set_text(TTR("Clear"));2178clear_button->set_h_size_flags(0);2179clear_button->set_disabled(true);2180clear_button->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_clear_errors_list));2181error_hbox->add_child(clear_button);21822183error_tree = memnew(Tree);2184error_tree->set_columns(2);21852186error_tree->set_column_expand(0, false);2187error_tree->set_column_custom_minimum_width(0, 140);2188error_tree->set_column_clip_content(0, true);21892190error_tree->set_column_expand(1, true);2191error_tree->set_column_clip_content(1, true);21922193error_tree->set_select_mode(Tree::SELECT_ROW);2194error_tree->set_hide_root(true);2195error_tree->set_v_size_flags(SIZE_EXPAND_FILL);2196error_tree->set_allow_rmb_select(true);2197error_tree->set_allow_reselect(true);2198error_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_error_tree_item_rmb_selected));2199errors_tab->add_child(error_tree);22002201item_menu = memnew(PopupMenu);2202item_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed));2203error_tree->add_child(item_menu);22042205tabs->add_child(errors_tab);2206}22072208{ // File dialog2209file_dialog = memnew(EditorFileDialog);2210file_dialog->connect("file_selected", callable_mp(this, &ScriptEditorDebugger::_file_selected));2211add_child(file_dialog);2212}22132214{ // Expression evaluator2215expression_evaluator = memnew(EditorExpressionEvaluator);2216expression_evaluator->set_name(TTR("Evaluator"));2217expression_evaluator->set_editor_debugger(this);2218tabs->add_child(expression_evaluator);2219}22202221{ //profiler2222profiler = memnew(EditorProfiler);2223profiler->set_name(TTR("Profiler"));2224tabs->add_child(profiler);2225profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_SCRIPTS_SERVERS));2226profiler->connect("break_request", callable_mp(this, &ScriptEditorDebugger::_profiler_seeked));2227}22282229{ //frame profiler2230visual_profiler = memnew(EditorVisualProfiler);2231visual_profiler->set_name(TTR("Visual Profiler"));2232tabs->add_child(visual_profiler);2233visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate).bind(PROFILER_VISUAL));2234}22352236{ //monitors2237performance_profiler = memnew(EditorPerformanceProfiler);2238tabs->add_child(performance_profiler);2239}22402241{ //vmem inspect2242VBoxContainer *vmem_vb = memnew(VBoxContainer);2243HBoxContainer *vmem_hb = memnew(HBoxContainer);22442245Label *vmlb = memnew(Label(TTRC("List of Video Memory Usage by Resource:")));2246vmlb->set_theme_type_variation("HeaderSmall");2247vmem_hb->add_child(vmlb);22482249{ // Add notice icon.2250vmem_notice_icon = memnew(TextureRect);2251vmem_notice_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);2252vmem_notice_icon->set_h_size_flags(SIZE_SHRINK_CENTER);2253vmem_notice_icon->set_visible(true);2254vmem_notice_icon->set_tooltip_text(TTR(R"(Notice:2255This tool only reports memory allocations tracked by the engine.2256Therefore, total VRAM usage is inaccurate compared to what the Monitors tab or external tools can report.2257Instead, use the monitors tab to obtain more precise VRAM usage.22582259- Buffer Memory (e.g. GPUParticles) is not tracked.2260- Meshes are not tracked in the Compatibility renderer.)"));2261vmem_hb->add_child(vmem_notice_icon);2262}22632264{ // Add some space to move the rest of the controls to the right.2265Control *space = memnew(Control);2266space->set_h_size_flags(SIZE_EXPAND_FILL);2267vmem_hb->add_child(space);2268}22692270vmem_hb->add_child(memnew(Label(TTR("Total:") + " ")));2271vmem_total = memnew(LineEdit);2272vmem_total->set_editable(false);2273vmem_total->set_accessibility_name(TTRC("Video RAM Total"));2274vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE);2275vmem_hb->add_child(vmem_total);2276vmem_refresh = memnew(Button);2277vmem_refresh->set_accessibility_name(TTRC("Refresh Video RAM"));2278vmem_refresh->set_theme_type_variation(SceneStringName(FlatButton));2279vmem_hb->add_child(vmem_refresh);2280vmem_export = memnew(Button);2281vmem_export->set_theme_type_variation(SceneStringName(FlatButton));2282vmem_export->set_tooltip_text(TTR("Export list to a CSV file"));2283vmem_hb->add_child(vmem_export);2284vmem_vb->add_child(vmem_hb);2285vmem_refresh->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_video_mem_request));2286vmem_export->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_video_mem_export));22872288VBoxContainer *vmmc = memnew(VBoxContainer);2289vmem_tree = memnew(Tree);2290vmem_tree->set_v_size_flags(SIZE_EXPAND_FILL);2291vmem_tree->set_h_size_flags(SIZE_EXPAND_FILL);2292vmmc->add_child(vmem_tree);2293vmmc->set_v_size_flags(SIZE_EXPAND_FILL);2294vmem_vb->add_child(vmmc);22952296vmem_vb->set_name(TTR("Video RAM"));2297vmem_tree->set_columns(4);2298vmem_tree->set_column_titles_visible(true);2299vmem_tree->set_column_title(0, TTR("Resource Path"));2300vmem_tree->set_column_expand(0, true);2301vmem_tree->set_column_expand(1, false);2302vmem_tree->set_column_title(1, TTR("Type"));2303vmem_tree->set_column_custom_minimum_width(1, 100 * EDSCALE);2304vmem_tree->set_column_expand(2, false);2305vmem_tree->set_column_title(2, TTR("Format"));2306vmem_tree->set_column_custom_minimum_width(2, 150 * EDSCALE);2307vmem_tree->set_column_expand(3, false);2308vmem_tree->set_column_title(3, TTR("Usage"));2309vmem_tree->set_column_custom_minimum_width(3, 80 * EDSCALE);2310vmem_tree->set_hide_root(true);2311vmem_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_vmem_item_activated));23122313tabs->add_child(vmem_vb);2314}23152316{ // misc2317VBoxContainer *misc = memnew(VBoxContainer);2318misc->set_name(TTR("Misc"));2319tabs->add_child(misc);23202321GridContainer *info_left = memnew(GridContainer);2322info_left->set_columns(2);2323misc->add_child(info_left);2324clicked_ctrl = memnew(LineEdit);2325clicked_ctrl->set_editable(false);2326clicked_ctrl->set_accessibility_name(TTRC("Clicked Control:"));2327clicked_ctrl->set_h_size_flags(SIZE_EXPAND_FILL);2328info_left->add_child(memnew(Label(TTR("Clicked Control:"))));2329info_left->add_child(clicked_ctrl);2330clicked_ctrl_type = memnew(LineEdit);2331clicked_ctrl_type->set_editable(false);2332clicked_ctrl_type->set_accessibility_name(TTRC("Clicked Control Type:"));2333info_left->add_child(memnew(Label(TTR("Clicked Control Type:"))));2334info_left->add_child(clicked_ctrl_type);23352336scene_tree = memnew(SceneDebuggerTree);2337live_edit_root = memnew(LineEdit);2338live_edit_root->set_editable(false);2339live_edit_root->set_h_size_flags(SIZE_EXPAND_FILL);2340live_edit_root->set_accessibility_name(TTRC("Live Edit Root:"));23412342{2343HBoxContainer *lehb = memnew(HBoxContainer);2344Label *l = memnew(Label(TTR("Live Edit Root:")));2345info_left->add_child(l);2346lehb->add_child(live_edit_root);2347le_set = memnew(Button(TTR("Set From Tree")));2348lehb->add_child(le_set);2349le_clear = memnew(Button(TTR("Clear")));2350lehb->add_child(le_clear);2351info_left->add_child(lehb);2352}23532354misc->add_child(memnew(VSeparator));23552356HBoxContainer *buttons = memnew(HBoxContainer);23572358export_csv = memnew(Button(TTR("Export measures as CSV")));2359export_csv->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::_export_csv));2360buttons->add_child(export_csv);23612362misc->add_child(buttons);2363}23642365msgdialog = memnew(AcceptDialog);2366add_child(msgdialog);23672368camera_override = CameraOverride::OVERRIDE_NONE;2369error_count = 0;2370warning_count = 0;2371_update_buttons_state();2372}23732374ScriptEditorDebugger::~ScriptEditorDebugger() {2375if (peer.is_valid()) {2376peer->close();2377peer.unref();2378}2379memdelete(scene_tree);2380}238123822383