2014-02-09 22:10:30 -03:00
/**************************************************************************/
/* error_macros.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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-09 22:10:30 -03:00
# include "error_macros.h"
2017-01-16 08:04:19 +01:00
2018-09-11 18:13:45 +02:00
# include "core/io/logger.h"
2025-02-20 08:40:46 -06:00
# include "core/object/object_id.h"
2025-03-31 21:39:43 +02:00
# include "core/object/script_language.h"
2020-11-07 19:33:38 -03:00
# include "core/os/os.h"
# include "core/string/ustring.h"
2014-02-09 22:10:30 -03:00
2024-05-26 19:39:28 +02:00
// Optional physics interpolation warnings try to include the path to the relevant node.
# if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
# include "core/config/project_settings.h"
# include "scene/main/node.h"
# endif
2020-04-02 01:20:12 +02:00
static ErrorHandlerList * error_handler_list = nullptr ;
2014-02-09 22:10:30 -03:00
void add_error_handler ( ErrorHandlerList * p_handler ) {
2021-12-14 13:42:46 +01:00
// If p_handler is already in error_handler_list
// we'd better remove it first then we can add it.
// This prevent cyclic redundancy.
remove_error_handler ( p_handler ) ;
2014-02-09 22:10:30 -03:00
_global_lock ( ) ;
2021-12-14 13:42:46 +01:00
2014-02-09 22:10:30 -03:00
p_handler - > next = error_handler_list ;
error_handler_list = p_handler ;
2021-12-14 13:42:46 +01:00
2014-02-09 22:10:30 -03:00
_global_unlock ( ) ;
}
2022-04-05 13:40:26 +03:00
void remove_error_handler ( const ErrorHandlerList * p_handler ) {
2014-02-09 22:10:30 -03:00
_global_lock ( ) ;
2020-04-02 01:20:12 +02:00
ErrorHandlerList * prev = nullptr ;
2014-02-09 22:10:30 -03:00
ErrorHandlerList * l = error_handler_list ;
while ( l ) {
if ( l = = p_handler ) {
2020-05-14 16:41:43 +02:00
if ( prev ) {
2014-02-09 22:10:30 -03:00
prev - > next = l - > next ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-09 22:10:30 -03:00
error_handler_list = l - > next ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
break ;
}
prev = l ;
l = l - > next ;
}
_global_unlock ( ) ;
}
2021-09-22 17:36:40 +02:00
// Errors without messages.
void _err_print_error ( const char * p_function , const char * p_file , int p_line , const char * p_error , bool p_editor_notify , ErrorHandlerType p_type ) {
_err_print_error ( p_function , p_file , p_line , p_error , " " , p_editor_notify , p_type ) ;
2014-02-09 22:10:30 -03:00
}
2017-10-17 11:37:40 -03:00
2021-09-22 17:36:40 +02:00
void _err_print_error ( const char * p_function , const char * p_file , int p_line , const String & p_error , bool p_editor_notify , ErrorHandlerType p_type ) {
_err_print_error ( p_function , p_file , p_line , p_error . utf8 ( ) . get_data ( ) , " " , p_editor_notify , p_type ) ;
2019-11-06 08:38:23 +01:00
}
2021-09-22 17:36:40 +02:00
// Main error printing function.
void _err_print_error ( const char * p_function , const char * p_file , int p_line , const char * p_error , const char * p_message , bool p_editor_notify , ErrorHandlerType p_type ) {
2022-06-30 15:42:03 +02:00
if ( OS : : get_singleton ( ) ) {
2025-03-31 21:39:43 +02:00
Vector < Ref < ScriptBacktrace > > script_backtraces ;
// If script languages aren't initialized, we could be in the process of shutting down, in which case we don't want to allocate any objects, as we could be
// logging ObjectDB leaks, where ObjectDB would be locked, thus causing a deadlock.
if ( ScriptServer : : are_languages_initialized ( ) ) {
script_backtraces = ScriptServer : : capture_script_backtraces ( false ) ;
}
OS : : get_singleton ( ) - > print_error ( p_function , p_file , p_line , p_error , p_message , p_editor_notify , ( Logger : : ErrorType ) p_type , script_backtraces ) ;
2022-06-30 15:42:03 +02:00
} else {
// Fallback if errors happen before OS init or after it's destroyed.
const char * err_details = ( p_message & & * p_message ) ? p_message : p_error ;
fprintf ( stderr , " ERROR: %s \n at: %s (%s:%i) \n " , err_details , p_function , p_file , p_line ) ;
}
2019-11-10 07:44:31 +01:00
_global_lock ( ) ;
ErrorHandlerList * l = error_handler_list ;
while ( l ) {
2021-09-22 17:36:40 +02:00
l - > errfunc ( l - > userdata , p_function , p_file , p_line , p_error , p_message , p_editor_notify , p_type ) ;
2019-11-10 07:44:31 +01:00
l = l - > next ;
}
_global_unlock ( ) ;
}
2024-10-21 15:35:22 -03:00
// For printing errors when we may crash at any point, so we must flush ASAP a lot of lines
// but we don't want to make it noisy by printing lots of file & line info (because it's already
// been printing by a preceding _err_print_error).
void _err_print_error_asap ( const String & p_error , ErrorHandlerType p_type ) {
if ( OS : : get_singleton ( ) ) {
OS : : get_singleton ( ) - > printerr ( " ERROR: %s \n " , p_error . utf8 ( ) . get_data ( ) ) ;
} else {
// Fallback if errors happen before OS init or after it's destroyed.
const char * err_details = p_error . utf8 ( ) . get_data ( ) ;
fprintf ( stderr , " ERROR: %s \n " , err_details ) ;
}
_global_lock ( ) ;
ErrorHandlerList * l = error_handler_list ;
while ( l ) {
l - > errfunc ( l - > userdata , " " , " " , 0 , p_error . utf8 ( ) . get_data ( ) , " " , false , p_type ) ;
l = l - > next ;
}
_global_unlock ( ) ;
}
2021-09-22 17:36:40 +02:00
// Errors with message. (All combinations of p_error and p_message as String or char*.)
void _err_print_error ( const char * p_function , const char * p_file , int p_line , const String & p_error , const char * p_message , bool p_editor_notify , ErrorHandlerType p_type ) {
_err_print_error ( p_function , p_file , p_line , p_error . utf8 ( ) . get_data ( ) , p_message , p_editor_notify , p_type ) ;
2019-11-10 07:44:31 +01:00
}
2021-09-22 17:36:40 +02:00
void _err_print_error ( const char * p_function , const char * p_file , int p_line , const char * p_error , const String & p_message , bool p_editor_notify , ErrorHandlerType p_type ) {
_err_print_error ( p_function , p_file , p_line , p_error , p_message . utf8 ( ) . get_data ( ) , p_editor_notify , p_type ) ;
2019-11-10 07:44:31 +01:00
}
2021-09-22 17:36:40 +02:00
void _err_print_error ( const char * p_function , const char * p_file , int p_line , const String & p_error , const String & p_message , bool p_editor_notify , ErrorHandlerType p_type ) {
_err_print_error ( p_function , p_file , p_line , p_error . utf8 ( ) . get_data ( ) , p_message . utf8 ( ) . get_data ( ) , p_editor_notify , p_type ) ;
2019-11-10 07:44:31 +01:00
}
2021-09-22 17:36:40 +02:00
// Index errors. (All combinations of p_message as String or char*.)
void _err_print_index_error ( const char * p_function , const char * p_file , int p_line , int64_t p_index , int64_t p_size , const char * p_index_str , const char * p_size_str , const char * p_message , bool p_editor_notify , bool p_fatal ) {
String fstr ( p_fatal ? " FATAL: " : " " ) ;
2020-01-24 16:53:52 +01:00
String err ( fstr + " Index " + p_index_str + " = " + itos ( p_index ) + " is out of bounds ( " + p_size_str + " = " + itos ( p_size ) + " ). " ) ;
2023-01-22 12:23:56 +01:00
_err_print_error ( p_function , p_file , p_line , err . utf8 ( ) . get_data ( ) , p_message , p_editor_notify , ERR_HANDLER_ERROR ) ;
2019-11-10 07:44:31 +01:00
}
2021-09-22 17:36:40 +02:00
void _err_print_index_error ( const char * p_function , const char * p_file , int p_line , int64_t p_index , int64_t p_size , const char * p_index_str , const char * p_size_str , const String & p_message , bool p_editor_notify , bool p_fatal ) {
2023-01-22 12:23:56 +01:00
_err_print_index_error ( p_function , p_file , p_line , p_index , p_size , p_index_str , p_size_str , p_message . utf8 ( ) . get_data ( ) , p_editor_notify , p_fatal ) ;
2017-10-17 11:37:40 -03:00
}
2022-02-05 12:31:54 +00:00
void _err_flush_stdout ( ) {
fflush ( stdout ) ;
}
2024-05-26 19:39:28 +02:00
// Prevent error spam by limiting the warnings to a certain frequency.
void _physics_interpolation_warning ( const char * p_function , const char * p_file , int p_line , ObjectID p_id , const char * p_warn_string ) {
# if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
const uint32_t warn_max = 2048 ;
const uint32_t warn_timeout_seconds = 15 ;
static uint32_t warn_count = warn_max ;
static uint32_t warn_timeout = warn_timeout_seconds ;
uint32_t time_now = UINT32_MAX ;
if ( warn_count ) {
warn_count - - ;
}
if ( ! warn_count ) {
time_now = OS : : get_singleton ( ) - > get_ticks_msec ( ) / 1000 ;
}
if ( ( warn_count = = 0 ) & & ( time_now > = warn_timeout ) ) {
warn_count = warn_max ;
warn_timeout = time_now + warn_timeout_seconds ;
if ( GLOBAL_GET ( " debug/settings/physics_interpolation/enable_warnings " ) ) {
// UINT64_MAX means unused.
if ( p_id . operator uint64_t ( ) = = UINT64_MAX ) {
_err_print_error ( p_function , p_file , p_line , " [Physics interpolation] " + String ( p_warn_string ) + " (possibly benign). " , false , ERR_HANDLER_WARNING ) ;
} else {
String node_name ;
if ( p_id . is_valid ( ) ) {
2025-03-27 14:42:42 +01:00
Node * node = ObjectDB : : get_instance < Node > ( p_id ) ;
2024-05-26 19:39:28 +02:00
if ( node & & node - > is_inside_tree ( ) ) {
node_name = " \" " + String ( node - > get_path ( ) ) + " \" " ;
} else {
node_name = " \" unknown \" " ;
}
}
_err_print_error ( p_function , p_file , p_line , " [Physics interpolation] " + String ( p_warn_string ) + " : " + node_name + " (possibly benign). " , false , ERR_HANDLER_WARNING ) ;
}
}
}
# endif
}