Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand Down
81 changes: 81 additions & 0 deletions lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
Original file line number Diff line number Diff line change
@@ -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
Loading