Skip to content
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

SCons: Add -ffile-prefix-map GCC/Clang option to make debug file paths relative #78232

Merged

Conversation

akien-mga
Copy link
Member

@akien-mga akien-mga commented Jun 14, 2023

Supported since GCC 8 and Clang 10.

I need to add checks to actually enforce that we only define it for compatible versions, or just add a SCons option to toggle this on or off (which might be needed for distro packaging as those typically remap debuginfo paths to where they install them system wide).

Follow-up to #78063.

Before this PR:

ERROR: oh noes
   at: crash (./core/core_bind.cpp:240)

================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.1.beta.custom_build (a7263d32fba7fcbcae18ae5f654556af7bca1188)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib64/libc.so.6(+0x36980) [0x7f57ed5c5980] (??:0)
[2] core_bind::OS::crash(String const&) (/home/akien/Projects/godot/godot.git/./core/core_bind.cpp:240)
[3] void call_with_ptr_args_helper<__UnexistingClass, String const&, 0ul>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), void const**, IndexSequence<0ul>) (/home/akien/Projects/godot/godot.git/./core/variant/binder_common.h:324 (discriminator 8))
[4] void call_with_ptr_args<__UnexistingClass, String const&>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), void const**) (/home/akien/Projects/godot/godot.git/./core/variant/binder_common.h:573)
[5] MethodBindT<String const&>::ptrcall(Object*, void const**, void*) const (/home/akien/Projects/godot/godot.git/./core/object/method_bind.h:350)
[6] GDScriptFunction::call(GDScriptInstance*, Variant const**, int, Callable::CallError&, GDScriptFunction::CallState*) (/home/akien/Projects/godot/godot.git/./modules/gdscript/gdscript_vm.cpp:2085)
[7] GDScript::_create_instance(Variant const**, int, Object*, bool, Callable::CallError&) (/home/akien/Projects/godot/godot.git/./modules/gdscript/gdscript.cpp:176)
[8] GDScript::instance_create(Object*) (/home/akien/Projects/godot/godot.git/./modules/gdscript/gdscript.cpp:410)
[9] Object::set_script(Variant const&) (/home/akien/Projects/godot/godot.git/./core/object/object.cpp:850)
[10] MainLoop::initialize() (/home/akien/Projects/godot/godot.git/./core/os/main_loop.cpp:61 (discriminator 4))
[11] OS_LinuxBSD::run() (/home/akien/Projects/godot/godot.git/platform/linuxbsd/os_linuxbsd.cpp:900)
[12] godot-git(main+0x15a) [0x5687280] (/home/akien/Projects/godot/godot.git/platform/linuxbsd/godot_linuxbsd.cpp:76)
[13] /lib64/libc.so.6(+0x236b7) [0x7f57ed5b26b7] (??:0)
[14] /lib64/libc.so.6(__libc_start_main+0x85) [0x7f57ed5b2775] (??:0)
[15] godot-git(_start+0x21) [0x5687061] (??:?)
-- END OF BACKTRACE --
================================================================
Aborted (core dumped)

With this PR:

ERROR: oh noes
   at: crash (./core/core_bind.cpp:241)

================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.1.beta.custom_build (5af89e87a4d96255007ec8f40ac2cc177800d8d5)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib64/libc.so.6(+0x36980) [0x7ff1934a5980] (??:0)
[2] core_bind::OS::crash(String const&) (./core/core_bind.cpp:241)
[3] void call_with_ptr_args_helper<__UnexistingClass, String const&, 0ul>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), void const**, IndexSequence<0ul>) (./core/variant/binder_common.h:324 (discriminator 8))
[4] void call_with_ptr_args<__UnexistingClass, String const&>(__UnexistingClass*, void (__UnexistingClass::*)(String const&), void const**) (./core/variant/binder_common.h:573)
[5] MethodBindT<String const&>::ptrcall(Object*, void const**, void*) const (./core/object/method_bind.h:350)
[6] GDScriptFunction::call(GDScriptInstance*, Variant const**, int, Callable::CallError&, GDScriptFunction::CallState*) (./modules/gdscript/gdscript_vm.cpp:2085)
[7] GDScript::_create_instance(Variant const**, int, Object*, bool, Callable::CallError&) (./modules/gdscript/gdscript.cpp:178)
[8] GDScript::instance_create(Object*) (./modules/gdscript/gdscript.cpp:412)
[9] Object::set_script(Variant const&) (./core/object/object.cpp:850)
[10] MainLoop::initialize() (./core/os/main_loop.cpp:61 (discriminator 4))
[11] OS_LinuxBSD::run() (platform/linuxbsd/os_linuxbsd.cpp:900)
[12] godot-git(main+0x15a) [0x56552c0] (platform/linuxbsd/godot_linuxbsd.cpp:76)
[13] /lib64/libc.so.6(+0x236b7) [0x7ff1934926b7] (??:0)
[14] /lib64/libc.so.6(__libc_start_main+0x85) [0x7ff193492775] (??:0)
[15] godot-git(_start+0x21) [0x56550a1] (??:?)
-- END OF BACKTRACE --
================================================================
Aborted (core dumped)

There's still this pesky ./ prefix but it might come from something else, as it's also present unnecessarily in the "before" path: /home/akien/Projects/godot/godot.git/./core/core_bind.cpp:240

I'd appreciate help from interesting contributors who want to review the actual impact of the various -f*-prefix-map flags (there's quite a bunch of them) and best practices when it comes to making reproducible builds and avoiding including buildsystem specific paths in the binary and debuginfo.

SConstruct Outdated
@@ -81,7 +81,7 @@ for x in sorted(glob.glob("platform/*")):
continue
tmppath = "./" + x

sys.path.insert(0, tmppath)
sys.path.insert(1, tmppath)
Copy link
Member Author

@akien-mga akien-mga Jun 14, 2023

Choose a reason for hiding this comment

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

I changed these to be able to rely on sys.path[0] to get the project folder where SConstruct is. Yet again this seemingly unsolved Python problem to get easy access to the main working directory :P

Another option is to redo the same that @lawnjelly did in scu_builders.py:

from pathlib import Path
from os.path import normpath, basename

base_folder_path = str(Path(__file__).parent) + "/"
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))

Both options feel quite hacky. Feedback welcome from Python experts :)

Copy link
Member Author

Choose a reason for hiding this comment

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

CC @touilleMan if you have any suggestion.

Copy link
Member

@ajreckof ajreckof Apr 10, 2024

Choose a reason for hiding this comment

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

sys.path.insert(0, tmppath) will make sure the import just below is always the right one putting it on second place means that it will check the path only after checking the local folder. if we are sure there will not be any file that could be misinterpreted as one of the libraries this should be good to go. On the other hand relying on sys.path[0] feels strange. Normally to get the project folder I would set an env variable from the main script using file and get the env var from all other scripts.
Beware I'm not familiar with godot codebase specificity hence giving advice based on quickly looking at the files which means I might (and most likely) have missed something.

Copy link
Contributor

@shana shana Apr 11, 2024

Choose a reason for hiding this comment

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

I agree with @ajreckof, changing the insert order risks causing name clash issues. It's already a hack changing the path order the way we do, if we start messing with it because other things rely on some random thing being in a certain place in the path array, it's only going to be worse.

Scons provides objects that resolve paths to the root - Dir("#").abspath or File("#nameoffile").abspath. If a script doesn't have access to these Scons functions, then having the path to the root available from an env var, like @ajreckof suggested, would be a great fallback.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks both for the advice! Both options sound good. I don't know why I didn't think of Dir("#") since we're in SConstruct and it's available... I tested it and it works, so we don't need to hack sys.path further.

@@ -592,6 +592,9 @@ if selected_platform in platform_list:
env.Append(CCFLAGS=["-g3"])
else:
env.Append(CCFLAGS=["-g2"])
# Remap absolute paths to relative paths for debug symbols.
project_path = sys.path[0]
env.Append(CCFLAGS=[f"-ffile-prefix-map={project_path}=."])
Copy link
Member Author

Choose a reason for hiding this comment

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

For the record I tried both with =. and just =, both work fine but there's no difference, relative paths still start with ./.

@akien-mga akien-mga force-pushed the scons-debug-ffile-prefix-map branch from 597368c to 2630a47 Compare April 11, 2024 08:22
@akien-mga akien-mga marked this pull request as ready for review April 11, 2024 08:22
@akien-mga akien-mga requested a review from a team as a code owner April 11, 2024 08:22
@akien-mga
Copy link
Member Author

I need to add checks to actually enforce that we only define it for compatible versions, or just add a SCons option to toggle this on or off (which might be needed for distro packaging as those typically remap debuginfo paths to where they install them system wide).

Added a second commit implementing this, to be squashed if approved.
The compiler version checks keep getting hackier and hackier, I'll really need to sit down and refactor this one day.

The behavior can now be disabled with the debug_paths_relative SCons option.

One "drawback" of using this feature is that the pwd will appear in the build logs:

g++ -o platform/linuxbsd/x11/gl_manager_x11.linuxbsd.editor.dev.x86_64.o -c -std=gnu++17 -fno-exceptions -Wctor-dtor-privacy -Wnon-virtual-dtor -Wplacement-new=1 -pipe -gdwarf-4 -g3 -ffile-prefix-map=/home/akien/Godot/godot=. -O0 -Wall -Wextra ... <snip> ... -I. platform/linuxbsd/x11/gl_manager_x11.cpp

But that's what needed for it to not appear in the actual binary's symbols (my godot-4.2 build in this example doesn't use this PR):

$ readelf --string-dump=.debug_str ../godot-4.2/bin/godot.linuxbsd.editor.dev.x86_64 | grep akien
  [ a2711]  /home/akien/Godot/godot-4.2
$ readelf --string-dump=.debug_str bin/godot.linuxbsd.editor.dev.x86_64 | grep akien

@akien-mga akien-mga requested review from shana and bruvzg April 11, 2024 08:43
Comment on lines -586 to -594
# Set optimize and debug_symbols flags.
# "custom" means do nothing and let users set their own optimization flags.
# Needs to happen after configure to have `env.msvc` defined.
if env.msvc:
if env["debug_symbols"]:
env.Append(CCFLAGS=["/Zi", "/FS"])
env.Append(LINKFLAGS=["/DEBUG:FULL"])
else:
env.Append(LINKFLAGS=["/DEBUG:NONE"])
Copy link
Member Author

Choose a reason for hiding this comment

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

Technically I just moved the compiler version checks before this stuff, but Git shows it the other way around ;)

Comment on lines +666 to +668
if env["debug_paths_relative"] and cc_version_major < 10:
print("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
Copy link
Member Author

@akien-mga akien-mga Apr 11, 2024

Choose a reason for hiding this comment

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

To avoid repetition I only check the not vanilla case in the macOS/iOS branch, which should already gracefully handle disabling the option if Apple Clang version is too old. So I think it should be fine with this check re-running for vanilla Clang in the main scope.

The same reasoning could likely be adopted for the repeated Clang 6 check.

Copy link
Contributor

@shana shana left a comment

Choose a reason for hiding this comment

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

Looks good! 🚀

@mhilbrunner
Copy link
Member

Confirmed the editor builds and runs fine with this PR on VS2022 MSVC.

@akien-mga akien-mga modified the milestones: 4.x, 4.3 Apr 11, 2024
@mhilbrunner
Copy link
Member

mhilbrunner commented Apr 11, 2024

@akien-mga akien-mga force-pushed the scons-debug-ffile-prefix-map branch from b5a314f to 74e6b5a Compare April 12, 2024 08:08
@akien-mga akien-mga merged commit e5c4ce6 into godotengine:master Apr 13, 2024
16 checks passed
@akien-mga akien-mga deleted the scons-debug-ffile-prefix-map branch April 13, 2024 09:34
@akien-mga
Copy link
Member Author

akien-mga commented Apr 15, 2024

So I noticed an issue caused by this, which is this warning in gdb:

0x000000000716af79 in CowData<GodotCollisionObject3D::Shape>::size (this=0xe7ca378) at ./core/templates/cowdata.h:183
warning: 183    ./core/templates/cowdata.h: No such file or directory

Since the paths are relative, the source files can only be found when running Godot from the root of the source repository folder. So running Godot from a project folder with gdb godot and r won't be able to locate those. One needs to instead run Godot from the source folder with gdb godot and r --path path/to/project.

While the backtrace still looks the same, inspecting frames with list doesn't work in the case where it fails to locate the header. I assume similar issues to locate the source files might affect graphical debuggers and lldb.

So for local development builds, we should probably default this new option to False and keep the previous behavior. The -ffile-prefix-map option may still be useful for official builds once we figure out how to provide debug symbols for them easily, since there the absolute path to the buildserver isn't usable, and header files may not be provided (or may be handled by a symbolication server or similar approach).

akien-mga added a commit to akien-mga/godot that referenced this pull request Apr 15, 2024
As pointed out in godotengine#78232 (comment),
it actually makes it harder to run Godot locally while keeping the relationship with the
header files it was compiled from.
divshekhar pushed a commit to divshekhar/godot that referenced this pull request Apr 23, 2024
As pointed out in godotengine#78232 (comment),
it actually makes it harder to run Godot locally while keeping the relationship with the
header files it was compiled from.
theromis pushed a commit to theromis/godot that referenced this pull request Apr 29, 2024
As pointed out in godotengine#78232 (comment),
it actually makes it harder to run Godot locally while keeping the relationship with the
header files it was compiled from.
dimitry- pushed a commit to AndroidWasm/godot that referenced this pull request May 16, 2024
As pointed out in godotengine#78232 (comment),
it actually makes it harder to run Godot locally while keeping the relationship with the
header files it was compiled from.
MewPurPur pushed a commit to MewPurPur/godot that referenced this pull request Jul 11, 2024
As pointed out in godotengine#78232 (comment),
it actually makes it harder to run Godot locally while keeping the relationship with the
header files it was compiled from.
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.

5 participants