mirror of
https://github.com/godotengine/godot.git
synced 2025-12-08 06:09:55 +00:00
Merge pull request #91006 from reduz/live-backtrace
Ability to print and log script backtraces
This commit is contained in:
commit
28089c40c1
32 changed files with 813 additions and 95 deletions
|
|
@ -2949,17 +2949,14 @@ GDScriptLanguage::GDScriptLanguage() {
|
|||
script_frame_time = 0;
|
||||
#endif
|
||||
|
||||
int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024);
|
||||
|
||||
if (EngineDebugger::is_active()) {
|
||||
//debugging enabled!
|
||||
|
||||
_debug_max_call_stack = dmcs;
|
||||
} else {
|
||||
_debug_max_call_stack = 0;
|
||||
}
|
||||
_debug_max_call_stack = GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024);
|
||||
track_call_stack = GLOBAL_DEF_RST("debug/settings/gdscript/always_track_call_stacks", false);
|
||||
track_locals = GLOBAL_DEF_RST("debug/settings/gdscript/always_track_local_variables", false);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
track_call_stack = true;
|
||||
track_locals = track_locals || EngineDebugger::is_active();
|
||||
|
||||
GLOBAL_DEF("debug/gdscript/warnings/enable", true);
|
||||
GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
|
||||
GLOBAL_DEF("debug/gdscript/warnings/renamed_in_godot_4_hint", true);
|
||||
|
|
|
|||
|
|
@ -458,6 +458,8 @@ class GDScriptLanguage : public ScriptLanguage {
|
|||
|
||||
static thread_local CallStack _call_stack;
|
||||
int _debug_max_call_stack = 0;
|
||||
bool track_call_stack = false;
|
||||
bool track_locals = false;
|
||||
|
||||
void _add_global(const StringName &p_name, const Variant &p_value);
|
||||
void _remove_global(const StringName &p_name);
|
||||
|
|
@ -490,37 +492,63 @@ public:
|
|||
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
|
||||
|
||||
_FORCE_INLINE_ void enter_function(GDScriptInstance *p_instance, GDScriptFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
|
||||
if (!track_call_stack) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(_call_stack.levels == nullptr)) {
|
||||
_call_stack.levels = memnew_arr(CallLevel, _debug_max_call_stack + 1);
|
||||
}
|
||||
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
|
||||
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() + 1);
|
||||
#ifdef DEBUG_ENABLED
|
||||
ScriptDebugger *script_debugger = EngineDebugger::get_script_debugger();
|
||||
if (script_debugger != nullptr && script_debugger->get_lines_left() > 0 && script_debugger->get_depth() >= 0) {
|
||||
script_debugger->set_depth(script_debugger->get_depth() + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_call_stack.stack_pos >= _debug_max_call_stack) {
|
||||
//stack overflow
|
||||
if (unlikely(_call_stack.stack_pos >= _debug_max_call_stack)) {
|
||||
_debug_error = vformat("Stack overflow (stack size: %s). Check for infinite recursion in your script.", _debug_max_call_stack);
|
||||
EngineDebugger::get_script_debugger()->debug(this);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (script_debugger != nullptr) {
|
||||
script_debugger->debug(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_call_stack.levels[_call_stack.stack_pos].stack = p_stack;
|
||||
_call_stack.levels[_call_stack.stack_pos].instance = p_instance;
|
||||
_call_stack.levels[_call_stack.stack_pos].function = p_function;
|
||||
_call_stack.levels[_call_stack.stack_pos].ip = p_ip;
|
||||
_call_stack.levels[_call_stack.stack_pos].line = p_line;
|
||||
CallLevel &call_level = _call_stack.levels[_call_stack.stack_pos];
|
||||
call_level.stack = p_stack;
|
||||
call_level.instance = p_instance;
|
||||
call_level.function = p_function;
|
||||
call_level.ip = p_ip;
|
||||
call_level.line = p_line;
|
||||
_call_stack.stack_pos++;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void exit_function() {
|
||||
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
|
||||
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() - 1);
|
||||
if (!track_call_stack) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_call_stack.stack_pos == 0) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
ScriptDebugger *script_debugger = EngineDebugger::get_script_debugger();
|
||||
if (script_debugger != nullptr && script_debugger->get_lines_left() > 0 && script_debugger->get_depth() >= 0) {
|
||||
script_debugger->set_depth(script_debugger->get_depth() - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(_call_stack.stack_pos == 0)) {
|
||||
_debug_error = "Stack Underflow (Engine Bug)";
|
||||
EngineDebugger::get_script_debugger()->debug(this);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (script_debugger != nullptr) {
|
||||
script_debugger->debug(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -554,6 +582,7 @@ public:
|
|||
|
||||
} strings;
|
||||
|
||||
_FORCE_INLINE_ bool should_track_locals() const { return track_locals; }
|
||||
_FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); }
|
||||
_FORCE_INLINE_ Variant *get_global_array() { return _global_array; }
|
||||
_FORCE_INLINE_ const HashMap<StringName, int> &get_global_map() const { return globals; }
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ void GDScriptByteCodeGenerator::end_parameters() {
|
|||
|
||||
void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Variant p_rpc_config, const GDScriptDataType &p_return_type) {
|
||||
function = memnew(GDScriptFunction);
|
||||
debug_stack = EngineDebugger::is_active();
|
||||
debug_stack = GDScriptLanguage::get_singleton()->should_track_locals();
|
||||
|
||||
function->name = p_function_name;
|
||||
function->_script = p_script;
|
||||
|
|
|
|||
|
|
@ -237,11 +237,9 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
|
|||
emit_signal(SNAME("completed"), ret);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (EngineDebugger::is_active()) {
|
||||
GDScriptLanguage::get_singleton()->exit_function();
|
||||
}
|
||||
GDScriptLanguage::get_singleton()->exit_function();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
_clear_stack();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -621,12 +621,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
String err_text;
|
||||
|
||||
GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
if (EngineDebugger::is_active()) {
|
||||
GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
|
||||
}
|
||||
|
||||
#define GD_ERR_BREAK(m_cond) \
|
||||
{ \
|
||||
if (unlikely(m_cond)) { \
|
||||
|
|
@ -698,10 +695,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
profile.frame_call_count.increment();
|
||||
}
|
||||
bool exit_ok = false;
|
||||
bool awaited = false;
|
||||
int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? (int)p_instance->members.size() : 0 };
|
||||
#endif
|
||||
|
||||
bool awaited = false;
|
||||
Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr };
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
|
@ -2567,9 +2564,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
awaited = true;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
exit_ok = true;
|
||||
awaited = true;
|
||||
#endif
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
|
@ -3865,23 +3863,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if this is not the last time it was interrupted by `await` or if it's the first time executing.
|
||||
// If that is the case then we exit the function as normal. Otherwise we postpone it until the last `await` is completed.
|
||||
// This ensures the call stack can be properly shown when using `await`, showing what resumed the function.
|
||||
if (!p_state || awaited) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
GDScriptLanguage::get_singleton()->exit_function();
|
||||
}
|
||||
#endif
|
||||
GDScriptLanguage::get_singleton()->exit_function();
|
||||
|
||||
// Free stack, except reserved addresses.
|
||||
for (int i = FIXED_ADDRESSES_MAX; i < _stack_size; i++) {
|
||||
stack[i].~Variant();
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
}
|
||||
#endif
|
||||
|
||||
// Always free reserved addresses, since they are never copied.
|
||||
for (int i = 0; i < FIXED_ADDRESSES_MAX; i++) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue