2023-01-05 12:25:55 +00:00
/**************************************************************************/
/* engine_debugger.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* 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. */
/**************************************************************************/
2020-02-27 02:30:20 +00:00
# include "engine_debugger.h"
# include "core/debugger/local_debugger.h"
# include "core/debugger/remote_debugger.h"
2020-03-11 10:23:21 +00:00
# include "core/debugger/remote_debugger_peer.h"
2020-02-27 02:30:20 +00:00
# include "core/debugger/script_debugger.h"
# include "core/os/os.h"
2020-04-01 23:20:12 +00:00
EngineDebugger * EngineDebugger : : singleton = nullptr ;
ScriptDebugger * EngineDebugger : : script_debugger = nullptr ;
2020-02-27 02:30:20 +00:00
2022-05-13 13:04:37 +00:00
HashMap < StringName , EngineDebugger : : Profiler > EngineDebugger : : profilers ;
HashMap < StringName , EngineDebugger : : Capture > EngineDebugger : : captures ;
HashMap < String , EngineDebugger : : CreatePeerFunc > EngineDebugger : : protocols ;
2020-02-27 02:30:20 +00:00
2022-07-21 19:41:02 +00:00
void ( * EngineDebugger : : allow_focus_steal_fn ) ( ) ;
2020-02-27 02:30:20 +00:00
void EngineDebugger : : register_profiler ( const StringName & p_name , const Profiler & p_func ) {
ERR_FAIL_COND_MSG ( profilers . has ( p_name ) , " Profiler already registered: " + p_name ) ;
profilers . insert ( p_name , p_func ) ;
}
void EngineDebugger : : unregister_profiler ( const StringName & p_name ) {
ERR_FAIL_COND_MSG ( ! profilers . has ( p_name ) , " Profiler not registered: " + p_name ) ;
Profiler & p = profilers [ p_name ] ;
if ( p . active & & p . toggle ) {
p . toggle ( p . data , false , Array ( ) ) ;
p . active = false ;
}
profilers . erase ( p_name ) ;
}
void EngineDebugger : : register_message_capture ( const StringName & p_name , Capture p_func ) {
ERR_FAIL_COND_MSG ( captures . has ( p_name ) , " Capture already registered: " + p_name ) ;
captures . insert ( p_name , p_func ) ;
}
void EngineDebugger : : unregister_message_capture ( const StringName & p_name ) {
ERR_FAIL_COND_MSG ( ! captures . has ( p_name ) , " Capture not registered: " + p_name ) ;
captures . erase ( p_name ) ;
}
2020-03-11 10:23:21 +00:00
void EngineDebugger : : register_uri_handler ( const String & p_protocol , CreatePeerFunc p_func ) {
ERR_FAIL_COND_MSG ( protocols . has ( p_protocol ) , " Protocol handler already registered: " + p_protocol ) ;
protocols . insert ( p_protocol , p_func ) ;
}
2020-02-27 02:30:20 +00:00
void EngineDebugger : : profiler_enable ( const StringName & p_name , bool p_enabled , const Array & p_opts ) {
ERR_FAIL_COND_MSG ( ! profilers . has ( p_name ) , " Can't change profiler state, no profiler: " + p_name ) ;
Profiler & p = profilers [ p_name ] ;
if ( p . toggle ) {
p . toggle ( p . data , p_enabled , p_opts ) ;
}
p . active = p_enabled ;
}
void EngineDebugger : : profiler_add_frame_data ( const StringName & p_name , const Array & p_data ) {
ERR_FAIL_COND_MSG ( ! profilers . has ( p_name ) , " Can't add frame data, no profiler: " + p_name ) ;
Profiler & p = profilers [ p_name ] ;
if ( p . add ) {
p . add ( p . data , p_data ) ;
}
}
bool EngineDebugger : : is_profiling ( const StringName & p_name ) {
return profilers . has ( p_name ) & & profilers [ p_name ] . active ;
}
bool EngineDebugger : : has_profiler ( const StringName & p_name ) {
return profilers . has ( p_name ) ;
}
bool EngineDebugger : : has_capture ( const StringName & p_name ) {
return captures . has ( p_name ) ;
}
Error EngineDebugger : : capture_parse ( const StringName & p_name , const String & p_msg , const Array & p_args , bool & r_captured ) {
r_captured = false ;
ERR_FAIL_COND_V_MSG ( ! captures . has ( p_name ) , ERR_UNCONFIGURED , " Capture not registered: " + p_name ) ;
const Capture & cap = captures [ p_name ] ;
return cap . capture ( cap . data , p_msg , p_args , r_captured ) ;
}
void EngineDebugger : : line_poll ( ) {
// The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught
2020-05-14 14:41:43 +00:00
if ( poll_every % 2048 = = 0 ) {
2020-02-27 02:30:20 +00:00
poll_events ( false ) ;
2020-05-14 14:41:43 +00:00
}
2020-02-27 02:30:20 +00:00
poll_every + + ;
}
2021-02-02 02:16:37 +00:00
void EngineDebugger : : iteration ( uint64_t p_frame_ticks , uint64_t p_process_ticks , uint64_t p_physics_ticks , double p_physics_frame_time ) {
2020-02-27 02:30:20 +00:00
frame_time = USEC_TO_SEC ( p_frame_ticks ) ;
2020-12-22 09:50:29 +00:00
process_time = USEC_TO_SEC ( p_process_ticks ) ;
2020-02-27 02:30:20 +00:00
physics_time = USEC_TO_SEC ( p_physics_ticks ) ;
physics_frame_time = p_physics_frame_time ;
// Notify tick to running profilers
2021-08-09 20:13:42 +00:00
for ( KeyValue < StringName , Profiler > & E : profilers ) {
Profiler & p = E . value ;
2020-05-14 14:41:43 +00:00
if ( ! p . active | | ! p . tick ) {
2020-02-27 02:30:20 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-12-22 09:50:29 +00:00
p . tick ( p . data , frame_time , process_time , physics_time , physics_frame_time ) ;
2020-02-27 02:30:20 +00:00
}
singleton - > poll_events ( true ) ;
}
2022-07-21 19:41:02 +00:00
void EngineDebugger : : initialize ( const String & p_uri , bool p_skip_breakpoints , Vector < String > p_breakpoints , void ( * p_allow_focus_steal_fn ) ( ) ) {
2020-03-11 10:23:21 +00:00
register_uri_handler ( " tcp:// " , RemoteDebuggerPeerTCP : : create ) ; // TCP is the default protocol. Platforms/modules can add more.
2020-12-15 12:04:21 +00:00
if ( p_uri . is_empty ( ) ) {
2020-02-27 02:30:20 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2020-02-27 02:30:20 +00:00
if ( p_uri = = " local:// " ) {
singleton = memnew ( LocalDebugger ) ;
script_debugger = memnew ( ScriptDebugger ) ;
// Tell the OS that we want to handle termination signals.
OS : : get_singleton ( ) - > initialize_debugging ( ) ;
2020-03-11 10:23:21 +00:00
} else if ( p_uri . find ( " :// " ) > = 0 ) {
const String proto = p_uri . substr ( 0 , p_uri . find ( " :// " ) + 3 ) ;
2020-05-14 14:41:43 +00:00
if ( ! protocols . has ( proto ) ) {
2020-03-11 10:23:21 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2020-03-11 10:23:21 +00:00
RemoteDebuggerPeer * peer = protocols [ proto ] ( p_uri ) ;
2020-05-14 14:41:43 +00:00
if ( ! peer ) {
2020-02-27 02:30:20 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2020-03-11 10:23:21 +00:00
singleton = memnew ( RemoteDebugger ( Ref < RemoteDebuggerPeer > ( peer ) ) ) ;
2020-02-27 02:30:20 +00:00
script_debugger = memnew ( ScriptDebugger ) ;
// Notify editor of our pid (to allow focus stealing).
Array msg ;
msg . push_back ( OS : : get_singleton ( ) - > get_process_id ( ) ) ;
singleton - > send_message ( " set_pid " , msg ) ;
}
2020-05-14 14:41:43 +00:00
if ( ! singleton ) {
2020-02-27 02:30:20 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2020-02-27 02:30:20 +00:00
// There is a debugger, parse breakpoints.
2020-03-10 09:24:23 +00:00
ScriptDebugger * singleton_script_debugger = singleton - > get_script_debugger ( ) ;
singleton_script_debugger - > set_skip_breakpoints ( p_skip_breakpoints ) ;
2020-02-27 02:30:20 +00:00
for ( int i = 0 ; i < p_breakpoints . size ( ) ; i + + ) {
String bp = p_breakpoints [ i ] ;
2020-07-03 13:26:22 +00:00
int sp = bp . rfind ( " : " ) ;
2020-02-27 02:30:20 +00:00
ERR_CONTINUE_MSG ( sp = = - 1 , " Invalid breakpoint: ' " + bp + " ', expected file:line format. " ) ;
2020-03-10 09:24:23 +00:00
singleton_script_debugger - > insert_breakpoint ( bp . substr ( sp + 1 , bp . length ( ) ) . to_int ( ) , bp . substr ( 0 , sp ) ) ;
2020-02-27 02:30:20 +00:00
}
2022-07-21 19:41:02 +00:00
allow_focus_steal_fn = p_allow_focus_steal_fn ;
2020-02-27 02:30:20 +00:00
}
void EngineDebugger : : deinitialize ( ) {
2020-03-11 10:23:21 +00:00
if ( singleton ) {
// Stop all profilers
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , Profiler > & E : profilers ) {
if ( E . value . active ) {
singleton - > profiler_enable ( E . key , false ) ;
2020-05-14 14:41:43 +00:00
}
2020-03-11 10:23:21 +00:00
}
// Flush any remaining message
singleton - > poll_events ( false ) ;
memdelete ( singleton ) ;
singleton = nullptr ;
2020-02-27 02:30:20 +00:00
}
2021-03-12 13:35:16 +00:00
// Clear profilers/captures/protocol handlers.
2020-02-27 02:30:20 +00:00
profilers . clear ( ) ;
captures . clear ( ) ;
2020-03-11 10:23:21 +00:00
protocols . clear ( ) ;
2020-02-27 02:30:20 +00:00
}
EngineDebugger : : ~ EngineDebugger ( ) {
2020-05-14 14:41:43 +00:00
if ( script_debugger ) {
2020-02-27 02:30:20 +00:00
memdelete ( script_debugger ) ;
2020-05-14 14:41:43 +00:00
}
2020-04-01 23:20:12 +00:00
script_debugger = nullptr ;
singleton = nullptr ;
2020-02-27 02:30:20 +00:00
}