From 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sun, 9 Feb 2014 22:10:30 -0300 Subject: GODOT IS OPEN SOURCE --- tools/editor/script_editor_debugger.cpp | 884 ++++++++++++++++++++++++++++++++ 1 file changed, 884 insertions(+) create mode 100644 tools/editor/script_editor_debugger.cpp (limited to 'tools/editor/script_editor_debugger.cpp') diff --git a/tools/editor/script_editor_debugger.cpp b/tools/editor/script_editor_debugger.cpp new file mode 100644 index 000000000..c66328cf5 --- /dev/null +++ b/tools/editor/script_editor_debugger.cpp @@ -0,0 +1,884 @@ +/*************************************************************************/ +/* script_editor_debugger.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "script_editor_debugger.h" +#include "scene/gui/separator.h" +#include "scene/gui/label.h" +#include "scene/gui/split_container.h" +#include "scene/gui/tree.h" +#include "scene/gui/texture_button.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/rich_text_label.h" +#include "scene/gui/margin_container.h" +#include "property_editor.h" +#include "globals.h" +#include "editor_node.h" +#include "main/performance.h" + +class ScriptEditorDebuggerVariables : public Object { + + OBJ_TYPE( ScriptEditorDebuggerVariables, Object ); + + List props; + Map values; +protected: + + bool _set(const StringName& p_name, const Variant& p_value) { + + return false; + } + + bool _get(const StringName& p_name,Variant &r_ret) const { + + if (!values.has(p_name)) + return false; + r_ret=values[p_name]; + return true; + } + void _get_property_list( List *p_list) const { + + for(const List::Element *E=props.front();E;E=E->next() ) + p_list->push_back(E->get()); + } + + +public: + + + void clear() { + + props.clear(); + values.clear(); + } + + String get_var_value(const String& p_var) const { + + for(Map::Element *E=values.front();E;E=E->next()) { + String v = E->key().operator String().get_slice("/",1); + if (v==p_var) + return E->get(); + } + + return ""; + } + + void add_property(const String &p_name, const Variant& p_value) { + + PropertyInfo pinfo; + pinfo.name=p_name; + pinfo.type=p_value.get_type(); + props.push_back(pinfo); + values[p_name]=p_value; + + } + + void update() { + _change_notify(); + } + + + ScriptEditorDebuggerVariables() { + + } +}; + +void ScriptEditorDebugger::debug_next() { + + ERR_FAIL_COND(!breaked); + ERR_FAIL_COND(connection.is_null()); + ERR_FAIL_COND(!connection->is_connected()); + Array msg; + msg.push_back("next"); + ppeer->put_var(msg); + stack_dump->clear(); + inspector->edit(NULL); + +} +void ScriptEditorDebugger::debug_step() { + + ERR_FAIL_COND(!breaked); + ERR_FAIL_COND(connection.is_null()); + ERR_FAIL_COND(!connection->is_connected()); + + Array msg; + msg.push_back("step"); + ppeer->put_var(msg); + stack_dump->clear(); + inspector->edit(NULL); +} + +void ScriptEditorDebugger::debug_break() { + + ERR_FAIL_COND(breaked); + ERR_FAIL_COND(connection.is_null()); + ERR_FAIL_COND(!connection->is_connected()); + + Array msg; + msg.push_back("break"); + ppeer->put_var(msg); + +} + +void ScriptEditorDebugger::debug_continue() { + + ERR_FAIL_COND(!breaked); + ERR_FAIL_COND(connection.is_null()); + ERR_FAIL_COND(!connection->is_connected()); + + Array msg; + msg.push_back("continue"); + ppeer->put_var(msg); + +} + +void ScriptEditorDebugger::_scene_tree_request() { + + ERR_FAIL_COND(connection.is_null()); + ERR_FAIL_COND(!connection->is_connected()); + + Array msg; + msg.push_back("request_scene_tree"); + ppeer->put_var(msg); + +} + +Size2 ScriptEditorDebugger::get_minimum_size() const { + + Size2 ms = Control::get_minimum_size(); + ms.y = MAX(ms.y , 250 ); + return ms; + +} +void ScriptEditorDebugger::_parse_message(const String& p_msg,const Array& p_data) { + + + + if (p_msg=="debug_enter") { + + Array msg; + msg.push_back("get_stack_dump"); + ppeer->put_var(msg); + ERR_FAIL_COND(p_data.size()!=2); + bool can_continue=p_data[0]; + String error = p_data[1]; + step->set_disabled(!can_continue); + next->set_disabled(!can_continue); + reason->set_text(error); + reason->set_tooltip(error); + breaked=true; + dobreak->set_disabled(true); + docontinue->set_disabled(false); + emit_signal("breaked",true,can_continue); + OS::get_singleton()->move_window_to_foreground(); + tabs->set_current_tab(0); + + } else if (p_msg=="debug_exit") { + + breaked=false; + step->set_disabled(true); + next->set_disabled(true); + reason->set_text(""); + reason->set_tooltip(""); + back->set_disabled(true); + forward->set_disabled(true); + dobreak->set_disabled(false); + docontinue->set_disabled(true); + emit_signal("breaked",false,false); + //tabs->set_current_tab(0); + + } else if (p_msg=="message:click_ctrl") { + + clicked_ctrl->set_text(p_data[0]); + clicked_ctrl_type->set_text(p_data[1]); + + } else if (p_msg=="message:scene_tree") { + + scene_tree->clear(); + Map lv; + + for(int i=0;icreate_item(p); + it->set_text(0,p_data[i+1]); + if (has_icon(p_data[i+2],"EditorIcons")) + it->set_icon(0,get_icon(p_data[i+2],"EditorIcons")); + lv[level]=it; + } + + + } else if (p_msg=="stack_dump") { + + stack_dump->clear(); + TreeItem *r = stack_dump->create_item(); + + for(int i=0;icreate_item(r); + d["frame"]=i; + s->set_metadata(0,d); + +// String line = itos(i)+" - "+String(d["file"])+":"+itos(d["line"])+" - at func: "+d["function"]; + String line = itos(i)+" - "+String(d["file"])+":"+itos(d["line"]); + s->set_text(0,line); + + if (i==0) + s->select(0); + } + } else if (p_msg=="stack_frame_vars") { + + + variables->clear(); + + + + int ofs =0; + int mcount = p_data[ofs]; + + ofs++; + for(int i=0;iadd_property("members/"+n,v); + } + ofs+=mcount*2; + + mcount = p_data[ofs]; + + ofs++; + for(int i=0;iadd_property("locals/"+n,v); + } + + variables->update(); + inspector->edit(variables); + + } else if (p_msg=="output") { + + //OUT + for(int i=0;iis_hidden()) { + log_forced_visible=true; + EditorNode::get_log()->show(); + } + EditorNode::get_log()->add_message(t); + + } + + } else if (p_msg=="performance") { + Array arr = p_data[0]; + Vector p; + p.resize(arr.size()); + for(int i=0;iset_text(1,rtos(p[i])); + if (p[i]>perf_max[i]) + perf_max[i]=p[i]; + } + + } + perf_history.push_front(p); + perf_draw->update(); + + } else if (p_msg=="kill_me") { + + editor->call_deferred("stop_child_process"); + } + +} + + +void ScriptEditorDebugger::_performance_select(Object*,int,bool) { + + perf_draw->update(); + +} + +void ScriptEditorDebugger::_performance_draw() { + + + Vector which; + for(int i=0;iis_selected(0)) + which.push_back(i); + } + + + if(which.empty()) + return; + + Color graph_color=get_color("font_color","TextEdit"); + Ref graph_sb = get_stylebox("normal","TextEdit"); + Ref graph_font = get_font("font","TextEdit"); + + int cols = Math::ceil(Math::sqrt(which.size())); + int rows = (which.size()+1)/cols; + if (which.size()==1) + rows=1; + + + int margin =3; + int point_sep=5; + Size2i s = Size2i(perf_draw->get_size())/Size2i(cols,rows); + for(int i=0;idraw_style_box(graph_sb,r); + r.pos+=graph_sb->get_offset(); + r.size-=graph_sb->get_minimum_size(); + int pi=which[i]; + Color c = Color(0.7,0.9,0.5); + c.set_hsv(Math::fmod(c.get_h()+pi*0.7654,1),c.get_s(),c.get_v()); + + c.a=0.8; + perf_draw->draw_string(graph_font,r.pos+Point2(0,graph_font->get_ascent()),perf_items[pi]->get_text(0),c,r.size.x); + c.a=0.6; + perf_draw->draw_string(graph_font,r.pos+Point2(graph_font->get_char_size('X').width,graph_font->get_ascent()+graph_font->get_height()),perf_items[pi]->get_text(1),c,r.size.y); + + float spacing=point_sep/float(cols); + float from = r.size.width; + + List >::Element *E=perf_history.front(); + float prev=-1; + while(from>=0 && E) { + + float m = perf_max[pi]; + if (m==0) + m=0.00001; + float h = E->get()[pi]/m; + h=(1.0-h)*r.size.y; + + c.a=0.7; + if (E!=perf_history.front()) + perf_draw->draw_line(r.pos+Point2(from,h),r.pos+Point2(from+spacing,prev),c,2.0); + prev=h; + E=E->next(); + from-=spacing; + } + + } + +} + +void ScriptEditorDebugger::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + step->set_icon( get_icon("DebugStep","EditorIcons")); + next->set_icon( get_icon("DebugNext","EditorIcons")); + back->set_icon( get_icon("Back","EditorIcons")); + forward->set_icon( get_icon("Forward","EditorIcons")); + dobreak->set_icon( get_icon("Pause","EditorIcons")); + docontinue->set_icon( get_icon("DebugContinue","EditorIcons")); + tb->set_normal_texture( get_icon("Close","EditorIcons")); + tb->set_hover_texture( get_icon("CloseHover","EditorIcons")); + tb->set_pressed_texture( get_icon("Close","EditorIcons")); + scene_tree_refresh->set_icon( get_icon("Reload","EditorIcons")); + + } break; + case NOTIFICATION_PROCESS: { + + if (connection.is_null()) { + + if (server->is_connection_available()) { + + connection = server->take_connection(); + if (connection.is_null()) + break; + + EditorNode::get_log()->add_message("** Debug Process Started **"); + log_forced_visible=false; + + ppeer->set_stream_peer(connection); + + + show(); + dobreak->set_disabled(false); + tabs->set_current_tab(0); + + emit_signal("show_debugger",true); + reason->set_text("Child Process Connected"); + reason->set_tooltip("Child Process Connected"); + + } else { + + break; + } + }; + + if (!connection->is_connected()) { + stop(); + editor->notify_child_process_exited(); //somehow, exited + msgdialog->set_text("Process being debugged exited."); + msgdialog->popup_centered(Size2(250,100)); + break; + }; + + if (ppeer->get_available_packet_count() <= 0) { + break; + }; + + while(ppeer->get_available_packet_count() > 0) { + + if (pending_in_queue) { + + int todo = MIN( ppeer->get_available_packet_count(), pending_in_queue ); + + for(int i=0;iget_var(cmd); + if (ret!=OK) { + stop(); + ERR_FAIL_COND(ret!=OK); + } + + message.push_back(cmd); + pending_in_queue--; + } + + + if (pending_in_queue==0) { + _parse_message(message_type,message); + message.clear(); + + } + + + } else { + + if (ppeer->get_available_packet_count()>=2) { + + + Variant cmd; + Error ret = ppeer->get_var(cmd); + if (ret!=OK) { + stop(); + ERR_FAIL_COND(ret!=OK); + } + if (cmd.get_type()!=Variant::STRING) { + stop(); + ERR_FAIL_COND(cmd.get_type()!=Variant::STRING); + } + + message_type=cmd; + + ret = ppeer->get_var(cmd); + if (ret!=OK) { + stop(); + ERR_FAIL_COND(ret!=OK); + } + if (cmd.get_type()!=Variant::INT) { + stop(); + ERR_FAIL_COND(cmd.get_type()!=Variant::INT); + } + + pending_in_queue=cmd; + + if (pending_in_queue==0) { + _parse_message(message_type,Array()); + message.clear(); + } + + } else { + + + break; + } + + } + } + + + + } break; + } + +} + + +void ScriptEditorDebugger::start() { + + stop(); + + + uint16_t port = GLOBAL_DEF("debug/remote_port",6007); + perf_history.clear(); + for(int i=0;ilisten(port); + set_process(true); + +} + +void ScriptEditorDebugger::pause(){ + + +} + +void ScriptEditorDebugger::unpause(){ + + +} + +void ScriptEditorDebugger::stop(){ + + + set_process(false); + + server->stop(); + + ppeer->set_stream_peer(Ref()); + + if (connection.is_valid()) { + EditorNode::get_log()->add_message("** Debug Process Stopped **"); + connection.unref(); + } + + pending_in_queue=0; + message.clear(); + + if (log_forced_visible) { + EditorNode::get_log()->hide(); + log_forced_visible=false; + } + + + + hide(); + emit_signal("show_debugger",false); + +} + + +void ScriptEditorDebugger::_stack_dump_frame_selected() { + + TreeItem *ti = stack_dump->get_selected(); + if (!ti) + return; + + + Dictionary d = ti->get_metadata(0); + + Ref