Author: nerix Date: 2025-12-01T19:27:54+01:00 New Revision: c9d9dddc1c5e9f203f5db890f383b956c5b2d295
URL: https://github.com/llvm/llvm-project/commit/c9d9dddc1c5e9f203f5db890f383b956c5b2d295 DIFF: https://github.com/llvm/llvm-project/commit/c9d9dddc1c5e9f203f5db890f383b956c5b2d295.diff LOG: [LLDB][NativePDB] Look for PDBs in `target.debug-file-search-paths` (#169719) Similar to DWARF's DWO, we should look for PDBs in `target.debug-file-search-paths` if the PDB isn't at the original location or next to the executable. With this PR, the search order is as follows: 1. PDB path specified in the PE/COFF file 2. Next to the executable 3. In `target.debug-file-search-paths` This roughly matches [the order Visual Studio uses](https://learn.microsoft.com/en-us/visualstudio/debugger/specify-symbol-dot-pdb-and-source-files-in-the-visual-studio-debugger?view=vs-2022#where-the-debugger-looks-for-symbols), except that we don't have a project folder and don't support symbol servers. Closes #125355 (though I think this is already fixed in the native plugin). Added: lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test Modified: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 40e783f9bad38..3bf113a07d28c 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 + // diff erent 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..c35c82ad84d2f --- /dev/null +++ b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test @@ -0,0 +1,76 @@ +# 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 | 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 diff erent 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 | 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 | 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 | 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 | 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 | 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 +# 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 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
