@@ -255,18 +255,26 @@ class IdeVSCode(IdeBase):
launch_config['additionalSOLibSearchPath'] = modified_recipe.solib_search_path_str(
gdb_cross_config.image_recipe)
# First: Search for sources of this recipe in the workspace folder
- if modified_recipe.pn in modified_recipe.target_dbgsrc_dir:
- src_file_map[modified_recipe.target_dbgsrc_dir] = "${workspaceFolder}"
- else:
- logger.error(
- "TARGET_DBGSRC_DIR must contain the recipe name PN.")
+ # If compiled with DEBUG_PREFIX_MAP = "", no reverse map is is needed. The binaries
+ # contain the full path to the source files. But by default there is a reverse map.
+ for target_path, host_path in modified_recipe.reverse_debug_prefix_map.items():
+ if host_path.startswith(modified_recipe.real_srctree):
+ src_file_map[target_path] = "${workspaceFolder}" + host_path[len(modified_recipe.real_srctree):]
+ else:
+ src_file_map[target_path] = host_path
+
# Second: Search for sources of other recipes in the rootfs-dbg
- if modified_recipe.target_dbgsrc_dir.startswith("/usr/src/debug"):
+ # We expect that /usr/src/debug/${PN}/${PV} or no mapping is used for the recipes
+ # own sources and we can use the key "/usr/src/debug" for the rootfs-dbg.
+ if "/usr/src/debug" in src_file_map:
+ logger.error(
+ 'Key "/usr/src/debug" already exists in src_file_map. '
+ 'Something with DEBUG_PREFIX_MAP looks unexpected and finding '
+ 'sources in the rootfs-dbg will not work as expected.'
+ )
+ else:
src_file_map["/usr/src/debug"] = os.path.join(
gdb_cross_config.image_recipe.rootfs_dbg, "usr", "src", "debug")
- else:
- logger.error(
- "TARGET_DBGSRC_DIR must start with /usr/src/debug.")
else:
logger.warning(
"Cannot setup debug symbols configuration for GDB. IMAGE_GEN_DEBUGFS is not enabled.")
@@ -78,19 +78,25 @@ class GdbCrossConfigNone(GdbCrossConfig):
os.path.join(self.modified_recipe.recipe_sysroot, 'usr', 'include') + '"')
if self.image_recipe.rootfs_dbg:
# First: Search for sources of this recipe in the workspace folder
- if self.modified_recipe.pn in self.modified_recipe.target_dbgsrc_dir:
- gdbinit_lines.append('set substitute-path "%s" "%s"' %
- (self.modified_recipe.target_dbgsrc_dir, self.modified_recipe.real_srctree))
- else:
- logger.error(
- "TARGET_DBGSRC_DIR must contain the recipe name PN.")
+ # If compiled with DEBUG_PREFIX_MAP = "", no reverse map is is needed. The binaries
+ # contain the full path to the source files. But by default there is a reverse map.
+ src_file_map = dict(self.modified_recipe.reverse_debug_prefix_map)
+
# Second: Search for sources of other recipes in the rootfs-dbg
- if self.modified_recipe.target_dbgsrc_dir.startswith("/usr/src/debug"):
- gdbinit_lines.append('set substitute-path "/usr/src/debug" "%s"' % os.path.join(
- self.image_recipe.rootfs_dbg, "usr", "src", "debug"))
- else:
+ # We expect that /usr/src/debug/${PN}/${PV} or no mapping is used for the recipes
+ # own sources and we can use the key "/usr/src/debug" for the rootfs-dbg.
+ if "/usr/src/debug" in src_file_map:
logger.error(
- "TARGET_DBGSRC_DIR must start with /usr/src/debug.")
+ 'Key "/usr/src/debug" already exists in src_file_map. '
+ 'Something with DEBUG_PREFIX_MAP looks unexpected and finding sources '
+ 'in the rootfs-dbg will not work as expected.'
+ )
+ else:
+ src_file_map["/usr/src/debug"] = os.path.join(
+ self.image_recipe.rootfs_dbg, "usr", "src", "debug")
+
+ for target_path, host_path in src_file_map.items():
+ gdbinit_lines.append('set substitute-path "%s" "%s"' % (target_path, host_path))
else:
logger.warning(
"Cannot setup debug symbols configuration for GDB. IMAGE_GEN_DEBUGFS is not enabled.")
@@ -14,6 +14,7 @@ import shutil
import stat
import subprocess
import sys
+import shlex
from argparse import RawTextHelpFormatter
from enum import Enum
@@ -394,6 +395,7 @@ class RecipeModified:
self.bpn = None
self.d = None
self.debug_build = None
+ self.reverse_debug_prefix_map = {}
self.fakerootcmd = None
self.fakerootenv = None
self.libdir = None
@@ -404,10 +406,10 @@ class RecipeModified:
self.pn = None
self.recipe_sysroot = None
self.recipe_sysroot_native = None
+ self.s = None
self.staging_incdir = None
self.strip_cmd = None
self.target_arch = None
- self.target_dbgsrc_dir = None
self.topdir = None
self.workdir = None
self.recipe_id = None
@@ -478,13 +480,13 @@ class RecipeModified:
recipe_d.getVar('RECIPE_SYSROOT'))
self.recipe_sysroot_native = os.path.realpath(
recipe_d.getVar('RECIPE_SYSROOT_NATIVE'))
+ self.s = recipe_d.getVar('S')
self.staging_bindir_toolchain = os.path.realpath(
recipe_d.getVar('STAGING_BINDIR_TOOLCHAIN'))
self.staging_incdir = os.path.realpath(
recipe_d.getVar('STAGING_INCDIR'))
self.strip_cmd = recipe_d.getVar('STRIP')
self.target_arch = recipe_d.getVar('TARGET_ARCH')
- self.target_dbgsrc_dir = recipe_d.getVar('TARGET_DBGSRC_DIR')
self.topdir = recipe_d.getVar('TOPDIR')
self.workdir = os.path.realpath(recipe_d.getVar('WORKDIR'))
@@ -493,6 +495,9 @@ class RecipeModified:
self.f_do_install_dirs = recipe_d.getVarFlag(
'do_install', 'dirs').split()
+ self.reverse_debug_prefix_map = self.extract_debug_prefix_map_paths(
+ recipe_d.getVar('DEBUG_PREFIX_MAP'))
+
self.__init_exported_variables(recipe_d)
self.__init_systemd_services(recipe_d)
self.__init_init_scripts(recipe_d)
@@ -508,6 +513,9 @@ class RecipeModified:
self.meson_cross_file = recipe_d.getVar('MESON_CROSS_FILE')
self.build_tool = BuildTool.MESON
+ self.reverse_debug_prefix_map = self._init_reverse_debug_prefix_map(
+ recipe_d.getVar('DEBUG_PREFIX_MAP'))
+
# Recipe ID is the identifier for IDE config sections
self.recipe_id = self.bpn + "-" + self.package_arch
self.recipe_id_pretty = self.bpn + ": " + self.package_arch
@@ -567,6 +575,70 @@ class RecipeModified:
"""Return a : separated list of paths usable by GDB's set solib-search-path"""
return ':'.join(self.solib_search_path(image))
+ def _init_reverse_debug_prefix_map(self, debug_prefix_map):
+ """Parses GCC map options and returns a mapping of target to host paths.
+
+ This function scans a string containing GCC options such as -fdebug-prefix-map,
+ -fmacro-prefix-map, and -ffile-prefix-map (in both '--option value' and '--option=value'
+ forms), extracting all mappings from target paths (used in debug info) to host source
+ paths. If multiple mappings for the same target path are found, the most suitable
+ host path is selected (preferring 'sources' over 'build' directories).
+ """
+ prefixes = ("-fdebug-prefix-map", "-fmacro-prefix-map", "-ffile-prefix-map")
+ all_mappings = {}
+ args = shlex.split(debug_prefix_map)
+ i = 0
+
+ # Collect all mappings, storing potentially multiple host paths per target path
+ while i < len(args):
+ arg = args[i]
+ mapping = None
+ for prefix in prefixes:
+ if arg == prefix:
+ i += 1
+ mapping = args[i]
+ break
+ elif arg.startswith(prefix + '='):
+ mapping = arg[len(prefix)+1:]
+ break
+ if mapping:
+ host_path, target_path = mapping.split('=', 1)
+ if target_path:
+ if target_path not in all_mappings:
+ all_mappings[target_path] = []
+ all_mappings[target_path].append(os.path.realpath(host_path))
+ i += 1
+
+ # Select the best host path for each target path (only 1:1 mappings are supported by GDB)
+ mappings = {}
+ unused_host_paths = []
+ for target_path, host_paths in all_mappings.items():
+ if len(host_paths) == 1:
+ mappings[target_path] = host_paths[0]
+ else:
+ # First priority path for sources is the source directory S
+ # Second priority path is any other directory
+ # Least priority is the build directory B, which probably contains only generated source files
+ sources_paths = [path for path in host_paths if os.path.realpath(path).startswith(os.path.realpath(self.s))]
+ if sources_paths:
+ mappings[target_path] = sources_paths[0]
+ unused_host_paths.extend([path for path in host_paths if path != sources_paths[0]])
+ else:
+ # If no 'sources' path, prefer non-'build' paths
+ non_build_paths = [path for path in host_paths if not os.path.realpath(path).startswith(os.path.realpath(self.b))]
+ if non_build_paths:
+ mappings[target_path] = non_build_paths[0]
+ unused_host_paths.extend([path for path in host_paths if path != non_build_paths[0]])
+ else:
+ # Fall back to first path if all are build paths
+ mappings[target_path] = host_paths[0]
+ unused_host_paths.extend(host_paths[1:])
+
+ if unused_host_paths:
+ logger.info("Some source directories mapped by -fdebug-prefix-map are not included in the debugger search paths. Ignored host paths: %s", unused_host_paths)
+
+ return mappings
+
def __init_exported_variables(self, d):
"""Find all variables with export flag set.
@@ -971,7 +971,12 @@ def modify(args, config, basepath, workspace):
continue
f.write('# patches_%s: %s\n' % (branch, ','.join(branch_patches[branch])))
if args.debug_build:
- f.write('\nDEBUG_BUILD = "1"\n')
+ f.write('\n# Optimize for debugging. Use e.g. -Og instead of -O2\n')
+ f.write('DEBUG_BUILD = "1"\n')
+ f.write('\n# Keep the paths to the source files for remote debugging\n')
+ f.write('# DEBUG_PREFIX_MAP = ""\n')
+ f.write('# WARN_QA:remove = "buildpaths"\n')
+ f.write('# ERROR_QA:remove = "buildpaths"\n')
update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn])