Skip to content

Commit 28089c4

Browse files
committed
Merge pull request #91006 from reduz/live-backtrace
Ability to print and log script backtraces
2 parents 500e0d5 + d1dcb40 commit 28089c4

32 files changed

+813
-95
lines changed

core/core_bind.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,79 @@ void ResourceSaver::_bind_methods() {
210210
BIND_BITFIELD_FLAG(FLAG_REPLACE_SUBRESOURCE_PATHS);
211211
}
212212

213+
////// Logger ///////
214+
215+
void Logger::_bind_methods() {
216+
GDVIRTUAL_BIND(_log_error, "function", "file", "line", "code", "rationale", "editor_notify", "error_type", "script_backtraces");
217+
GDVIRTUAL_BIND(_log_message, "message", "error");
218+
BIND_ENUM_CONSTANT(ERROR_TYPE_ERROR);
219+
BIND_ENUM_CONSTANT(ERROR_TYPE_WARNING);
220+
BIND_ENUM_CONSTANT(ERROR_TYPE_SCRIPT);
221+
BIND_ENUM_CONSTANT(ERROR_TYPE_SHADER);
222+
}
223+
224+
void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const TypedArray<ScriptBacktrace> &p_script_backtraces) {
225+
GDVIRTUAL_CALL(_log_error, String::utf8(p_function), String::utf8(p_file), p_line, String::utf8(p_code), String::utf8(p_rationale), p_editor_notify, p_type, p_script_backtraces);
226+
}
227+
228+
void Logger::log_message(const String &p_text, bool p_error) {
229+
GDVIRTUAL_CALL(_log_message, p_text, p_error);
230+
}
231+
213232
////// OS //////
214233

234+
void OS::LoggerBind::logv(const char *p_format, va_list p_list, bool p_err) {
235+
if (!should_log(p_err) || is_logging) {
236+
return;
237+
}
238+
239+
is_logging = true;
240+
241+
constexpr int static_buf_size = 1024;
242+
char static_buf[static_buf_size] = { '\0' };
243+
char *buf = static_buf;
244+
va_list list_copy;
245+
va_copy(list_copy, p_list);
246+
int len = vsnprintf(buf, static_buf_size, p_format, p_list);
247+
if (len >= static_buf_size) {
248+
buf = (char *)Memory::alloc_static(len + 1);
249+
vsnprintf(buf, len + 1, p_format, list_copy);
250+
}
251+
va_end(list_copy);
252+
253+
String str;
254+
str.append_utf8(buf, len);
255+
for (Ref<CoreBind::Logger> &logger : loggers) {
256+
logger->log_message(str, p_err);
257+
}
258+
259+
if (len >= static_buf_size) {
260+
Memory::free_static(buf);
261+
}
262+
263+
is_logging = false;
264+
}
265+
266+
void OS::LoggerBind::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
267+
if (!should_log(true) || is_logging) {
268+
return;
269+
}
270+
271+
TypedArray<ScriptBacktrace> backtraces;
272+
backtraces.resize(p_script_backtraces.size());
273+
for (int i = 0; i < p_script_backtraces.size(); i++) {
274+
backtraces[i] = p_script_backtraces[i];
275+
}
276+
277+
is_logging = true;
278+
279+
for (Ref<CoreBind::Logger> &logger : loggers) {
280+
logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, CoreBind::Logger::ErrorType(p_type), backtraces);
281+
}
282+
283+
is_logging = false;
284+
}
285+
215286
PackedByteArray OS::get_entropy(int p_bytes) {
216287
PackedByteArray pba;
217288
pba.resize(p_bytes);
@@ -628,6 +699,24 @@ String OS::get_unique_id() const {
628699
return ::OS::get_singleton()->get_unique_id();
629700
}
630701

702+
void OS::add_logger(const Ref<Logger> &p_logger) {
703+
ERR_FAIL_COND(p_logger.is_null());
704+
705+
if (!logger_bind) {
706+
logger_bind = memnew(LoggerBind);
707+
::OS::get_singleton()->add_logger(logger_bind);
708+
}
709+
710+
ERR_FAIL_COND_MSG(logger_bind->loggers.find(p_logger) != -1, "Could not add logger, as it has already been added.");
711+
logger_bind->loggers.push_back(p_logger);
712+
}
713+
714+
void OS::remove_logger(const Ref<Logger> &p_logger) {
715+
ERR_FAIL_COND(p_logger.is_null());
716+
ERR_FAIL_COND_MSG(!logger_bind || logger_bind->loggers.find(p_logger) == -1, "Could not remove logger, as it hasn't been added.");
717+
logger_bind->loggers.erase(p_logger);
718+
}
719+
631720
OS *OS::singleton = nullptr;
632721

633722
void OS::_bind_methods() {
@@ -734,6 +823,9 @@ void OS::_bind_methods() {
734823
ClassDB::bind_method(D_METHOD("get_granted_permissions"), &OS::get_granted_permissions);
735824
ClassDB::bind_method(D_METHOD("revoke_granted_permissions"), &OS::revoke_granted_permissions);
736825

826+
ClassDB::bind_method(D_METHOD("add_logger", "logger"), &OS::add_logger);
827+
ClassDB::bind_method(D_METHOD("remove_logger", "logger"), &OS::remove_logger);
828+
737829
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode");
738830
ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec");
739831
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "delta_smoothing"), "set_delta_smoothing", "is_delta_smoothing_enabled");
@@ -764,6 +856,20 @@ void OS::_bind_methods() {
764856
BIND_ENUM_CONSTANT(STD_HANDLE_UNKNOWN);
765857
}
766858

859+
OS::OS() {
860+
singleton = this;
861+
}
862+
863+
OS::~OS() {
864+
if (singleton == this) {
865+
singleton = nullptr;
866+
}
867+
868+
if (logger_bind) {
869+
logger_bind->clear();
870+
}
871+
}
872+
767873
////// Geometry2D //////
768874

769875
Geometry2D *Geometry2D::singleton = nullptr;
@@ -1898,6 +2004,16 @@ ScriptLanguage *Engine::get_script_language(int p_index) const {
18982004
return ScriptServer::get_language(p_index);
18992005
}
19002006

2007+
TypedArray<ScriptBacktrace> Engine::capture_script_backtraces(bool p_include_variables) const {
2008+
Vector<Ref<ScriptBacktrace>> backtraces = ScriptServer::capture_script_backtraces(p_include_variables);
2009+
TypedArray<ScriptBacktrace> result;
2010+
result.resize(backtraces.size());
2011+
for (int i = 0; i < backtraces.size(); i++) {
2012+
result[i] = backtraces[i];
2013+
}
2014+
return result;
2015+
}
2016+
19012017
void Engine::set_editor_hint(bool p_enabled) {
19022018
::Engine::get_singleton()->set_editor_hint(p_enabled);
19032019
}
@@ -1985,6 +2101,7 @@ void Engine::_bind_methods() {
19852101
ClassDB::bind_method(D_METHOD("unregister_script_language", "language"), &Engine::unregister_script_language);
19862102
ClassDB::bind_method(D_METHOD("get_script_language_count"), &Engine::get_script_language_count);
19872103
ClassDB::bind_method(D_METHOD("get_script_language", "index"), &Engine::get_script_language);
2104+
ClassDB::bind_method(D_METHOD("capture_script_backtraces", "include_variables"), &Engine::capture_script_backtraces, DEFVAL(false));
19882105

19892106
ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);
19902107
ClassDB::bind_method(D_METHOD("is_embedded_in_editor"), &Engine::is_embedded_in_editor);

core/core_bind.h

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@
3333
#include "core/debugger/engine_profiler.h"
3434
#include "core/io/resource_loader.h"
3535
#include "core/io/resource_saver.h"
36+
#include "core/object/script_backtrace.h"
3637
#include "core/os/semaphore.h"
3738
#include "core/os/thread.h"
3839
#include "core/templates/safe_refcount.h"
40+
#include "core/variant/typed_array.h"
3941

4042
class MainLoop;
41-
template <typename T>
42-
class TypedArray;
4343

4444
namespace CoreBind {
4545

@@ -119,11 +119,46 @@ class ResourceSaver : public Object {
119119
ResourceSaver() { singleton = this; }
120120
};
121121

122+
class Logger : public RefCounted {
123+
GDCLASS(Logger, RefCounted);
124+
125+
public:
126+
enum ErrorType {
127+
ERROR_TYPE_ERROR,
128+
ERROR_TYPE_WARNING,
129+
ERROR_TYPE_SCRIPT,
130+
ERROR_TYPE_SHADER,
131+
};
132+
133+
protected:
134+
GDVIRTUAL2(_log_message, String, bool);
135+
GDVIRTUAL8(_log_error, String, String, int, String, String, bool, int, TypedArray<ScriptBacktrace>);
136+
static void _bind_methods();
137+
138+
public:
139+
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERROR_TYPE_ERROR, const TypedArray<ScriptBacktrace> &p_script_backtraces = {});
140+
virtual void log_message(const String &p_text, bool p_error);
141+
};
142+
122143
class OS : public Object {
123144
GDCLASS(OS, Object);
124145

125146
mutable HashMap<String, bool> feature_cache;
126147

148+
class LoggerBind : public ::Logger {
149+
inline static thread_local bool is_logging = false;
150+
151+
public:
152+
LocalVector<Ref<CoreBind::Logger>> loggers;
153+
154+
virtual void logv(const char *p_format, va_list p_list, bool p_err) override _PRINTF_FORMAT_ATTRIBUTE_2_0;
155+
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces = {}) override;
156+
157+
void clear() { loggers.clear(); }
158+
};
159+
160+
LoggerBind *logger_bind = nullptr;
161+
127162
protected:
128163
static void _bind_methods();
129164
static OS *singleton;
@@ -274,9 +309,13 @@ class OS : public Object {
274309
Vector<String> get_granted_permissions() const;
275310
void revoke_granted_permissions();
276311

312+
void add_logger(const Ref<Logger> &p_logger);
313+
void remove_logger(const Ref<Logger> &p_logger);
314+
277315
static OS *get_singleton() { return singleton; }
278316

279-
OS() { singleton = this; }
317+
OS();
318+
~OS();
280319
};
281320

282321
class Geometry2D : public Object {
@@ -579,6 +618,7 @@ class Engine : public Object {
579618
Error unregister_script_language(const ScriptLanguage *p_language);
580619
int get_script_language_count();
581620
ScriptLanguage *get_script_language(int p_index) const;
621+
TypedArray<ScriptBacktrace> capture_script_backtraces(bool p_include_variables = false) const;
582622

583623
void set_editor_hint(bool p_enabled);
584624
bool is_editor_hint() const;
@@ -653,6 +693,7 @@ class EngineDebugger : public Object {
653693

654694
} // namespace CoreBind
655695

696+
VARIANT_ENUM_CAST(CoreBind::Logger::ErrorType);
656697
VARIANT_ENUM_CAST(CoreBind::ResourceLoader::ThreadLoadStatus);
657698
VARIANT_ENUM_CAST(CoreBind::ResourceLoader::CacheMode);
658699

core/error/error_macros.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "core/io/logger.h"
3434
#include "core/object/object_id.h"
35+
#include "core/object/script_language.h"
3536
#include "core/os/os.h"
3637
#include "core/string/ustring.h"
3738

@@ -91,7 +92,15 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
9192
// Main error printing function.
9293
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) {
9394
if (OS::get_singleton()) {
94-
OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, p_editor_notify, (Logger::ErrorType)p_type);
95+
Vector<Ref<ScriptBacktrace>> script_backtraces;
96+
97+
// 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
98+
// logging ObjectDB leaks, where ObjectDB would be locked, thus causing a deadlock.
99+
if (ScriptServer::are_languages_initialized()) {
100+
script_backtraces = ScriptServer::capture_script_backtraces(false);
101+
}
102+
103+
OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, p_editor_notify, (Logger::ErrorType)p_type, script_backtraces);
95104
} else {
96105
// Fallback if errors happen before OS init or after it's destroyed.
97106
const char *err_details = (p_message && *p_message) ? p_message : p_error;

core/io/logger.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void Logger::set_flush_stdout_on_print(bool value) {
5555
_flush_stdout_on_print = value;
5656
}
5757

58-
void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
58+
void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
5959
if (!should_log(true)) {
6060
return;
6161
}
@@ -88,6 +88,10 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
8888

8989
logf_error("%s: %s\n", err_type, err_details);
9090
logf_error(" at: %s (%s:%i)\n", p_function, p_file, p_line);
91+
92+
for (const Ref<ScriptBacktrace> &backtrace : p_script_backtraces) {
93+
logf_error("%s\n", backtrace->format(3).utf8().get_data());
94+
}
9195
}
9296

9397
void Logger::logf(const char *p_format, ...) {
@@ -263,13 +267,13 @@ void CompositeLogger::logv(const char *p_format, va_list p_list, bool p_err) {
263267
}
264268
}
265269

266-
void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
270+
void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
267271
if (!should_log(true)) {
268272
return;
269273
}
270274

271275
for (int i = 0; i < loggers.size(); ++i) {
272-
loggers[i]->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type);
276+
loggers[i]->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type, p_script_backtraces);
273277
}
274278
}
275279

core/io/logger.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#pragma once
3232

3333
#include "core/io/file_access.h"
34+
#include "core/object/script_backtrace.h"
3435
#include "core/string/ustring.h"
3536
#include "core/templates/vector.h"
3637

@@ -55,7 +56,7 @@ class Logger {
5556
static void set_flush_stdout_on_print(bool value);
5657

5758
virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0;
58-
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR);
59+
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces = {});
5960

6061
void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
6162
void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@@ -102,7 +103,7 @@ class CompositeLogger : public Logger {
102103
explicit CompositeLogger(const Vector<Logger *> &p_loggers);
103104

104105
virtual void logv(const char *p_format, va_list p_list, bool p_err) override _PRINTF_FORMAT_ATTRIBUTE_2_0;
105-
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR) override;
106+
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces = {}) override;
106107

107108
void add_logger(Logger *p_logger);
108109

0 commit comments

Comments
 (0)