[Lldb-commits] [lldb] [LLDB][NativePDB] Look for PDBs in `target.debug-file-search-paths` (PR #169719)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Dec 1 07:39:23 PST 2025
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/169719
>From 4cde00bf2947b8b10f0579f76d519c3af17bcb58 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Wed, 26 Nov 2025 20:43:32 +0100
Subject: [PATCH 1/2] [LLDB][NativePDB] Look for PDB in
`target.debug-file-search-paths`
---
.../NativePDB/SymbolFileNativePDB.cpp | 52 +++++++++---
.../NativePDB/find-pdb-next-to-exe.test | 81 +++++++++++++++++++
2 files changed, 124 insertions(+), 9 deletions(-)
create mode 100644 lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index aaec1600dacff..4ee1b0759a10e 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -86,6 +86,40 @@ static lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
}
}
+static std::optional<std::string>
+findMatchingPDBFilePath(llvm::StringRef original_pdb_path,
+ llvm::StringRef exe_path) {
+ const FileSystem &fs = FileSystem::Instance();
+
+ if (fs.Exists(original_pdb_path))
+ return std::string(original_pdb_path);
+
+ const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent();
+ // While the exe_path uses the native style, the exe might be compiled on a
+ // different OS, so try to guess the style used.
+ const FileSpec original_pdb_spec(original_pdb_path,
+ FileSpec::GuessPathStyle(original_pdb_path)
+ .value_or(FileSpec::Style::native));
+ const llvm::StringRef pdb_filename = original_pdb_spec.GetFilename();
+
+ // If the file doesn't exist, perhaps the path specified at build time
+ // doesn't match the PDB's current location, so check the location of the
+ // executable.
+ const FileSpec local_pdb = exe_dir.CopyByAppendingPathComponent(pdb_filename);
+ if (fs.Exists(local_pdb))
+ return local_pdb.GetPath();
+
+ // Otherwise, search for one in target.debug-file-search-paths
+ FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
+ for (const FileSpec &search_dir : search_paths) {
+ FileSpec pdb_path = search_dir.CopyByAppendingPathComponent(pdb_filename);
+ if (fs.Exists(pdb_path))
+ return pdb_path.GetPath();
+ }
+
+ return std::nullopt;
+}
+
static std::unique_ptr<PDBFile>
loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
// Try to find a matching PDB for an EXE.
@@ -113,17 +147,14 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
return nullptr;
}
- // If the file doesn't exist, perhaps the path specified at build time
- // doesn't match the PDB's current location, so check the location of the
- // executable.
- if (!FileSystem::Instance().Exists(pdb_file)) {
- const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent();
- const auto pdb_name = FileSpec(pdb_file).GetFilename().GetCString();
- pdb_file = exe_dir.CopyByAppendingPathComponent(pdb_name).GetPathAsConstString().GetStringRef();
- }
+ std::optional<std::string> resolved_pdb_path =
+ findMatchingPDBFilePath(pdb_file, exe_path);
+ if (!resolved_pdb_path)
+ return nullptr;
// If the file is not a PDB or if it doesn't have a matching GUID, fail.
- auto pdb = ObjectFilePDB::loadPDBFile(std::string(pdb_file), allocator);
+ auto pdb =
+ ObjectFilePDB::loadPDBFile(*std::move(resolved_pdb_path), allocator);
if (!pdb)
return nullptr;
@@ -137,6 +168,9 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
if (expected_info->getGuid() != guid)
return nullptr;
+
+ LLDB_LOG(GetLog(LLDBLog::Symbols), "Loading {0} for {1}", pdb->getFilePath(),
+ exe_path);
return pdb;
}
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
new file mode 100644
index 0000000000000..9ca850b1fd5b6
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
@@ -0,0 +1,81 @@
+# REQUIRES: lld, target-windows
+
+# Test where LLDB looks for PDBs.
+# RUN: split-file %s %t
+
+# RUN: mkdir -p %t/build
+# RUN: mkdir -p %t/dir1
+# RUN: mkdir -p %t/dir2
+# RUN: mkdir -p %t/dir3
+
+# RUN: echo "settings append target.debug-file-search-paths %t/dir2" >> %t/init.input
+# RUN: echo "settings append target.debug-file-search-paths %t/dir3" >> %t/init.input
+
+# RUN: %build --compiler=clang-cl --nodefaultlib --output=%t/build/a.exe %t/main.cpp
+
+# Regular setup - PDB is at the original path
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/build/a.exe > %t.out.txt
+# CHECK: (lldb) target create
+# CHECK-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}build{{[/\\]}}a.exe
+# CHECK: (A) a = (x = 47)
+
+# Move the executable to a different directory but keep the PDB.
+# RUN: mv %t/build/a.exe %t/dir1
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
+# CHECK: (lldb) target create
+# CHECK-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# CHECK: (A) a = (x = 47)
+
+# Copy the PDB to the same directory and all search dirs. LLDB should prefer the original PDB.
+# RUN: cp %t/build/a.pdb %t/dir1
+# RUN: cp %t/build/a.pdb %t/dir2
+# RUN: cp %t/build/a.pdb %t/dir3
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
+# CHECK: (lldb) target create
+# CHECK-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# CHECK: (A) a = (x = 47)
+
+# Remove the original PDB. LLDB should now use the one next to the exe.
+# RUN: rm %t/build/a.pdb
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
+# CHECK: (lldb) target create
+# CHECK-NEXT: Loading {{.*[/\\]}}dir1{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# CHECK: (A) a = (x = 47)
+
+# Remove the PDB next to the exe. LLDB should now use the one in dir2 (first in list).
+# RUN: rm %t/dir1/a.pdb
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
+# CHECK: (lldb) target create
+# CHECK-NEXT: Loading {{.*[/\\]}}dir2{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# CHECK: (A) a = (x = 47)
+
+# Remove the PDB in dir2. LLDB should now use the one in dir3 (second in list).
+# RUN: rm %t/dir2/a.pdb
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
+# CHECK: (lldb) target create
+# CHECK-NEXT: Loading {{.*[/\\]}}dir3{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# CHECK: (A) a = (x = 47)
+
+# RUN: cat %t.out.txt | FileCheck %s
+
+# Remove the last PDB in dir3. Now, there's no matching PDB anymore.
+# RUN: rm %t/dir3/a.pdb
+# RUN: %lldb -S %t/init.input -s %t/check.input -f %t/dir1/a.exe 2>&1 | FileCheck --check-prefix=NOPDB %s
+# NOPDB: error: can't find global variable 'a'
+
+#--- main.cpp
+
+struct A {
+ int x = 47;
+};
+A a;
+int main() {}
+
+#--- init.input
+
+log enable lldb symbol
+
+#--- check.input
+
+target variable a
+q
>From bbb689888a74d8e3e625e9ed229f7ab16e933f18 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Mon, 1 Dec 2025 16:39:02 +0100
Subject: [PATCH 2/2] fix: use individual check prefixes
---
.../NativePDB/find-pdb-next-to-exe.test | 47 +++++++++----------
1 file changed, 21 insertions(+), 26 deletions(-)
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
index 9ca850b1fd5b6..c35c82ad84d2f 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
+++ b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
@@ -14,49 +14,44 @@
# RUN: %build --compiler=clang-cl --nodefaultlib --output=%t/build/a.exe %t/main.cpp
# Regular setup - PDB is at the original path
-# RUN: %lldb -S %t/init.input -s %t/check.input %t/build/a.exe > %t.out.txt
-# CHECK: (lldb) target create
-# CHECK-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}build{{[/\\]}}a.exe
-# CHECK: (A) a = (x = 47)
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/build/a.exe | FileCheck --check-prefix=BOTH-ORIG %s
+# BOTH-ORIG: (lldb) target create
+# BOTH-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}build{{[/\\]}}a.exe
+# BOTH-ORIG: (A) a = (x = 47)
# Move the executable to a different directory but keep the PDB.
# RUN: mv %t/build/a.exe %t/dir1
-# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
-# CHECK: (lldb) target create
-# CHECK-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
-# CHECK: (A) a = (x = 47)
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=PDB-ORIG %s
+# PDB-ORIG: (lldb) target create
+# PDB-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# PDB-ORIG: (A) a = (x = 47)
# Copy the PDB to the same directory and all search dirs. LLDB should prefer the original PDB.
# RUN: cp %t/build/a.pdb %t/dir1
# RUN: cp %t/build/a.pdb %t/dir2
# RUN: cp %t/build/a.pdb %t/dir3
-# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
-# CHECK: (lldb) target create
-# CHECK-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
-# CHECK: (A) a = (x = 47)
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=PDB-ORIG %s
# Remove the original PDB. LLDB should now use the one next to the exe.
# RUN: rm %t/build/a.pdb
-# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
-# CHECK: (lldb) target create
-# CHECK-NEXT: Loading {{.*[/\\]}}dir1{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
-# CHECK: (A) a = (x = 47)
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=NEXT-TO-EXE %s
+# NEXT-TO-EXE: (lldb) target create
+# NEXT-TO-EXE-NEXT: Loading {{.*[/\\]}}dir1{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# NEXT-TO-EXE: (A) a = (x = 47)
# Remove the PDB next to the exe. LLDB should now use the one in dir2 (first in list).
# RUN: rm %t/dir1/a.pdb
-# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
-# CHECK: (lldb) target create
-# CHECK-NEXT: Loading {{.*[/\\]}}dir2{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
-# CHECK: (A) a = (x = 47)
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR2 %s
+# DIR2: (lldb) target create
+# DIR2-NEXT: Loading {{.*[/\\]}}dir2{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# DIR2: (A) a = (x = 47)
# Remove the PDB in dir2. LLDB should now use the one in dir3 (second in list).
# RUN: rm %t/dir2/a.pdb
-# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe >> %t.out.txt
-# CHECK: (lldb) target create
-# CHECK-NEXT: Loading {{.*[/\\]}}dir3{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
-# CHECK: (A) a = (x = 47)
-
-# RUN: cat %t.out.txt | FileCheck %s
+# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR3 %s
+# DIR3: (lldb) target create
+# DIR3-NEXT: Loading {{.*[/\\]}}dir3{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
+# DIR3: (A) a = (x = 47)
# Remove the last PDB in dir3. Now, there's no matching PDB anymore.
# RUN: rm %t/dir3/a.pdb
More information about the lldb-commits
mailing list