Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e8f982e
[llldb][test] Mark a DWO test unsupported on Darwin and Windows (#156…
DavidSpickett Sep 1, 2025
e0dada5
[lldb][test] Use lld on Windows in frame format test (#156320)
DavidSpickett Sep 1, 2025
0a15ba1
[lldb] Consider FileSpec from ObjectFilePDB in SymbolFilePDB (not ups…
weliveindetail May 6, 2026
8d4c9df
Reland "[LLDB][NativePDB] Find functions by basename" ( #152295) (#15…
Nerixyz Aug 12, 2025
07948be
[LLDB][NativePDB] Ignore functions with no type in name lookup (#153382)
Nerixyz Aug 14, 2025
93bf224
[LLDB][NativePDB] Implement `AddSymbols` (#154121)
Nerixyz Sep 11, 2025
42d1fc2
[LLDB][NativePDB] Estimate symbol sizes (#165727)
Nerixyz Oct 31, 2025
a0e5b37
[LLDB][PDB] Access object file through module (#169728)
Nerixyz Nov 28, 2025
4755c27
Reland "[lldb] Initial plugin and test for SymbolLocatorSymStore" (#1…
weliveindetail Mar 17, 2026
87d5335
Re-reland "[Support] Move HTTP client/server to new LLVMSupportHTTP l…
weliveindetail Mar 12, 2026
1e1c033
[lldb] Fix liblldb linkage in libllvm build after 5eaf19a15129 (#186515)
weliveindetail Mar 13, 2026
b8a3e15
[lldb] Fix permission issue in API test on lldb-x86_64-win (#187021)
weliveindetail Mar 17, 2026
cedcfd4
[lldb] Avoid permission issue in API test with SHARED_BUILD_TESTCASE …
weliveindetail Mar 18, 2026
d5029e2
[lldb] Skip file cleanup to avoid permission issue in API test (#187227)
weliveindetail Mar 18, 2026
9217c11
[lldb] Add HTTP support in SymbolLocatorSymStore (#186986)
weliveindetail Mar 20, 2026
f67025d
[llvm] Restrict llvm-debginfod-find test to localhost to fix winhttp …
weliveindetail Mar 20, 2026
63e619f
[llvm] Run headers-winhttp.test only if the Python side of it works (…
weliveindetail Mar 20, 2026
9221455
[llvm] Silence llvm-debuginfod-find/headers-winhttp.test on Windows b…
weliveindetail Mar 20, 2026
03ecf21
[lldb] Fix warning style for SymStore symbol locator (#187776)
Nerixyz Mar 20, 2026
eedd6c7
[lldb] Let SymStore locator download PDBs to a temp file (#188798)
weliveindetail Mar 27, 2026
c3af12c
[llvm][SupportHTTP] Guard SSL settings by Secure flag to avoid failin…
weliveindetail Mar 27, 2026
2e1f865
[llvm] Fix SupportHTTP linkage with libLLVM in unit-tests (#190097)
weliveindetail Apr 2, 2026
d9f886a
[lldb] Bring Debuginfod's StreamedHTTPResponseHandler to SymbolLocato…
weliveindetail Apr 8, 2026
963710c
[lldb] Fix: Disable shared build dir when testing with PDB (#190991)
weliveindetail Apr 14, 2026
f1b285c
[SupportHTTP] Remove a redundant pragma (#188533)
mstorsjo Mar 26, 2026
21d2869
[llvm] Move libSupportHTTP to top-level libHTTP (NFC) (#191202)
weliveindetail Apr 15, 2026
b9b65ac
[llvm] Fix: DebuginfodTests must link libHTTP after 789f30c73ef4 (#19…
weliveindetail Apr 15, 2026
f6ac927
[lldb] Add caching and _NT_SYMBOL_PATH parsing in SymbolLocatorSymSto…
weliveindetail Apr 20, 2026
ed30d8e
[lldb] Add HTTPS tests for SymbolLocatorSymStore (#192274)
weliveindetail Apr 22, 2026
01cead3
[lldb] Change verbose logs into regular ones in SymbolLocatorSymStore…
weliveindetail Apr 30, 2026
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
11 changes: 11 additions & 0 deletions lldb/packages/Python/lldbsuite/test/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1411,3 +1411,14 @@ def skipIfBuildType(types: list[str]):
and configuration.cmake_build_type.lower() in types,
"skip on {} build type(s)".format(", ".join(types)),
)


def skipUnlessPackageAvailable(name):
"""Skip the test case if the named package is not available on the system."""
available = True
try:
__import__(name)
except ImportError:
available = False

return unittest.skipUnless(available, f"requires the '{name}' package")
4 changes: 4 additions & 0 deletions lldb/packages/Python/lldbsuite/test/lldbtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2049,6 +2049,10 @@ def test_method(self, attrvalue=attrvalue):

else:
newattrs[attrname] = attrvalue

if original_testcase.TEST_WITH_PDB_DEBUG_INFO:
newattrs["SHARED_BUILD_TESTCASE"] = False

return super(LLDBTestCaseFactory, cls).__new__(cls, name, bases, newattrs)


Expand Down
21 changes: 21 additions & 0 deletions lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,27 @@ std::optional<FileSpec> ObjectFilePECOFF::GetDebugLink() {
return std::nullopt;
}

std::optional<FileSpec> ObjectFilePECOFF::GetPDBPath() {
llvm::StringRef pdb_file;
const llvm::codeview::DebugInfo *pdb_info = nullptr;
if (llvm::Error Err = m_binary->getDebugPDBInfo(pdb_info, pdb_file)) {
// DebugInfo section is corrupt.
Log *log = GetLog(LLDBLog::Object);
llvm::StringRef file = m_binary->getFileName();
LLDB_LOG_ERROR(
log, std::move(Err),
"Failed to read Codeview record for PDB debug info file ({1}): {0}",
file);
return std::nullopt;
}
if (pdb_file.empty()) {
// No DebugInfo section present.
return std::nullopt;
}
return FileSpec(pdb_file, FileSpec::GuessPathStyle(pdb_file).value_or(
FileSpec::Style::native));
}

uint32_t ObjectFilePECOFF::ParseDependentModules() {
ModuleSP module_sp(GetModule());
if (!module_sp)
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class ObjectFilePECOFF : public lldb_private::ObjectFile {
/// contains it.
std::optional<lldb_private::FileSpec> GetDebugLink();

std::optional<lldb_private::FileSpec> GetPDBPath();

uint32_t GetDependentModules(lldb_private::FileSpecList &files) override;

lldb_private::Address GetEntryPointAddress() override;
Expand Down
233 changes: 213 additions & 20 deletions lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/PDB.h"
Expand Down Expand Up @@ -356,6 +357,11 @@ uint32_t SymbolFileNativePDB::CalculateAbilities() {
if (!pdb_file)
return 0;

LLDB_LOG(
GetLog(LLDBLog::Symbols), "Loading {0} for {1}",
pdb_file->getFilePath(),
m_objfile_sp->GetModule()->GetObjectFile()->GetFileSpec().GetPath());

auto expected_index = PdbIndex::create(pdb_file);
if (!expected_index) {
llvm::consumeError(expected_index.takeError());
Expand Down Expand Up @@ -1152,7 +1158,80 @@ lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) {
return TranslateLanguage(item->m_compile_opts->getLanguage());
}

void SymbolFileNativePDB::AddSymbols(Symtab &symtab) {}
void SymbolFileNativePDB::AddSymbols(Symtab &symtab) {
auto *section_list =
m_objfile_sp->GetModule()->GetObjectFile()->GetSectionList();
if (!section_list)
return;

PublicSym32 last_sym;
size_t last_sym_idx = 0;
lldb::SectionSP section_sp;

// To estimate the size of a symbol, we use the difference to the next symbol.
// If there's no next symbol or the section/segment changed, the symbol will
// take the remaining space. The estimate can be too high in case there's
// padding between symbols. This similar to the algorithm used by the DIA
// SDK.
auto finish_last_symbol = [&](const PublicSym32 *next) {
if (!section_sp)
return;
Symbol *last = symtab.SymbolAtIndex(last_sym_idx);
if (!last)
return;

if (next && last_sym.Segment == next->Segment) {
assert(last_sym.Offset <= next->Offset);
last->SetByteSize(next->Offset - last_sym.Offset);
} else {
// the last symbol was the last in its section
assert(section_sp->GetByteSize() >= last_sym.Offset);
assert(!next || next->Segment > last_sym.Segment);
last->SetByteSize(section_sp->GetByteSize() - last_sym.Offset);
}
};

// The address map is sorted by the address of a symbol.
for (auto pid : m_index->publics().getAddressMap()) {
PdbGlobalSymId global{pid, true};
CVSymbol sym = m_index->ReadSymbolRecord(global);
auto kind = sym.kind();
if (kind != S_PUB32)
continue;
PublicSym32 pub =
llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym));
finish_last_symbol(&pub);

if (!section_sp || last_sym.Segment != pub.Segment)
section_sp = section_list->FindSectionByID(pub.Segment);

if (!section_sp)
continue;

lldb::SymbolType type = eSymbolTypeData;
if ((pub.Flags & PublicSymFlags::Function) != PublicSymFlags::None ||
(pub.Flags & PublicSymFlags::Code) != PublicSymFlags::None)
type = eSymbolTypeCode;

last_sym_idx =
symtab.AddSymbol(Symbol(/*symID=*/pid,
/*name=*/pub.Name,
/*type=*/type,
/*external=*/true,
/*is_debug=*/true,
/*is_trampoline=*/false,
/*is_artificial=*/false,
/*section_sp=*/section_sp,
/*value=*/pub.Offset,
/*size=*/0,
/*size_is_valid=*/false,
/*contains_linker_annotations=*/false,
/*flags=*/0));
last_sym = pub;
}

finish_last_symbol(nullptr);
}

size_t SymbolFileNativePDB::ParseFunctions(CompileUnit &comp_unit) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
Expand Down Expand Up @@ -1772,6 +1851,94 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) {
ast_builder->Dump(s, filter);
}

void SymbolFileNativePDB::CacheFunctionNames() {
if (!m_func_full_names.IsEmpty())
return;

// (segment, code offset) -> gid
std::map<std::pair<uint16_t, uint32_t>, uint32_t> addr_ids;

// First, find all function references in the globals table.
for (const uint32_t gid : m_index->globals().getGlobalsTable()) {
CVSymbol ref_sym = m_index->symrecords().readRecord(gid);
auto kind = ref_sym.kind();
if (kind != S_PROCREF && kind != S_LPROCREF)
continue;

ProcRefSym ref =
llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(ref_sym));
if (ref.Name.empty())
continue;

// Find the function this is referencing.
CompilandIndexItem &cci =
m_index->compilands().GetOrCreateCompiland(ref.modi());
auto iter = cci.m_debug_stream.getSymbolArray().at(ref.SymOffset);
if (iter == cci.m_debug_stream.getSymbolArray().end())
continue;
kind = iter->kind();
if (kind != S_GPROC32 && kind != S_LPROC32)
continue;

ProcSym proc =
llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(*iter));
if ((proc.Flags & ProcSymFlags::IsUnreachable) != ProcSymFlags::None)
continue;
if (proc.Name.empty() || proc.FunctionType.isSimple())
continue;

// The function/procedure symbol only contains the demangled name.
// The mangled names are in the publics table. Save the address of this
// function to lookup the mangled name later.
addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid);

llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(proc.Name);
if (basename.empty())
basename = proc.Name;

m_func_base_names.Append(ConstString(basename), gid);
m_func_full_names.Append(ConstString(proc.Name), gid);

// To see if this is a member function, check the type.
auto type = m_index->tpi().getType(proc.FunctionType);
if (type.kind() == LF_MFUNCTION) {
MemberFunctionRecord mfr;
llvm::cantFail(
TypeDeserializer::deserializeAs<MemberFunctionRecord>(type, mfr));
if (!mfr.getThisType().isNoneType())
m_func_method_names.Append(ConstString(basename), gid);
}
}

// The publics stream contains all mangled function names and their address.
for (auto pid : m_index->publics().getPublicsTable()) {
PdbGlobalSymId global{pid, true};
CVSymbol sym = m_index->ReadSymbolRecord(global);
auto kind = sym.kind();
if (kind != S_PUB32)
continue;
PublicSym32 pub =
llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym));
// We only care about mangled names - if the name isn't mangled, it's
// already in the full name map.
if (!Mangled::IsMangledName(pub.Name))
continue;

// Check if this symbol is for one of our functions.
auto it = addr_ids.find({pub.Segment, pub.Offset});
if (it != addr_ids.end())
m_func_full_names.Append(ConstString(pub.Name), it->second);
}

// Sort them before value searching is working properly.
m_func_full_names.Sort();
m_func_full_names.SizeToFit();
m_func_method_names.Sort();
m_func_method_names.SizeToFit();
m_func_base_names.Sort();
m_func_base_names.SizeToFit();
}

void SymbolFileNativePDB::FindGlobalVariables(
ConstString name, const CompilerDeclContext &parent_decl_ctx,
uint32_t max_matches, VariableList &variables) {
Expand Down Expand Up @@ -1808,34 +1975,60 @@ void SymbolFileNativePDB::FindFunctions(
if (name_type_mask & eFunctionNameTypeFull)
name = lookup_info.GetName();

// For now we only support lookup by method name or full name.
if (!(name_type_mask & eFunctionNameTypeFull ||
name_type_mask & eFunctionNameTypeBase ||
name_type_mask & eFunctionNameTypeMethod))
return;
CacheFunctionNames();

using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
std::set<uint32_t> resolved_ids; // avoid duplicate lookups
auto resolve_from = [&](UniqueCStringMap<uint32_t> &Names) {
std::vector<uint32_t> ids;
if (!Names.GetValues(name, ids))
return;

std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName(
name.GetStringRef(), m_index->symrecords());
for (const SymbolAndOffset &match : matches) {
if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF)
continue;
ProcRefSym proc(match.second.kind());
cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc));
for (uint32_t id : ids) {
if (!resolved_ids.insert(id).second)
continue;

if (!IsValidRecord(proc))
continue;
PdbGlobalSymId global{id, false};
if (parent_decl_ctx.IsValid() &&
GetDeclContextContainingUID(toOpaqueUid(global)) != parent_decl_ctx)
continue;

CompilandIndexItem &cci =
m_index->compilands().GetOrCreateCompiland(proc.modi());
SymbolContext sc;
CVSymbol sym = m_index->ReadSymbolRecord(global);
auto kind = sym.kind();
lldbassert(kind == S_PROCREF || kind == S_LPROCREF);

sc.comp_unit = GetOrCreateCompileUnit(cci).get();
PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
ProcRefSym proc =
cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(sym));

sc_list.Append(sc);
}
if (!IsValidRecord(proc))
continue;

CompilandIndexItem &cci =
m_index->compilands().GetOrCreateCompiland(proc.modi());
SymbolContext sc;

sc.comp_unit = GetOrCreateCompileUnit(cci).get();
if (!sc.comp_unit)
continue;

PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
if (!sc.function)
continue;

sc_list.Append(sc);
}
};

if (name_type_mask & eFunctionNameTypeFull)
resolve_from(m_func_full_names);
if (name_type_mask & eFunctionNameTypeBase)
resolve_from(m_func_base_names);
if (name_type_mask & eFunctionNameTypeMethod)
resolve_from(m_func_method_names);
}

void SymbolFileNativePDB::FindFunctions(const RegularExpression &regex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
fn);

void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr);

void CacheFunctionNames();

llvm::BumpPtrAllocator m_allocator;

Expand All @@ -280,6 +282,13 @@ class SymbolFileNativePDB : public SymbolFileCommon {
llvm::DenseMap<lldb::user_id_t, std::shared_ptr<InlineSite>> m_inline_sites;
llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
m_parent_types;

/// mangled name/full function name -> Global ID(s)
lldb_private::UniqueCStringMap<uint32_t> m_func_full_names;
/// basename -> Global ID(s)
lldb_private::UniqueCStringMap<uint32_t> m_func_base_names;
/// method basename -> Global ID(s)
lldb_private::UniqueCStringMap<uint32_t> m_func_method_names;
};

} // namespace npdb
Expand Down
Loading