diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee7a5b3aa88..42d18337af3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -673,6 +673,7 @@ jobs: run: | cmake -S lang/cpp -B build \ -DCMAKE_BUILD_TYPE=Debug \ + -DVORTEX_DEBUG_INFO=0 \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DVORTEX_SANITIZER=asan \ diff --git a/lang/cpp/CMakeLists.txt b/lang/cpp/CMakeLists.txt index 47d977697df..4d969b375bd 100644 --- a/lang/cpp/CMakeLists.txt +++ b/lang/cpp/CMakeLists.txt @@ -39,6 +39,8 @@ option(VORTEX_WARNINGS_AS_ERRORS "Treat warnings in Vortex-owned sources as errors" "${_vortex_top_level}") +include(../../vortex-ffi/cmake/DebugInfo.cmake) + # The Rust FFI archive comes from vortex-ffi. A standalone configure of this # directory adds it here; the repository root or a parent may already have. if(NOT TARGET Vortex::ffi_static) diff --git a/lang/cpp/README.md b/lang/cpp/README.md index 5159bb9207b..950f11fe001 100644 --- a/lang/cpp/README.md +++ b/lang/cpp/README.md @@ -45,6 +45,7 @@ options. Defaults below are for standalone builds. | `VORTEX_RUSTUP_TOOLCHAIN` | Inferred | [Rust toolchain override](#toolchain-and-build-behavior). | | `VORTEX_SANITIZER` | Empty | [Sanitizers](#sanitizers): `asan`, `lsan`, `ubsan`, `tsan`. | | `VORTEX_SANITIZE_RUST_STD` | `OFF` | Also instrument Rust's standard library. | +| `VORTEX_DEBUG_INFO` | `2` | C/C++ and Rust debug info: `0` none, `1` limited, `2` full. | | `VORTEX_ENABLE_CUDA` | `OFF` | Linux-only [CUDA build](#cuda). | Embedded builds default `VORTEX_WARNINGS_AS_ERRORS` to `OFF`. Parents must call diff --git a/vortex-ffi/cmake/Configure.cmake b/vortex-ffi/cmake/Configure.cmake index e51ebe3b792..7ae8ad06405 100644 --- a/vortex-ffi/cmake/Configure.cmake +++ b/vortex-ffi/cmake/Configure.cmake @@ -7,6 +7,7 @@ include_guard(GLOBAL) include("${CMAKE_CURRENT_LIST_DIR}/Cuda.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/DebugInfo.cmake") include("${CMAKE_CURRENT_LIST_DIR}/Helpers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/RustToolchain.cmake") include("${CMAKE_CURRENT_LIST_DIR}/SystemDependencies.cmake") @@ -184,7 +185,6 @@ function(_vortex_resolve_sanitizer list(APPEND _rustflags -A warnings -Cunsafe-allow-abi-mismatch=sanitizer - -C debuginfo=2 -C opt-level=0 # Use the sanitizer runtime linked by the final C++ target for both languages. -Zexternal-clangrt @@ -239,8 +239,9 @@ function(_vortex_native_flags endif() # Native dependencies become part of the archive embedded in shared parents. - list(APPEND _cflags -fPIC) - list(APPEND _cxxflags -fPIC) + # Match CMake targets even when cc-rs or parent flags enable debug info. + list(APPEND _cflags -fPIC -g${VORTEX_DEBUG_INFO}) + list(APPEND _cxxflags -fPIC -g${VORTEX_DEBUG_INFO}) set(${cflags_output} "${_cflags}" PARENT_SCOPE) set(${cxxflags_output} "${_cxxflags}" PARENT_SCOPE) @@ -282,7 +283,10 @@ block(SCOPE_FOR VARIABLES) _native_cxx_flags) # Mirror .cargo/config.toml's Unix rustflags, which CARGO_ENCODED_RUSTFLAGS overrides. # Keep them in sync; PIC additionally allows embedding in shared libraries. - set(_rustflags ${_sanitizer_rustflags} -C force-frame-pointers=yes -C relocation-model=pic) + set(_rustflags ${_sanitizer_rustflags} + -C force-frame-pointers=yes + -C relocation-model=pic + -C "debuginfo=${VORTEX_DEBUG_INFO}") # Cargo owns incremental invalidation inside this CMake-build-local cache. # Registering the directory as additional clean state gives the standard diff --git a/vortex-ffi/cmake/DebugInfo.cmake b/vortex-ffi/cmake/DebugInfo.cmake new file mode 100644 index 00000000000..e683448c239 --- /dev/null +++ b/vortex-ffi/cmake/DebugInfo.cmake @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright the Vortex contributors + +include_guard(DIRECTORY) + +set(VORTEX_DEBUG_INFO "2" CACHE STRING + "Debug information for C, C++, and Rust: 0 (none), 1 (limited), 2 (full)") +set_property(CACHE VORTEX_DEBUG_INFO PROPERTY STRINGS 0 1 2) +if(NOT VORTEX_DEBUG_INFO MATCHES "^[012]$") + message(FATAL_ERROR "VORTEX_DEBUG_INFO must be 0, 1, or 2") +endif() + +# Keep the override in Vortex's directories, including fetched dependencies, +# without changing unrelated targets in an embedding parent. +add_compile_options("$<$:-g${VORTEX_DEBUG_INFO}>") diff --git a/vortex-ffi/cmake/tests/test_compiler_commands.py b/vortex-ffi/cmake/tests/test_compiler_commands.py index cfad6b024cf..0d2a67c5da0 100644 --- a/vortex-ffi/cmake/tests/test_compiler_commands.py +++ b/vortex-ffi/cmake/tests/test_compiler_commands.py @@ -52,11 +52,20 @@ def setUp(self) -> None: self.command("cargo", "generate-lockfile", "--offline", cwd=self.source) def configure( - self, value=7, argument=7, policy=True, instrumentation=False, generator="Ninja", build_name=None + self, + value=7, + argument=7, + policy=True, + instrumentation=False, + generator="Ninja", + build_name=None, + debug_info=None, ) -> None: self.build_dir = self.work / (build_name or f"{generator} build directory's") self.target_dir = self.build_dir / "ffi/cargo-target" options = [] + if debug_info is not None: + options.append(f"-DVORTEX_DEBUG_INFO={debug_info}") include = self.source / "native-helper/include directory's" errors = ["-Werror", "-Werror=unused-variable", "-pedantic-errors"] if policy else [] for language, compiler in (("C", "clang"), ("CXX", "clang++")): @@ -151,12 +160,13 @@ def test_host_target_instrumentation_and_cache_boundary(self) -> None: os.execv(sys.argv[1], sys.argv[1:]) """, ) - self.configure(instrumentation=True) + self.configure(instrumentation=True, debug_info=0) self.build() calls = [json.loads(line) for line in log.read_text().splitlines()] self.assertEqual(sorted(target for target, _ in calls), [False, False, True, True]) for target, args in calls: self.assertIn(Path(args[0]).name, ("clang", "clang++")) + self.assertEqual([arg for arg in args if arg.startswith("-g")][-1:], ["-g0"], args) for flag in ("--coverage", "-fsanitize=undefined"): self.assertEqual(flag in args, target, args) objects = sorted(self.target_dir.rglob("out/*-native.o")) diff --git a/vortex-ffi/cmake/tests/test_configure.py b/vortex-ffi/cmake/tests/test_configure.py index a82b4e6739e..d7f4c6bf272 100644 --- a/vortex-ffi/cmake/tests/test_configure.py +++ b/vortex-ffi/cmake/tests/test_configure.py @@ -68,6 +68,7 @@ def test_standalone_defaults_and_ffi_default_build(self) -> None: self.assertEqual(settings[:2], ["Debug", "ON"]) if len(settings) == 3: self.assertIn("-Werror", settings[2].split(";")) + self.assertIn("$<$:-g2>", settings[2].split(";")) build = self.work / "ffi" self.cmake_build(build) @@ -76,8 +77,14 @@ def test_standalone_defaults_and_ffi_default_build(self) -> None: self.assertEqual(recorded["args"][recorded["args"].index("--profile") + 1], "dev") config = tomllib.loads((self.repo / ".cargo/config.toml").read_text()) expected = config["target"]['cfg(target_family="unix")']["rustflags"] + ["-C", "relocation-model=pic"] + expected += ["-C", "debuginfo=2"] self.assertEqual(recorded["env"]["CARGO_ENCODED_RUSTFLAGS"].split("\x1f"), expected) + self.configure("ffi", "-DVORTEX_DEBUG_INFO=0") + self.cmake_build(build) + recorded = self.cargo_recording(build / "cargo-target") + self.assertEqual(recorded["env"]["CARGO_ENCODED_RUSTFLAGS"].split("\x1f"), expected[:-1] + ["debuginfo=0"]) + def test_embedded_root_preserves_parent_variables(self) -> None: source = self.write( "parent/CMakeLists.txt",