SERVER-118928 improve gdb toolchain wrapper (#48756)

GitOrigin-RevId: 241c32df8634e9058e8c62999979be106e1f05b7
This commit is contained in:
Daniel Moody
2026-02-27 10:35:08 -06:00
committed by MongoDB Bot
parent 9c6da8a82d
commit f4a8881826
4 changed files with 203 additions and 17 deletions

2
MODULE.bazel.lock generated
View File

@@ -237,7 +237,7 @@
"moduleExtensions": {
"//bazel:bzlmod.bzl%setup_mongo_python_toolchains": {
"general": {
"bzlTransitiveDigest": "d7d7d90UiWbgNAe+UNjVp0658ntOeokEMK2OPybABow=",
"bzlTransitiveDigest": "idv0XYvsT1OQdB9a7uhWyDT9+BJ+bHIqCuxGgcyyLUg=",
"usagesDigest": "bUxjq9n+hj2YwYT/lcSP4lHyQ2GVy5JpFgSmddUqUZg=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},

View File

@@ -1,3 +1,5 @@
"""Repository rule for MongoDB's GDB wrapper/toolchain."""
load("//bazel/toolchains/cc/mongo_linux:mongo_gdb_version_v5.bzl", "TOOLCHAIN_MAP_V5")
load("//bazel:utils.bzl", "generate_noop_toolchain", "get_toolchain_subs", "retry_download_and_extract", "write_python_pyc_cache_prefix_customization")
@@ -35,12 +37,12 @@ def _gdb_download(ctx):
external = str(ctx.path(".."))
pythonhome = external + "/gdb_" + ctx.attr.version + "/stow/python3-" + ctx.attr.version
gdbhome = external + "/gdb_" + ctx.attr.version + "/stow/gdb-" + ctx.attr.version
gdb_prefix = external + "/gdb_" + ctx.attr.version + "/" + ctx.attr.version
mongodb_toolchain_path = external + "/mongo_toolchain_" + ctx.attr.version
stdlib_pp_dir = mongodb_toolchain_path + "/stow/gcc-" + ctx.attr.version + "/share"
readelf = mongodb_toolchain_path + "/" + ctx.attr.version + "/bin/llvm-readelf"
objcopy = mongodb_toolchain_path + "/" + ctx.attr.version + "/bin/llvm-objcopy"
if "amazon_linux_2" == distro:
# our toolchain python version requires newer openssl, which is not available on AL2
@@ -84,8 +86,7 @@ def _gdb_download(ctx):
if ctx.getenv("CI"):
fail("Failed to install python module:\n" + result.stdout + "\n" + result.stderr)
else:
print("Failed to install python module:\n" + result.stdout + "\n" + result.stderr)
print("This means some pretty printer functions will not work while debugging.")
ctx.report_progress("Failed to install python module; some pretty printer functions may not work while debugging.\nSTDOUT:\n{}\nSTDERR:\n{}".format(result.stdout, result.stderr))
python_env = """{
"PYTHONPATH": "%s/lib/python3.10",
@@ -93,7 +94,21 @@ def _gdb_download(ctx):
"LD_LIBRARY_PATH": "%s",
"MONGO_GDB_PP_DIR": "%s",
"MONGO_GDB_READELF": "%s",
}""" % (pythonhome, pythonhome, python_lib_path, stdlib_pp_dir, readelf)
"READELF": "%s",
"OBJCOPY": "%s",
"MONGO_GDB_OBJCOPY": "%s",
"GDB": "%s/bin/gdb",
}""" % (
pythonhome,
pythonhome,
python_lib_path,
stdlib_pp_dir,
readelf,
readelf,
objcopy,
objcopy,
gdb_prefix,
)
# The wrapper scripts must also export these so gdb can load its python runtime (and pretty printers)
# when invoked via bazel run/test.
@@ -112,6 +127,63 @@ GDBHOME="${RUNFILES_WORKING_DIRECTORY}/../gdb_%s/stow/gdb-%s"
export LD_LIBRARY_PATH="${GDB_PREFIX}/lib:${GDBHOME}/lib:${LD_LIBRARY_PATH:-}"
""" % (ctx.attr.version, ctx.attr.version, ctx.attr.version, ctx.attr.version)
# Ensure GDB (and our in-GDB python helpers) use binutils that match the MongoDB toolchain.
#
# Some GDB operations call out to external binutils (e.g. readelf/objcopy). When invoked via
# `bazel run`/`bazel test`, we want these to come from the same mongo toolchain version used
# by the build, not the host OS.
wrapper_binutils_setup = """
# Prefer resolving runfiles via manifest (works for `bazel run` and `bazel test`).
RUNFILES_MANIFEST="${RUNFILES_MANIFEST_FILE:-${0}.runfiles_manifest}"
if [ -f "${RUNFILES_MANIFEST}" ]; then
rlocation() {
# shellcheck disable=SC2016
awk -v k="$1" '$1 == k { print $2; exit }' "${RUNFILES_MANIFEST}"
}
else
rlocation() {
echo ""
}
fi
READELF="$(rlocation mongo_toolchain_%s/%s/bin/llvm-readelf)"
if [ -z "${READELF}" ] || [ ! -x "${READELF}" ]; then
READELF="$(rlocation mongo_toolchain_%s/%s/bin/readelf)"
fi
if [ -z "${READELF}" ] || [ ! -x "${READELF}" ]; then
READELF="readelf"
fi
export READELF
export MONGO_GDB_READELF="${READELF}"
OBJCOPY="$(rlocation mongo_toolchain_%s/%s/bin/llvm-objcopy)"
if [ -z "${OBJCOPY}" ] || [ ! -x "${OBJCOPY}" ]; then
OBJCOPY="$(rlocation mongo_toolchain_%s/%s/bin/objcopy)"
fi
if [ -z "${OBJCOPY}" ] || [ ! -x "${OBJCOPY}" ]; then
OBJCOPY="objcopy"
fi
export OBJCOPY
GDB="$(rlocation gdb_%s/%s/bin/gdb)"
if [ ! -x "${GDB}" ]; then
# Best-effort fallback; the wrapper still execs a concrete gdb path below.
GDB="gdb"
fi
export GDB
""" % (
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
)
ctx.file(
"BUILD.bazel",
"""
@@ -137,6 +209,7 @@ sh_binary(
"%s/bin/gdb",
":gdb_runtime",
":python_runtime",
"%s",
],
env = %s,
visibility = ["//visibility:public"],
@@ -150,10 +223,36 @@ sh_binary(
"%s/bin/gdbserver",
":gdb_runtime",
":python_runtime",
"%s",
],
visibility = ["//visibility:public"],
)
""" % (ctx.attr.version, ctx.attr.version, ctx.attr.version, ctx.attr.version, python_env, ctx.attr.version),
sh_binary(
name = "gdb-add-index",
srcs = ["working_dir_gdb_add_index.sh"],
data = [
"%s/bin/gdb",
"%s/bin/gdb-add-index",
":gdb_runtime",
":python_runtime",
"%s",
],
visibility = ["//visibility:public"],
)
""" % (
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.version,
ctx.attr.mongo_toolchain,
python_env,
ctx.attr.version,
ctx.attr.mongo_toolchain,
ctx.attr.version,
ctx.attr.version,
ctx.attr.mongo_toolchain,
),
)
ctx.file(
@@ -173,8 +272,9 @@ fi
cd $BUILD_WORKING_DIRECTORY
%s
%s
%s
${RUNFILES_WORKING_DIRECTORY}/../gdb_%s/%s/bin/gdb -iex "set auto-load safe-path %s/.gdbinit" "${@:1}"
""" % (wrapper_gdb_setup, wrapper_python_setup, ctx.attr.version, ctx.attr.version, str(ctx.workspace_root)),
""" % (wrapper_gdb_setup, wrapper_binutils_setup, wrapper_python_setup, ctx.attr.version, ctx.attr.version, str(ctx.workspace_root)),
)
ctx.file(
@@ -197,8 +297,31 @@ cd $BUILD_WORKING_DIRECTORY
original_args="${@:1}"
%s
%s
%s
${RUNFILES_WORKING_DIRECTORY}/external/gdb_%s/%s/bin/gdbserver localhost:1234 ${TEST_SRCDIR}/_main/${original_args[0]} "${@:2}"
""" % (wrapper_gdb_setup, wrapper_python_setup, ctx.attr.version, ctx.attr.version),
""" % (wrapper_gdb_setup, wrapper_binutils_setup, wrapper_python_setup, ctx.attr.version, ctx.attr.version),
)
ctx.file(
"working_dir_gdb_add_index.sh",
"""
#!/bin/bash
set -e
RUNFILES_WORKING_DIRECTORY="$(pwd)"
if [ -z $BUILD_WORKING_DIRECTORY ]; then
echo "ERROR: BUILD_WORKING_DIRECTORY was not set, was this run from bazel?"
exit 1
fi
cd $BUILD_WORKING_DIRECTORY
%s
%s
%s
${RUNFILES_WORKING_DIRECTORY}/../gdb_%s/%s/bin/gdb-add-index "${@:1}"
""" % (wrapper_gdb_setup, wrapper_binutils_setup, wrapper_python_setup, ctx.attr.version, ctx.attr.version),
)
return None
@@ -225,10 +348,16 @@ def setup_gdb_toolchains():
gdb_v5_download(
name = "gdb_v5",
version = "v5",
mongo_toolchain = "@mongo_toolchain_v5//:all",
mongo_toolchain = "@mongo_toolchain_v5//:all_files",
)
def setup_gdb_toolchain_aliases(name = "setup_toolchains"):
"""Create unversioned and versioned gdb aliases.
Args:
name: Unused. Present to match other setup_*_aliases() signatures.
"""
# v5 is the default version we currently use, so we name it unversioned
native.alias(
name = "gdb",
@@ -238,6 +367,10 @@ def setup_gdb_toolchain_aliases(name = "setup_toolchains"):
name = "gdbserver",
actual = "@gdb_v5//:gdbserver",
)
native.alias(
name = "gdb-add-index",
actual = "@gdb_v5//:gdb-add-index",
)
native.alias(
name = "gdb_v5",
@@ -247,3 +380,7 @@ def setup_gdb_toolchain_aliases(name = "setup_toolchains"):
name = "gdbserver_v5",
actual = "@gdb_v5//:gdbserver",
)
native.alias(
name = "gdb-add-index_v5",
actual = "@gdb_v5//:gdb-add-index",
)

View File

@@ -1,3 +1,5 @@
"""Repository rule for MongoDB's C/C++ toolchain."""
load("//bazel:utils.bzl", "generate_noop_toolchain", "get_toolchain_subs", "retry_download_and_extract")
load("//bazel/toolchains/cc/mongo_linux:mongo_toolchain_version.bzl", "TOOLCHAIN_MAP")
load("//bazel/toolchains/cc/mongo_linux:mongo_mold.bzl", "MOLD_MAP")
@@ -10,7 +12,7 @@ def _toolchain_download(ctx):
skip_toolchain = ctx.os.environ.get(SKIP_TOOLCHAIN_ENVIRONMENT_VARIABLE, None)
if skip_toolchain:
generate_noop_toolchain(ctx, substitutions)
print("Skipping c++ toolchain download and defining noop toolchain due to " + SKIP_TOOLCHAIN_ENVIRONMENT_VARIABLE + " being defined.")
ctx.report_progress("Skipping c++ toolchain download and defining noop toolchain due to {} being defined.".format(SKIP_TOOLCHAIN_ENVIRONMENT_VARIABLE))
return None
toolchain_key = "{distro}_{arch}".format(distro = distro, arch = arch)
@@ -21,7 +23,6 @@ def _toolchain_download(ctx):
sha = toolchain_info["sha"]
ctx.report_progress("downloading {} mongo toolchain {}".format(toolchain_key, urls))
print("downloading {} mongo toolchain {}".format(toolchain_key, urls))
retry_download_and_extract(
ctx = ctx,
tries = 5,
@@ -30,7 +31,7 @@ def _toolchain_download(ctx):
)
if arch in MOLD_MAP:
print("Downloading mold from {}.".format(MOLD_MAP[arch]["url"]))
ctx.report_progress("Downloading mold from {}.".format(MOLD_MAP[arch]["url"]))
retry_download_and_extract(
ctx = ctx,
tries = 5,
@@ -84,7 +85,12 @@ toolchain_download = repository_rule(
},
)
def setup_mongo_toolchains():
def setup_mongo_toolchains(name = "setup_toolchains"):
"""Download/register the MongoDB C/C++ toolchain repositories.
Args:
name: Unused. Present to match public macro conventions.
"""
toolchain_download(
name = "mongo_toolchain_v5",
version = "v5",
@@ -92,7 +98,7 @@ def setup_mongo_toolchains():
)
native.register_toolchains(
"@mongo_toolchain_v5//:all",
"@mongo_toolchain_v5//:mongo_toolchain",
)
# Defines aliases for key targets inside the toolchain the user has chosen via
@@ -100,7 +106,13 @@ def setup_mongo_toolchains():
# toolchains, so this function defines an alias called clang_tidy that points to
# @mongo_toolchain_vN//:clang_tidy, where N depends on the config value. This lets us use
# the name //:clang_tidy to refer to the clang_tidy of the configured toolchain.
def setup_mongo_toolchain_aliases():
def setup_mongo_toolchain_aliases(name = "setup_aliases"):
"""Create aliases to tools within the configured mongo toolchain.
Args:
name: Unused. Present to match public macro conventions.
"""
# Map from target's name inside the toolchain to the name we want to alias it to.
toolchain_targets = {
"llvm_symbolizer": "llvm_symbolizer",

View File

@@ -69,10 +69,47 @@ sys.pycache_prefix = os.path.join(tempfile.gettempdir(), "{pycache_dirname}")
)
def generate_noop_toolchain(ctx, substitutions):
# BUILD file is required for a no-op
# BUILD file is required for a no-op.
# Keep a stub mongo_toolchain target so unconditional register_toolchains()
# calls don't fail when the toolchain is intentionally skipped/unsupported.
ctx.file(
"BUILD.bazel",
"# {} not supported on this platform".format(ctx.attr.version),
"""
# {} not supported on this platform
filegroup(
name = "all_files",
srcs = [],
)
filegroup(
name = "clang_tidy",
srcs = [],
)
filegroup(
name = "clang_format",
srcs = [],
)
filegroup(
name = "llvm_symbolizer",
srcs = [],
)
filegroup(
name = "llvm_symbolizer_libs",
srcs = [],
)
toolchain(
name = "mongo_toolchain",
exec_compatible_with = ["@platforms//:incompatible"],
target_compatible_with = ["@platforms//:incompatible"],
toolchain = "@bazel_tools//tools/cpp:current_cc_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
)
""".format(ctx.attr.version),
)
def get_toolchain_subs(ctx):