/*************************************************************************/ /* script_debugger_remote.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* 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_debugger_remote.h" #include "core/io/marshalls.h" #include "globals.h" #include "io/ip.h" #include "os/input.h" #include "os/os.h" void ScriptDebuggerRemote::_send_video_memory() { List usage; if (resource_usage_func) resource_usage_func(&usage); usage.sort(); packet_peer_stream->put_var("message:video_mem"); packet_peer_stream->put_var(usage.size() * 4); for (List::Element *E = usage.front(); E; E = E->next()) { packet_peer_stream->put_var(E->get().path); packet_peer_stream->put_var(E->get().type); packet_peer_stream->put_var(E->get().format); packet_peer_stream->put_var(E->get().vram); } } Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) { IP_Address ip; if (p_host.is_valid_ip_address()) ip = p_host; else ip = IP::get_singleton()->resolve_hostname(p_host); int port = p_port; int tries = 3; tcp_client->connect(ip, port); while (tries--) { if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) { break; } else { OS::get_singleton()->delay_usec(1000000); print_line("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in 1 sec."); }; }; if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { print_line("Remote Debugger: Unable to connect"); return FAILED; }; // print_line("Remote Debugger: Connection OK!"); packet_peer_stream->set_stream_peer(tcp_client); return OK; } static int _ScriptDebuggerRemote_found_id = 0; static Object *_ScriptDebuggerRemote_find = NULL; static void _ScriptDebuggerRemote_debug_func(Object *p_obj) { if (_ScriptDebuggerRemote_find == p_obj) { _ScriptDebuggerRemote_found_id = p_obj->get_instance_ID(); } } static ObjectID safe_get_instance_id(const Variant &p_v) { Object *o = p_v; if (o == NULL) return 0; else { REF r = p_v; if (r.is_valid()) { return r->get_instance_ID(); } else { _ScriptDebuggerRemote_found_id = 0; _ScriptDebuggerRemote_find = NULL; ObjectDB::debug_objects(_ScriptDebuggerRemote_debug_func); return _ScriptDebuggerRemote_found_id; } } } void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue) { //this function is called when there is a debugger break (bug on script) //or when execution is paused from editor if (!tcp_client->is_connected()) { ERR_EXPLAIN("Script Debugger failed to connect, but being used anyway."); ERR_FAIL(); } OS::get_singleton()->enable_for_stealing_focus(Globals::get_singleton()->get("editor_pid")); packet_peer_stream->put_var("debug_enter"); packet_peer_stream->put_var(2); packet_peer_stream->put_var(p_can_continue); packet_peer_stream->put_var(p_script->debug_get_error()); skip_profile_frame = true; // to avoid super long frame time for the frame Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode(); if (mouse_mode != Input::MOUSE_MODE_VISIBLE) Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); while (true) { _get_output(); if (packet_peer_stream->get_available_packet_count() > 0) { Variant var; Error err = packet_peer_stream->get_var(var); ERR_CONTINUE(err != OK); ERR_CONTINUE(var.get_type() != Variant::ARRAY); Array cmd = var; ERR_CONTINUE(cmd.size() == 0); ERR_CONTINUE(cmd[0].get_type() != Variant::STRING); String command = cmd[0]; if (command == "get_stack_dump") { packet_peer_stream->put_var("stack_dump"); int slc = p_script->debug_get_stack_level_count(); packet_peer_stream->put_var(slc); for (int i = 0; i < slc; i++) { Dictionary d; d["file"] = p_script->debug_get_stack_level_source(i); d["line"] = p_script->debug_get_stack_level_line(i); d["function"] = p_script->debug_get_stack_level_function(i); //d["id"]=p_script->debug_get_stack_level_ d["id"] = 0; packet_peer_stream->put_var(d); } } else if (command == "get_stack_frame_vars") { cmd.remove(0); ERR_CONTINUE(cmd.size() != 1); int lv = cmd[0]; ScriptInstance *self_instance = p_script->debug_get_stack_level_instance(lv); List members; List member_vals; p_script->debug_get_stack_level_members(lv, &members, &member_vals); ERR_CONTINUE(members.size() != member_vals.size()); List locals; List local_vals; p_script->debug_get_stack_level_locals(lv, &locals, &local_vals); ERR_CONTINUE(locals.size() != local_vals.size()); List globals; List global_vals; p_script->debug_get_globals(&globals, &global_vals); ERR_CONTINUE(globals.size() != global_vals.size()); packet_peer_stream->put_var("stack_frame_vars"); packet_peer_stream->put_var(locals.size() + members.size() + globals.size()); { //locals List::Element *E = locals.front(); List::Element *F = local_vals.front(); while (E) { PropertyInfo pi(var.get_type(), String("locals/") + E->get()); packet_peer_stream->put_var(_serialize(F->get(), pi)); E = E->next(); F = F->next(); } } { //members if (self_instance) { // self members.push_front("self"); member_vals.push_front(self_instance->get_owner()); } List::Element *E = members.front(); List::Element *F = member_vals.front(); while (E) { PropertyInfo pi(var.get_type(), String("members/") + E->get()); packet_peer_stream->put_var(_serialize(F->get(), pi)); E = E->next(); F = F->next(); } } if (self_instance) { // constants Ref