[Lldb-commits] [lldb] [PrefixMap] Teach lldb to auto-load compilation-prefix-map.json (PR #187145)
Steven Wu via lldb-commits
lldb-commits at lists.llvm.org
Tue Mar 17 15:49:08 PDT 2026
https://github.com/cachemeifyoucan created https://github.com/llvm/llvm-project/pull/187145
Add a LoadCompilationPrefixMap() helper in SymbolFile::FindPlugin that
walks up from the symbol file's directory looking for a
compilation-prefix-map.json file. When found, each key→value entry is
applied to the module's source path mapping list, allowing LLDB to
resolve source file paths that were rewritten by -fdebug-prefix-map at
build time without requiring manual `settings set target.source-map`.
The JSON file format maps fake paths (as written into debug info) back
to their real on-disk counterparts:
{ "/fake/srcdir": "/real/srcdir" }
Directory results are cached so the filesystem is walked at most once
per unique directory across all modules loaded in a session.
Also apply the module's source path remappings in
SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex when constructing
compile units from N_SO stabs. This mirrors what MakeAbsoluteAndRemap
does for the dSYM case so that fake paths baked into the debug map are
transparently resolved to real paths.
rdar://84824567
Assisted-By: Claude
>From f377a68f09a329e7838df0392fa61ab179b1aee0 Mon Sep 17 00:00:00 2001
From: Steven Wu <stevenwu at apple.com>
Date: Tue, 17 Mar 2026 15:48:56 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
=?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
---
lldb/include/lldb/Core/Module.h | 18 ++++-
lldb/source/Core/Module.cpp | 65 ++++++++++++++++++-
.../DWARF/SymbolFileDWARFDebugMap.cpp | 9 +++
lldb/source/Symbol/SymbolFile.cpp | 11 ++++
.../compilation-prefix-map/Makefile | 9 +++
.../TestCompilationPrefixMap.py | 41 ++++++++++++
.../compilation-prefix-map/main.c | 7 ++
7 files changed, 155 insertions(+), 5 deletions(-)
create mode 100644 lldb/test/API/functionalities/compilation-prefix-map/Makefile
create mode 100644 lldb/test/API/functionalities/compilation-prefix-map/TestCompilationPrefixMap.py
create mode 100644 lldb/test/API/functionalities/compilation-prefix-map/main.c
diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h
index ea875ac68cde2..cfe006629ae99 100644
--- a/lldb/include/lldb/Core/Module.h
+++ b/lldb/include/lldb/Core/Module.h
@@ -845,7 +845,7 @@ class Module : public std::enable_shared_from_this<Module>,
/// /b true if \a orig_spec was successfully located and
/// \a new_spec is filled in with an existing file spec,
/// \b false otherwise.
- bool FindSourceFile(const FileSpec &orig_spec, FileSpec &new_spec) const;
+ bool FindSourceFile(const FileSpec &orig_spec, FileSpec &new_spec);
/// Remaps a source file given \a path into \a new_path.
///
@@ -859,9 +859,14 @@ class Module : public std::enable_shared_from_this<Module>,
/// \return
/// The newly remapped filespec that is may or may not exist if
/// \a path was successfully located.
- std::optional<std::string> RemapSourceFile(llvm::StringRef path) const;
+ std::optional<std::string> RemapSourceFile(llvm::StringRef path);
bool RemapSourceFile(const char *, std::string &) const = delete;
+ /// Register a directory to be searched for \c compilation-prefix-map.json
+ /// on the first call to RemapSourceFile or FindSourceFile. Duplicate
+ /// directories are silently ignored.
+ void AddPrefixMapSearchDir(FileSpec dir);
+
/// Update the ArchSpec to a more specific variant.
bool MergeArchitecture(const ArchSpec &arch_spec);
@@ -1068,6 +1073,15 @@ class Module : public std::enable_shared_from_this<Module>,
PathMappingList m_source_mappings =
ModuleList::GetGlobalModuleListProperties().GetSymlinkMappings();
+ /// Directories registered via AddPrefixMapSearchDir, searched lazily on the
+ /// first call to RemapSourceFile or FindSourceFile. Cleared after searching.
+ llvm::DenseSet<ConstString> m_prefix_map_search_dirs;
+
+ /// Search each registered directory upward for compilation-prefix-map.json
+ /// and apply any found mappings to m_source_mappings. Called at most once.
+ /// Must be called with m_mutex held.
+ void LoadPrefixMapsIfNeeded();
+
lldb::SectionListUP m_sections_up; ///< Unified section list for module that
/// is used by the ObjectFile and
/// ObjectFile instances for the debug info
diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index 79618ab91e27a..79a43258aa36f 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -58,7 +58,9 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
@@ -1553,9 +1555,9 @@ bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) {
return true;
}
-bool Module::FindSourceFile(const FileSpec &orig_spec,
- FileSpec &new_spec) const {
+bool Module::FindSourceFile(const FileSpec &orig_spec, FileSpec &new_spec) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ LoadPrefixMapsIfNeeded();
if (auto remapped = m_source_mappings.FindFile(orig_spec)) {
new_spec = *remapped;
return true;
@@ -1563,8 +1565,65 @@ bool Module::FindSourceFile(const FileSpec &orig_spec,
return false;
}
-std::optional<std::string> Module::RemapSourceFile(llvm::StringRef path) const {
+void Module::AddPrefixMapSearchDir(FileSpec dir) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_prefix_map_search_dirs.insert(ConstString(dir.GetPath()));
+}
+
+void Module::LoadPrefixMapsIfNeeded() {
+ // Must be called with m_mutex held.
+ if (m_prefix_map_search_dirs.empty())
+ return;
+
+ Log *log = GetLog(LLDBLog::Symbols);
+ llvm::vfs::FileSystem &vfs = *llvm::vfs::getRealFileSystem();
+ // Track visited directories so two starting paths that share ancestors
+ // don't redundantly walk the same directory.
+ llvm::DenseSet<ConstString> searched;
+ for (ConstString start_cs : m_prefix_map_search_dirs) {
+ for (FileSpec d(start_cs.GetStringRef());;) {
+ ConstString d_cs(d.GetPath());
+ if (!searched.insert(d_cs).second)
+ break;
+ FileSpec jp(d);
+ jp.AppendPathComponent("compilation-prefix-map.json");
+ llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> file =
+ vfs.openFileForRead(jp.GetPath());
+ if (file && *file) {
+ LLDB_LOG(log, "found compilation-prefix-map.json at {0}", jp.GetPath());
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buf =
+ (*file)->getBuffer(jp.GetPath());
+ if (buf && *buf) {
+ llvm::Expected<llvm::json::Value> val =
+ llvm::json::parse((*buf)->getBuffer());
+ if (val) {
+ if (llvm::json::Object *obj = val->getAsObject()) {
+ for (const llvm::json::Object::value_type &kv : *obj)
+ if (std::optional<llvm::StringRef> to =
+ kv.second.getAsString()) {
+ LLDB_LOG(log, "applying prefix map: '{0}' -> '{1}'", kv.first,
+ *to);
+ m_source_mappings.AppendUnique(kv.first.str(), to->str(),
+ /*notify=*/false);
+ }
+ }
+ }
+ }
+ break;
+ }
+ FileSpec parent = d;
+ parent.RemoveLastPathComponent();
+ if (parent == d)
+ break;
+ d = parent;
+ }
+ }
+ m_prefix_map_search_dirs.clear();
+}
+
+std::optional<std::string> Module::RemapSourceFile(llvm::StringRef path) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ LoadPrefixMapsIfNeeded();
if (auto remapped = m_source_mappings.RemapPath(path))
return remapped->GetPath();
return {};
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index 1553fcbc831a9..fd2da1ea46fec 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -600,6 +600,15 @@ CompUnitSP SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) {
if (oso_module) {
FileSpec so_file_spec;
if (GetFileSpecForSO(cu_idx, so_file_spec)) {
+ // Apply the module's source path remappings so that compile units
+ // created from N_SO stabs (which may contain paths rewritten by
+ // -fdebug-prefix-map at build time) report their real on-disk paths.
+ // This mirrors what MakeAbsoluteAndRemap does for the dSYM case.
+ if (ModuleSP module_sp = m_objfile_sp->GetModule())
+ if (auto remapped =
+ module_sp->RemapSourceFile(so_file_spec.GetPath()))
+ so_file_spec.SetFile(*remapped, FileSpec::Style::native);
+
// User zero as the ID to match the compile unit at offset zero in each
// .o file.
lldb::user_id_t cu_id = 0;
diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp
index 7c2b499c97b4c..0ef139b1d453a 100644
--- a/lldb/source/Symbol/SymbolFile.cpp
+++ b/lldb/source/Symbol/SymbolFile.cpp
@@ -16,6 +16,7 @@
#include "lldb/Symbol/TypeMap.h"
#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StructuredData.h"
@@ -96,6 +97,16 @@ SymbolFile *SymbolFile::FindPlugin(ObjectFileSP objfile_sp) {
// Let the winning symbol file parser initialize itself more completely
// now that it has been chosen
best_symfile_up->InitializeObject();
+
+ // Register the object file's directory so the module can lazily search
+ // for a compilation-prefix-map.json when source paths are first remapped.
+ if (ObjectFile *obj = best_symfile_up->GetMainObjectFile())
+ if (ModuleSP mod = obj->GetModule()) {
+ FileSpec dir = obj->GetFileSpec();
+ dir.ClearFilename();
+ if (dir)
+ mod->AddPrefixMapSearchDir(std::move(dir));
+ }
}
}
return best_symfile_up.release();
diff --git a/lldb/test/API/functionalities/compilation-prefix-map/Makefile b/lldb/test/API/functionalities/compilation-prefix-map/Makefile
new file mode 100644
index 0000000000000..ebf94eff13390
--- /dev/null
+++ b/lldb/test/API/functionalities/compilation-prefix-map/Makefile
@@ -0,0 +1,9 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS = -fdebug-prefix-map=$(SRCDIR)=/fake/srcdir
+
+all: $(EXE) compilation-prefix-map.json
+
+include Makefile.rules
+
+compilation-prefix-map.json:
+ printf '{ "/fake/srcdir": "%s" }' "$(SRCDIR)" > $(BUILDDIR)/compilation-prefix-map.json
diff --git a/lldb/test/API/functionalities/compilation-prefix-map/TestCompilationPrefixMap.py b/lldb/test/API/functionalities/compilation-prefix-map/TestCompilationPrefixMap.py
new file mode 100644
index 0000000000000..9ea2d9813ddc3
--- /dev/null
+++ b/lldb/test/API/functionalities/compilation-prefix-map/TestCompilationPrefixMap.py
@@ -0,0 +1,41 @@
+# TestCompilationPrefixMap.py
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+"""
+Test that LLDB auto-loads compilation-prefix-map.json to resolve remapped
+source paths without requiring manual `settings set target.source-map`.
+"""
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+
+
+class TestCompilationPrefixMap(TestBase):
+ @skipIfWindows
+ def test_compilation_prefix_map(self):
+ """
+ Build a binary with -fdebug-prefix-map remapping the source directory
+ to /fake/srcdir, place compilation-prefix-map.json next to the binary
+ mapping /fake/srcdir back to the real source directory, and verify that
+ LLDB resolves a source-line breakpoint without any manual source-map
+ configuration.
+ """
+ self.build()
+
+ src_dir = self.getSourceDir()
+
+ log = self.getBuildArtifact("symbol.log")
+ self.runCmd('log enable lldb symbol -f "%s"' % log)
+
+ source_spec = lldb.SBFileSpec(os.path.join(src_dir, "main.c"))
+ lldbutil.run_to_source_breakpoint(self, "return x", source_spec)
+
+ self.filecheck_log(log, __file__)
+# CHECK: found compilation-prefix-map.json
+# CHECK: applying prefix map: '/fake/srcdir'
diff --git a/lldb/test/API/functionalities/compilation-prefix-map/main.c b/lldb/test/API/functionalities/compilation-prefix-map/main.c
new file mode 100644
index 0000000000000..0f34f1945b09a
--- /dev/null
+++ b/lldb/test/API/functionalities/compilation-prefix-map/main.c
@@ -0,0 +1,7 @@
+int add(int x, int y) {
+ return x + y;
+}
+
+int main() {
+ return add(1, 2) - 3;
+}
More information about the lldb-commits
mailing list