Skip to content

Improve script backtrace print in crash handlers #106139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

akien-mga
Copy link
Member

@akien-mga akien-mga commented May 7, 2025

Also fix the editor crash handler message for bug reports not properly using the .editor override.

Before this change, -- END OF SCRIPT BACKTRACE -- would always be printed, even if it's a pure C++ crash with no script backtrace. That's because ScriptServer::capture_script_backtraces is never empty (unless there's no script language compiled in) as it includes empty backtraces for languages with no backtrace.

I moved that end message to the inner loop so it's only printed if each specific backtrace is non-empty, and while at it I included the language name in that message.

Before

C++ only crash:

[1] /lib64/libc.so.6(+0x19c30) [0x7ffff7ccec30] (??:0)
[2] EditorNode::drag_resource(Ref<Resource> const&, Control*) (/home/akien/Godot/godot/editor/editor_node.cpp:6033 (discriminator 1))
[3] EditorResourcePicker::get_drag_data_fw(Vector2 const&, Control*) (/home/akien/Godot/godot/editor/editor_resource_picker.cpp:741)
[4] void call_with_variant_args_ret_helper<EditorResourcePicker, Variant, Vector2 const&, Control*, 0ul, 1ul>(EditorResourcePicker*, Variant (EditorResourcePicker::*)(Vector2 const&, Control*), Variant const**, Variant&, Callable::CallError&, IndexSequence<0ul, 1ul>) (/home/akien/Godot/godot/core/variant/binder_common.h:672 (discriminator 7))
[5] void call_with_variant_args_ret<EditorResourcePicker, Variant, Vector2 const&, Control*>(EditorResourcePicker*, Variant (EditorResourcePicker::*)(Vector2 const&, Control*), Variant const**, int, Variant&, Callable::CallError&) (/home/akien/Godot/godot/core/variant/binder_common.h:716)
[6] CallableCustomMethodPointer<EditorResourcePicker, Variant, Vector2 const&, Control*>::call(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/object/callable_method_pointer.h:105)
[7] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable.cpp:57)
[8] CallableCustomBind::call(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable_bind.cpp:151)
[9] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable.cpp:57)
[10] Control::get_drag_data(Vector2 const&) (/home/akien/Godot/godot/scene/gui/control.cpp:2019)
[11] Viewport::_gui_input_event(Ref<InputEvent>) (/home/akien/Godot/godot/scene/main/viewport.cpp:2015 (discriminator 5))
[12] Viewport::push_input(Ref<InputEvent> const&, bool) (/home/akien/Godot/godot/scene/main/viewport.cpp:3426 (discriminator 2))
[13] Window::_window_input(Ref<InputEvent> const&) (/home/akien/Godot/godot/scene/main/window.cpp:1812)
[14] void call_with_variant_args_helper<Window, Ref<InputEvent> const&, 0ul>(Window*, void (Window::*)(Ref<InputEvent> const&), Variant const**, Callable::CallError&, IndexSequence<0ul>) (/home/akien/Godot/godot/core/variant/binder_common.h:223 (discriminator 6))
[15] void call_with_variant_args<Window, Ref<InputEvent> const&>(Window*, void (Window::*)(Ref<InputEvent> const&), Variant const**, int, Callable::CallError&) (/home/akien/Godot/godot/core/variant/binder_common.h:338)
[16] CallableCustomMethodPointer<Window, void, Ref<InputEvent> const&>::call(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/object/callable_method_pointer.h:107)
[17] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable.cpp:57)
[18] Variant Callable::call<Ref<InputEvent> >(Ref<InputEvent>) const (/home/akien/Godot/godot/core/variant/variant.h:942)
[19] DisplayServerX11::_dispatch_input_event(Ref<InputEvent> const&) (/home/akien/Godot/godot/platform/linuxbsd/x11/display_server_x11.cpp:4393 (discriminator 2))
[20] DisplayServerX11::_dispatch_input_events(Ref<InputEvent> const&) (/home/akien/Godot/godot/platform/linuxbsd/x11/display_server_x11.cpp:4370)
[21] Input::_parse_input_event_impl(Ref<InputEvent> const&, bool) (/home/akien/Godot/godot/core/input/input.cpp:903)
[22] Input::flush_buffered_events() (/home/akien/Godot/godot/core/input/input.cpp:1184)
[23] DisplayServerX11::process_events() (/home/akien/Godot/godot/platform/linuxbsd/x11/display_server_x11.cpp:5548)
[24] OS_LinuxBSD::run() (/home/akien/Godot/godot/platform/linuxbsd/os_linuxbsd.cpp:977)
[25] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(main+0x14b) [0x6c7e4f1] (/home/akien/Godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:85)
[26] /lib64/libc.so.6(+0x35f5) [0x7ffff7cb85f5] (??:0)
[27] /lib64/libc.so.6(__libc_start_main+0x88) [0x7ffff7cb86a8] (??:0)
[28] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(_start+0x25) [0x6c7e2e5] (??:?)
-- END OF BACKTRACE --
================================================================
-- END OF SCRIPT BACKTRACE --
================================================================

Crash originating in GDScript (OS.crash()):

ERROR: This is the end.
   at: crash (./core/core_bind.cpp:344)
   GDScript backtrace (most recent call first):
       [0] _ready (res://main.gd:5)

================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.5.dev.custom_build (6a6a1168a5a7b14ef988f3516303b6d67a985ebd)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib64/libc.so.6(+0x19c30) [0x7f7714186c30] (??:0)
[2] CoreBind::OS::crash(String const&) (/home/akien/Godot/godot/core/core_bind.cpp:344 (discriminator 2))
[3] void call_with_validated_variant_args_helper<__UnexistingClass, String const&, 0ul>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), Variant const**, IndexSequence<0ul>) (/home/akien/Godot/godot/core/variant/binder_common.h:285)
[4] void call_with_validated_object_instance_args<__UnexistingClass, String const&>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), Variant const**) (/home/akien/Godot/godot/core/variant/binder_common.h:572)
[5] MethodBindT<String const&>::validated_call(Object*, Variant const**, Variant*) const (/home/akien/Godot/godot/core/object/method_bind.h:351)
[6] GDScriptFunction::call(GDScriptInstance*, Variant const**, int, Callable::CallError&, GDScriptFunction::CallState*) (/home/akien/Godot/godot/modules/gdscript/gdscript_vm.cpp:2294)
[7] GDScriptInstance::callp(StringName const&, Variant const**, int, Callable::CallError&) (/home/akien/Godot/godot/modules/gdscript/gdscript.cpp:2058 (discriminator 1))
[8] Node::_gdvirtual__ready_call() (/home/akien/Godot/godot/scene/main/node.h:401 (discriminator 11))
[9] Node::_notification(int) (/home/akien/Godot/godot/scene/main/node.cpp:320)
[10] Node::_notification_forwardv(int) (/home/akien/Godot/godot/scene/main/node.h:48)
[11] CanvasItem::_notification_forwardv(int) (/home/akien/Godot/godot/scene/main/canvas_item.h:43 (discriminator 1))
[12] Node2D::_notification_forwardv(int) (/home/akien/Godot/godot/scene/2d/node_2d.h:36 (discriminator 1))
[13] Object::_notification_forward(int) (/home/akien/Godot/godot/core/object/object.cpp:933)
[14] Object::notification(int, bool) (/home/akien/Godot/godot/core/object/object.h:880)
[15] Node::_propagate_ready() (/home/akien/Godot/godot/scene/main/node.cpp:371)
[16] Node::_propagate_ready() (/home/akien/Godot/godot/scene/main/node.cpp:360 (discriminator 8))
[17] Node::_set_tree(SceneTree*) (/home/akien/Godot/godot/scene/main/node.cpp:3451)
[18] SceneTree::initialize() (/home/akien/Godot/godot/scene/main/scene_tree.cpp:574)
[19] OS_LinuxBSD::run() (/home/akien/Godot/godot/platform/linuxbsd/os_linuxbsd.cpp:975)
[20] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(main+0x14b) [0x6c7e4f1] (/home/akien/Godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:85)
[21] /lib64/libc.so.6(+0x35f5) [0x7f77141705f5] (??:0)
[22] /lib64/libc.so.6(__libc_start_main+0x88) [0x7f77141706a8] (??:0)
[23] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(_start+0x25) [0x6c7e2e5] (??:?)
-- END OF BACKTRACE --
================================================================
GDScript backtrace (most recent call first):
    [0] _ready (res://main.gd:5)
-- END OF SCRIPT BACKTRACE --
================================================================

After

C++ only crash:

================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.5.dev.custom_build (6a6a1168a5a7b14ef988f3516303b6d67a985ebd)
Dumping the backtrace. Please include this when reporting the bug on: https://github.com/godotengine/godot/issues
[1] /lib64/libc.so.6(+0x19c30) [0x7fbeb7964c30] (??:0)
[2] EditorNode::drag_resource(Ref<Resource> const&, Control*) (/home/akien/Godot/godot/editor/editor_node.cpp:6033 (discriminator 1))
[3] EditorResourcePicker::get_drag_data_fw(Vector2 const&, Control*) (/home/akien/Godot/godot/editor/editor_resource_picker.cpp:741)
[4] void call_with_variant_args_ret_helper<EditorResourcePicker, Variant, Vector2 const&, Control*, 0ul, 1ul>(EditorResourcePicker*, Variant (EditorResourcePicker::*)(Vector2 const&, Control*), Variant const**, Variant&, Callable::CallError&, IndexSequence<0ul, 1ul>) (/home/akien/Godot/godot/core/variant/binder_common.h:672 (discriminator 7))
[5] void call_with_variant_args_ret<EditorResourcePicker, Variant, Vector2 const&, Control*>(EditorResourcePicker*, Variant (EditorResourcePicker::*)(Vector2 const&, Control*), Variant const**, int, Variant&, Callable::CallError&) (/home/akien/Godot/godot/core/variant/binder_common.h:716)
[6] CallableCustomMethodPointer<EditorResourcePicker, Variant, Vector2 const&, Control*>::call(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/object/callable_method_pointer.h:105)
[7] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable.cpp:57)
[8] CallableCustomBind::call(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable_bind.cpp:151)
[9] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable.cpp:57)
[10] Control::get_drag_data(Vector2 const&) (/home/akien/Godot/godot/scene/gui/control.cpp:2019)
[11] Viewport::_gui_input_event(Ref<InputEvent>) (/home/akien/Godot/godot/scene/main/viewport.cpp:2015 (discriminator 5))
[12] Viewport::push_input(Ref<InputEvent> const&, bool) (/home/akien/Godot/godot/scene/main/viewport.cpp:3426 (discriminator 2))
[13] Window::_window_input(Ref<InputEvent> const&) (/home/akien/Godot/godot/scene/main/window.cpp:1812)
[14] void call_with_variant_args_helper<Window, Ref<InputEvent> const&, 0ul>(Window*, void (Window::*)(Ref<InputEvent> const&), Variant const**, Callable::CallError&, IndexSequence<0ul>) (/home/akien/Godot/godot/core/variant/binder_common.h:223 (discriminator 6))
[15] void call_with_variant_args<Window, Ref<InputEvent> const&>(Window*, void (Window::*)(Ref<InputEvent> const&), Variant const**, int, Callable::CallError&) (/home/akien/Godot/godot/core/variant/binder_common.h:338)
[16] CallableCustomMethodPointer<Window, void, Ref<InputEvent> const&>::call(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/object/callable_method_pointer.h:107)
[17] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/home/akien/Godot/godot/core/variant/callable.cpp:57)
[18] Variant Callable::call<Ref<InputEvent> >(Ref<InputEvent>) const (/home/akien/Godot/godot/core/variant/variant.h:942)
[19] DisplayServerX11::_dispatch_input_event(Ref<InputEvent> const&) (/home/akien/Godot/godot/platform/linuxbsd/x11/display_server_x11.cpp:4393 (discriminator 2))
[20] DisplayServerX11::_dispatch_input_events(Ref<InputEvent> const&) (/home/akien/Godot/godot/platform/linuxbsd/x11/display_server_x11.cpp:4370)
[21] Input::_parse_input_event_impl(Ref<InputEvent> const&, bool) (/home/akien/Godot/godot/core/input/input.cpp:903)
[22] Input::flush_buffered_events() (/home/akien/Godot/godot/core/input/input.cpp:1184)
[23] DisplayServerX11::process_events() (/home/akien/Godot/godot/platform/linuxbsd/x11/display_server_x11.cpp:5548)
[24] OS_LinuxBSD::run() (/home/akien/Godot/godot/platform/linuxbsd/os_linuxbsd.cpp:977)
[25] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(main+0x14b) [0x6c7e3f1] (/home/akien/Godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:85)
[26] /lib64/libc.so.6(+0x35f5) [0x7fbeb794e5f5] (??:0)
[27] /lib64/libc.so.6(__libc_start_main+0x88) [0x7fbeb794e6a8] (??:0)
[28] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(_start+0x25) [0x6c7e1e5] (??:?)
-- END OF C++ BACKTRACE --
================================================================

Crash originating in GDScript (OS.crash()):

ERROR: This is the end.
   at: crash (./core/core_bind.cpp:344)
   GDScript backtrace (most recent call first):
       [0] _ready (res://main.gd:5)

================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.5.dev.custom_build (6a6a1168a5a7b14ef988f3516303b6d67a985ebd)
Dumping the backtrace. Please include this when reporting the bug on: https://github.com/godotengine/godot/issues
[1] /lib64/libc.so.6(+0x19c30) [0x7f31a2d63c30] (??:0)
[2] CoreBind::OS::crash(String const&) (/home/akien/Godot/godot/core/core_bind.cpp:344 (discriminator 2))
[3] void call_with_validated_variant_args_helper<__UnexistingClass, String const&, 0ul>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), Variant const**, IndexSequence<0ul>) (/home/akien/Godot/godot/core/variant/binder_common.h:285)
[4] void call_with_validated_object_instance_args<__UnexistingClass, String const&>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), Variant const**) (/home/akien/Godot/godot/core/variant/binder_common.h:572)
[5] MethodBindT<String const&>::validated_call(Object*, Variant const**, Variant*) const (/home/akien/Godot/godot/core/object/method_bind.h:351)
[6] GDScriptFunction::call(GDScriptInstance*, Variant const**, int, Callable::CallError&, GDScriptFunction::CallState*) (/home/akien/Godot/godot/modules/gdscript/gdscript_vm.cpp:2294)
[7] GDScriptInstance::callp(StringName const&, Variant const**, int, Callable::CallError&) (/home/akien/Godot/godot/modules/gdscript/gdscript.cpp:2058 (discriminator 1))
[8] Node::_gdvirtual__ready_call() (/home/akien/Godot/godot/scene/main/node.h:401 (discriminator 11))
[9] Node::_notification(int) (/home/akien/Godot/godot/scene/main/node.cpp:320)
[10] Node::_notification_forwardv(int) (/home/akien/Godot/godot/scene/main/node.h:48)
[11] CanvasItem::_notification_forwardv(int) (/home/akien/Godot/godot/scene/main/canvas_item.h:43 (discriminator 1))
[12] Node2D::_notification_forwardv(int) (/home/akien/Godot/godot/scene/2d/node_2d.h:36 (discriminator 1))
[13] Object::_notification_forward(int) (/home/akien/Godot/godot/core/object/object.cpp:933)
[14] Object::notification(int, bool) (/home/akien/Godot/godot/core/object/object.h:880)
[15] Node::_propagate_ready() (/home/akien/Godot/godot/scene/main/node.cpp:371)
[16] Node::_propagate_ready() (/home/akien/Godot/godot/scene/main/node.cpp:360 (discriminator 8))
[17] Node::_set_tree(SceneTree*) (/home/akien/Godot/godot/scene/main/node.cpp:3451)
[18] SceneTree::initialize() (/home/akien/Godot/godot/scene/main/scene_tree.cpp:574)
[19] OS_LinuxBSD::run() (/home/akien/Godot/godot/platform/linuxbsd/os_linuxbsd.cpp:975)
[20] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(main+0x14b) [0x6c7e3f1] (/home/akien/Godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:85)
[21] /lib64/libc.so.6(+0x35f5) [0x7f31a2d4d5f5] (??:0)
[22] /lib64/libc.so.6(__libc_start_main+0x88) [0x7f31a2d4d6a8] (??:0)
[23] /home/akien/Godot/godot/bin/godot.linuxbsd.editor.dev.x86_64(_start+0x25) [0x6c7e1e5] (??:?)
-- END OF C++ BACKTRACE --
================================================================
GDScript backtrace (most recent call first):
    [0] _ready (res://main.gd:5)
-- END OF GDSCRIPT BACKTRACE --
================================================================

Also fix the editor crash handler message for bug reports not properly
using the `.editor` override.
@akien-mga
Copy link
Member Author

akien-mga commented May 7, 2025

As a side note, I'm not sure I'm fully sold yet on how this looks like:

image

My first thought seeing this was that the indentation and gray color for the GDScript backtrace were not intentional. Thinking about it more, I guess the intent is to show that this GDScript backtrace is directly related to the red "ERROR" print, which makes sense. But it blending with the "at:" location for the error looks a bit weird to me.

It might just be a matter of getting used to it, it kind of makes sense as is too.

Maybe a slightly lighter gray while keeping the indentation may help it stand out from the C++ location?

@akien-mga akien-mga requested review from mihe and bruvzg May 7, 2025 08:04
Copy link
Contributor

@mihe mihe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried running it, but the code LGTM.

I guess the intent is to show that this GDScript backtrace is directly related to the red "ERROR" print, which makes sense.

Yes.

But it blending with the "at:" location for the error looks a bit weird to me.

Agreed. I don't really like it either, but I struggled to think of a better alternative without changing the formatting of the at: stuff in some way. The current format at least seemed preferrable to not indenting the backtrace at all.

But yeah, maybe you're on to something with the lighter color.

@akien-mga
Copy link
Member Author

But it blending with the "at:" location for the error looks a bit weird to me.

Agreed. I don't really like it either, but I struggled to think of a better alternative without changing the formatting of the at: stuff in some way. The current format at least seemed preferrable to not indenting the backtrace at all.

But yeah, maybe you're on to something with the lighter color.

It seems like currently we only support 3-4 bit colors:
https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit

So there's no other shade of grey to use. We already use red, yellow, magenta and cyan, so only green and blue are left.
Blue is what it was using originally in the PR but @Calinou and I found it too jarring and suggested gray, which I now realize isn't perfect either.

Here's a try with green (Konsole):

image

but this starts looking like the flag of Hungary :P

Let's leave that for later bikeshedding. But I wonder whether we could start using 8-bit or 24-bit colors for our rich text printing, this might still be plenty enough for the terminals we aim to support, and others could just be without colors. 24-bit colors would mean we could support any hexadecimal code in print_rich instead of just a few hardcoded colors. CC @Calinou @bruvzg

@Calinou
Copy link
Member

Calinou commented May 7, 2025

Let's leave that for later bikeshedding. But I wonder whether we could start using 8-bit or 24-bit colors for our rich text printing, this might still be plenty enough for the terminals we aim to support, and others could just be without colors. 24-bit colors would mean we could support any hexadecimal code in print_rich instead of just a few hardcoded colors. CC

Truecolor ANSI codes are already supported in print_rich() with BBCode, but our logging code uses direct ANSI escape codes (so we could use 256-color or truecolor ANSI colors). One challenge however is that these colors are not adjustable by the user's terminal theme, so we need to pick one that looks good on both a dark and light background.

256-color codes should be preferred for portability, as macOS Terminal.app supports 256-color but not truecolor (even in macOS 15).

@Repiteo Repiteo merged commit 872ca1b into godotengine:master May 7, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented May 7, 2025

Thanks!

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.

4 participants