Skip to content

Conversation

@reduz
Copy link
Member

@reduz reduz commented Apr 22, 2024

  • Changes how stack information is stored in GDScript to a reverse linked list.
  • This makes it fast enough to leave it enabled all time time on debug.
  • Added a new script function script_backtrace() to get a string with the current script backtrace.
  • Added the script backtrace to the logger. Errors now include the full currently running script backtraces.
  • Added script backtrace printing to console logs (Windows, MacOS, Linux) using blue color.
  • Additionally, added a custom Logger class to allow users intercept the internal messages/warnings/error (supersedes Add user-facing log interception callback. #87576).

How it looks:

using script_backtrace()

image

Results in the following output:

image

Logger

Backtraces are now added to the loggers. Here is how it looks for ANSI/Windows console loggers:

image

@realcoloride
Copy link

Extremely wanted feature that could make it way easier to debug. Love this

@bruvzg
Copy link
Member

bruvzg commented Apr 22, 2024

Not sure how reliable it will be, but it might be worth calling it from the crash handler as well (tested on macOS only and seems to be working):

diff --git a/platform/macos/crash_handler_macos.mm b/platform/macos/crash_handler_macos.mm
index c370422bfa..91f76dc3b5 100644
--- a/platform/macos/crash_handler_macos.mm
+++ b/platform/macos/crash_handler_macos.mm
@@ -31,6 +31,7 @@
 #include "crash_handler_macos.h"
 
 #include "core/config/project_settings.h"
+#include "core/object/script_language.h"
 #include "core/os/os.h"
 #include "core/string/print_string.h"
 #include "core/version.h"
@@ -171,6 +172,9 @@ static void handle_crash(int sig) {
 	}
 	print_error("-- END OF BACKTRACE --");
 	print_error("================================================================");
+	print_error(ScriptServer::get_current_script_backtrace());
+	print_error("-- END OF SCRIPT BACKTRACE --");
+	print_error("================================================================");
 
 	// Abort to pass the error to the OS
 	abort();

@RedMser
Copy link
Contributor

RedMser commented Apr 22, 2024

How does the new function relate to the existing @GDScript.get_stack() and print_stack()? I assume there are technical differences, but these are not really made clear enough to the average user (when should I use this new function vs the existing ones?).

@farfalk
Copy link

farfalk commented Apr 22, 2024

In addition to what RedMser asked, how does this relate to #87576 ?

@reduz
Copy link
Member Author

reduz commented Apr 22, 2024

@RedMser

This new one works always, even if not running the debugger and it also is generic for all languages. print_stack() was created mostly for testing and should be deprecated and removed since its pretty useless.

@farfalk

Both PRs should work great together

@permelin
Copy link
Contributor

Did a quick test script with a function that is recursively called 500 times inside a for loop. To my surprise, this branch is 5% faster than master.

(Pushing the call stack beyond a depth of 506 or 507 will SIGSEGV, but that happens in master too.)

@dalexeev
Copy link
Member

Can script_backtrace() return data in a structured form (Array[Dictionary]), like get_stack()? It also looks like this PR will resolve godotengine/godot-proposals#105.

@reduz
Copy link
Member Author

reduz commented Apr 30, 2024

@dalexeev I honestly don't see a lot of use for this, since the main use case is to print it, but I think adding another function script_stack() may be good for this, that could also include more stack information if you want.

@reduz reduz force-pushed the live-backtrace branch 4 times, most recently from bb26553 to bcfa1bd Compare April 30, 2024 12:23
@reduz reduz requested a review from a team as a code owner April 30, 2024 12:23
limbonaut added a commit to limbonaut/godot that referenced this pull request May 25, 2025
* GDScript call stack as reverse linked list with issues fixed
(originally proposed in godotengineGH-91006).
* This makes it fast enough to leave it enabled all the time, so this
also drops "debug/settings/gdscript/always_track_call_stacks" setting.
* Fix coroutine issues with call stack by resuming async call chain
inside `GDScriptFunction::call()`.
* This fixes corrupted line numbers for coroutines in the debugger and
backtrace (godotengineGH-106758).

Co-authored-by: Juan Linietsky <[email protected]>
limbonaut added a commit to limbonaut/godot that referenced this pull request May 25, 2025
* GDScript call stack as reverse linked list with issues fixed
(originally proposed in godotengineGH-91006).
* This makes it fast enough to leave it enabled all the time, so this
also drops "debug/settings/gdscript/always_track_call_stacks" setting.
* Fix coroutine issues with call stack by resuming async call chain
inside `GDScriptFunction::call()`.
* This fixes corrupted line numbers for coroutines in the debugger and
backtrace (godotengineGH-106489).

Co-authored-by: Juan Linietsky <[email protected]>
limbonaut added a commit to limbonaut/godot that referenced this pull request May 25, 2025
* GDScript call stack as reverse linked list with issues fixed
(originally proposed in godotengineGH-91006).
* Fix coroutine issues with call stack by resuming async call chain
inside `GDScriptFunction::call()`.
* This fixes corrupted line numbers for coroutines in the debugger and
backtrace (godotengineGH-106489).

Co-authored-by: Juan Linietsky <[email protected]>
limbonaut added a commit to limbonaut/godot that referenced this pull request May 25, 2025
* GDScript call stack as reverse linked list with issues fixed
(originally proposed in godotengineGH-91006).
* Fix coroutine issues with call stack by resuming async call chain
inside `GDScriptFunction::call()`.
* This fixes corrupted line numbers for coroutines in the debugger and
backtrace (godotengineGH-106489).

Co-authored-by: Juan Linietsky <[email protected]>
limbonaut added a commit to limbonaut/godot that referenced this pull request May 25, 2025
* GDScript call stack as reverse linked list with issues fixed
(originally proposed in godotengineGH-91006).
* Fix coroutine issues with call stack by resuming async call chain
inside `GDScriptFunction::call()`.
* This fixes corrupted line numbers for coroutines in the debugger and
backtrace (godotengineGH-106489).

Co-authored-by: Juan Linietsky <[email protected]>
limbonaut added a commit to limbonaut/godot that referenced this pull request May 25, 2025
* GDScript call stack as reverse linked list with issues fixed
(originally proposed in godotengineGH-91006).
* Fix coroutine issues with call stack by resuming async call chain
inside `GDScriptFunction::call()`.
* This fixes corrupted line numbers for coroutines in the debugger and
backtrace (godotengineGH-106489).

Co-authored-by: Juan Linietsky <[email protected]>
limbonaut added a commit to limbonaut/godot that referenced this pull request May 25, 2025
* GDScript call stack as reverse linked list with issues fixed
(originally proposed in godotengineGH-91006).
* Fix coroutine issues with call stack by resuming async call chain
inside `GDScriptFunction::call()`.
* This fixes corrupted line numbers for coroutines in the debugger and
backtrace (godotengineGH-106489).

Co-authored-by: Juan Linietsky <[email protected]>
@akien-mga akien-mga changed the title Ability to print and log script backtraces Add Logger and ability to print and log script backtraces Jun 18, 2025
@OS-of-S
Copy link
Contributor

OS-of-S commented Sep 17, 2025

Ok, I see that this is powerful tool to get a detailed bug reports from real players. As I understend, the final realisation of script_backtrace() is game developer's deal. But on exemple screenshots of the use of the presented tool I see resource addresses and function names, and I have one little question: is it safe? I mean... To protect the game from being hacked and cheated, encryption is a common solution in Godot (otherwise the game is very easy to edit). Couldn't it possibly create a hole for hacking even an encrypted application?

@Mickeon
Copy link
Member

Mickeon commented Sep 17, 2025

But on exemple screenshots of the use of the presented tool I see resource addresses and function names,

The functionality introduced in this PR needs to be explicitly enabled in release builds. It is normally only available in debug builds.

Variable and function names are not normally obfuscated in GDScript, mainly because it is a dynamic language and it still relies heavily on strings. For example, an Object's script can change at any time, signals can be added for any given object with add_user_signal and dynamic properties can be added through _get_property_list. All of this means that these are not always guaranteed to exist, for one reason or another.

encryption is a common solution in Godot (otherwise the game is very easy to edit).

Regardless of encryption, Godot games are notoriously easy to decompile. This is not something new, and it's not something that this PR affects. Encryption only really blocks the common user.

@mihe
Copy link
Contributor

mihe commented Sep 17, 2025

The functionality introduced in this PR needs to be explicitly enabled in release builds. It is normally only available in debug builds.

To be more specific, you would need to enable the debug/settings/gdscript/always_track_call_stacks project setting for script backtraces to be available/printed in release exports.

@OS-of-S
Copy link
Contributor

OS-of-S commented Sep 18, 2025

Regardless of encryption, Godot games are notoriously easy to decompile. This is not something new, and it's not something that this PR affects. Encryption only really prevents the common user

As I know, GDRETool still require to insert an encryption key if project were encrypted. And my cheat-forums research shows that players still (likely for me :D) have problems with hacking godot games. Becouse of GDScript dinamical structure (as you mentioned allready) common strong tools became unsuitable. So the question of safity is quite relevant to this topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.