Skip to content

Commit 5962c77

Browse files
Nerixyzkcloudy0717
authored andcommitted
[LLDB][NativePDB] Look for PDBs in target.debug-file-search-paths (llvm#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 llvm#125355 (though I think this is already fixed in the native plugin).
1 parent 2342302 commit 5962c77

File tree

2 files changed

+119
-9
lines changed

2 files changed

+119
-9
lines changed

lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,40 @@ static lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
8686
}
8787
}
8888

89+
static std::optional<std::string>
90+
findMatchingPDBFilePath(llvm::StringRef original_pdb_path,
91+
llvm::StringRef exe_path) {
92+
const FileSystem &fs = FileSystem::Instance();
93+
94+
if (fs.Exists(original_pdb_path))
95+
return std::string(original_pdb_path);
96+
97+
const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent();
98+
// While the exe_path uses the native style, the exe might be compiled on a
99+
// different OS, so try to guess the style used.
100+
const FileSpec original_pdb_spec(original_pdb_path,
101+
FileSpec::GuessPathStyle(original_pdb_path)
102+
.value_or(FileSpec::Style::native));
103+
const llvm::StringRef pdb_filename = original_pdb_spec.GetFilename();
104+
105+
// If the file doesn't exist, perhaps the path specified at build time
106+
// doesn't match the PDB's current location, so check the location of the
107+
// executable.
108+
const FileSpec local_pdb = exe_dir.CopyByAppendingPathComponent(pdb_filename);
109+
if (fs.Exists(local_pdb))
110+
return local_pdb.GetPath();
111+
112+
// Otherwise, search for one in target.debug-file-search-paths
113+
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
114+
for (const FileSpec &search_dir : search_paths) {
115+
FileSpec pdb_path = search_dir.CopyByAppendingPathComponent(pdb_filename);
116+
if (fs.Exists(pdb_path))
117+
return pdb_path.GetPath();
118+
}
119+
120+
return std::nullopt;
121+
}
122+
89123
static std::unique_ptr<PDBFile>
90124
loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
91125
// Try to find a matching PDB for an EXE.
@@ -113,17 +147,14 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
113147
return nullptr;
114148
}
115149

116-
// If the file doesn't exist, perhaps the path specified at build time
117-
// doesn't match the PDB's current location, so check the location of the
118-
// executable.
119-
if (!FileSystem::Instance().Exists(pdb_file)) {
120-
const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent();
121-
const auto pdb_name = FileSpec(pdb_file).GetFilename().GetCString();
122-
pdb_file = exe_dir.CopyByAppendingPathComponent(pdb_name).GetPathAsConstString().GetStringRef();
123-
}
150+
std::optional<std::string> resolved_pdb_path =
151+
findMatchingPDBFilePath(pdb_file, exe_path);
152+
if (!resolved_pdb_path)
153+
return nullptr;
124154

125155
// If the file is not a PDB or if it doesn't have a matching GUID, fail.
126-
auto pdb = ObjectFilePDB::loadPDBFile(std::string(pdb_file), allocator);
156+
auto pdb =
157+
ObjectFilePDB::loadPDBFile(*std::move(resolved_pdb_path), allocator);
127158
if (!pdb)
128159
return nullptr;
129160

@@ -137,6 +168,9 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
137168

138169
if (expected_info->getGuid() != guid)
139170
return nullptr;
171+
172+
LLDB_LOG(GetLog(LLDBLog::Symbols), "Loading {0} for {1}", pdb->getFilePath(),
173+
exe_path);
140174
return pdb;
141175
}
142176

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# REQUIRES: lld, target-windows
2+
3+
# Test where LLDB looks for PDBs.
4+
# RUN: split-file %s %t
5+
6+
# RUN: mkdir -p %t/build
7+
# RUN: mkdir -p %t/dir1
8+
# RUN: mkdir -p %t/dir2
9+
# RUN: mkdir -p %t/dir3
10+
11+
# RUN: echo "settings append target.debug-file-search-paths %t/dir2" >> %t/init.input
12+
# RUN: echo "settings append target.debug-file-search-paths %t/dir3" >> %t/init.input
13+
14+
# RUN: %build --compiler=clang-cl --nodefaultlib --output=%t/build/a.exe %t/main.cpp
15+
16+
# Regular setup - PDB is at the original path
17+
# RUN: %lldb -S %t/init.input -s %t/check.input %t/build/a.exe | FileCheck --check-prefix=BOTH-ORIG %s
18+
# BOTH-ORIG: (lldb) target create
19+
# BOTH-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}build{{[/\\]}}a.exe
20+
# BOTH-ORIG: (A) a = (x = 47)
21+
22+
# Move the executable to a different directory but keep the PDB.
23+
# RUN: mv %t/build/a.exe %t/dir1
24+
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=PDB-ORIG %s
25+
# PDB-ORIG: (lldb) target create
26+
# PDB-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
27+
# PDB-ORIG: (A) a = (x = 47)
28+
29+
# Copy the PDB to the same directory and all search dirs. LLDB should prefer the original PDB.
30+
# RUN: cp %t/build/a.pdb %t/dir1
31+
# RUN: cp %t/build/a.pdb %t/dir2
32+
# RUN: cp %t/build/a.pdb %t/dir3
33+
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=PDB-ORIG %s
34+
35+
# Remove the original PDB. LLDB should now use the one next to the exe.
36+
# RUN: rm %t/build/a.pdb
37+
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=NEXT-TO-EXE %s
38+
# NEXT-TO-EXE: (lldb) target create
39+
# NEXT-TO-EXE-NEXT: Loading {{.*[/\\]}}dir1{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
40+
# NEXT-TO-EXE: (A) a = (x = 47)
41+
42+
# Remove the PDB next to the exe. LLDB should now use the one in dir2 (first in list).
43+
# RUN: rm %t/dir1/a.pdb
44+
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR2 %s
45+
# DIR2: (lldb) target create
46+
# DIR2-NEXT: Loading {{.*[/\\]}}dir2{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
47+
# DIR2: (A) a = (x = 47)
48+
49+
# Remove the PDB in dir2. LLDB should now use the one in dir3 (second in list).
50+
# RUN: rm %t/dir2/a.pdb
51+
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR3 %s
52+
# DIR3: (lldb) target create
53+
# DIR3-NEXT: Loading {{.*[/\\]}}dir3{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
54+
# DIR3: (A) a = (x = 47)
55+
56+
# Remove the last PDB in dir3. Now, there's no matching PDB anymore.
57+
# RUN: rm %t/dir3/a.pdb
58+
# RUN: %lldb -S %t/init.input -s %t/check.input -f %t/dir1/a.exe 2>&1 | FileCheck --check-prefix=NOPDB %s
59+
# NOPDB: error: can't find global variable 'a'
60+
61+
#--- main.cpp
62+
63+
struct A {
64+
int x = 47;
65+
};
66+
A a;
67+
int main() {}
68+
69+
#--- init.input
70+
71+
log enable lldb symbol
72+
73+
#--- check.input
74+
75+
target variable a
76+
q

0 commit comments

Comments
 (0)