llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: nerix (Nerixyz)

<details>
<summary>Changes</summary>

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).

---
Full diff: https://github.com/llvm/llvm-project/pull/169719.diff


2 Files Affected:

- (modified) lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp 
(+43-9) 
- (added) lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test (+81) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/169719
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to