Skip to content
Merged
153 changes: 134 additions & 19 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 @@ -1641,6 +1642,94 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) {
clang->GetNativePDBParser()->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())
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 @@ -1677,34 +1766,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 @@ -260,6 +260,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {

std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);

void CacheFunctionNames();

llvm::BumpPtrAllocator m_allocator;

lldb::addr_t m_obj_load_address = 0;
Expand All @@ -282,6 +284,13 @@ class SymbolFileNativePDB : public SymbolFileCommon {
m_parent_types;

lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;

/// 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
34 changes: 34 additions & 0 deletions lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,52 @@

// RUN: lldb-test symbols --find=function --name=main --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-MAIN
// RUN: lldb-test symbols --find=function --name=main --function-flags=method %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
// RUN: lldb-test symbols --find=function --name=main --function-flags=base %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-MAIN

// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-STATIC
// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=method %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=base %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-STATIC

// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-VAR
// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=method %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=base %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-VAR

// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=method %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=base %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE

// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=method %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=base %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL

// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-STATIC-METHOD
// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=method %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=base %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-STATIC-METHOD

// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD
// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=method %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD-METHOD
// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=base %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add tests on 2 different classes/namespaces with the same function basename?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added them. --function-flags=method will only match the basename of methods (the invocation might look a bit confusing). This is consistent with DWARF:

NativePDB
> &'f:\dev\llvm-project\build\bin\lldb-test.exe' symbols --find=function --name=Struct::overloaded_method --function-flags=method 'F:\Dev\llvm-project\build\tools\lldb\test\Shell\SymbolFile\NativePDB\Output\find-functions.cpp.tmp.exe'
Module: F:\Dev\llvm-project\build\tools\lldb\test\Shell\SymbolFile\NativePDB\Output\find-functions.cpp.tmp.exe
Found 4 functions:
0x0000001f9e98e988: SymbolContextList
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++"
     Function: id = {0x5d000001}, name = "Class::overloaded_method", decl_context = {}, range = find-functions.cpp.tmp.exe[0x0000000140001200-0x000000014000121b)
     FuncType: id = {0x000101b4}, byte-size = 0, compiler_type = "_Bool (int)"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++"
     Function: id = {0x67000001}, name = "Class::overloaded_method", decl_context = {}, range = find-functions.cpp.tmp.exe[0x0000000140001220-0x000000014000122b)
     FuncType: id = {0x000101a4}, byte-size = 0, compiler_type = "_Bool (void)"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++"
     Function: id = {0x4c800001}, name = "Struct::overloaded_method", decl_context = {}, range = find-functions.cpp.tmp.exe[0x00000001400011a0-0x00000001400011d9)
     FuncType: id = {0x000100e4}, byte-size = 0, compiler_type = "int (void)"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++"
     Function: id = {0x6f400001}, name = "Struct::overloaded_method", decl_context = {}, range = find-functions.cpp.tmp.exe[0x0000000140001230-0x0000000140001246)
     FuncType: id = {0x00010114}, byte-size = 0, compiler_type = "int (char)"
DWARF
> &'f:\dev\llvm-project\build\bin\lldb-test.exe' symbols --find=function --name=Struct::overloaded_method --function-flags=method a.exe
Module: a.exe
Found 6 functions:
0x0000003203f8e5d8: SymbolContextList
       Module: file = "a.exe", arch = "x86_64"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++14"
     Function: id = {0x0000028f}, name = "int Struct::overloaded_method(void)", mangled = "?overloaded_method@Struct@@QEAAHXZ", decl_context = {ClassOrStruct(Struct)}, range = a.exe[0x0000000140001150-0x0000000140001189)
     FuncType: id = {0x0000028f}, byte-size = 0, decl = F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp:69, compiler_type = "int (void)"
       Module: file = "a.exe", arch = "x86_64"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++14"
     Function: id = {0x000002b3}, name = "int Class::overloaded_method(bool)", mangled = "?overloaded_method@Class@@SAH_N@Z", decl_context = {ClassOrStruct(Class)}, range = a.exe[0x0000000140001190-0x00000001400011ae)
     FuncType: id = {0x000002b3}, byte-size = 0, decl = F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp:90, compiler_type = "int (_Bool)"
       Module: file = "a.exe", arch = "x86_64"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++14"
     Function: id = {0x000002d5}, name = "bool Class::overloaded_method(int)", mangled = "?overloaded_method@Class@@QEAA_NH@Z", decl_context = {ClassOrStruct(Class)}, range = a.exe[0x00000001400011b0-0x00000001400011cb)
     FuncType: id = {0x000002d5}, byte-size = 0, decl = F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp:87, compiler_type = "_Bool (int)"
       Module: file = "a.exe", arch = "x86_64"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++14"
     Function: id = {0x00000307}, name = "bool Class::overloaded_method(void)", mangled = "?overloaded_method@Class@@QEAA_NXZ", decl_context = {ClassOrStruct(Class)}, range = a.exe[0x00000001400011d0-0x00000001400011db)
     FuncType: id = {0x00000307}, byte-size = 0, decl = F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp:84, compiler_type = "_Bool (void)"
       Module: file = "a.exe", arch = "x86_64"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++14"
     Function: id = {0x0000032b}, name = "int Struct::overloaded_method(char)", mangled = "?overloaded_method@Struct@@MEAAHD@Z", decl_context = {ClassOrStruct(Struct)}, range = a.exe[0x00000001400011e0-0x00000001400011f6)
     FuncType: id = {0x0000032b}, byte-size = 0, decl = F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp:73, compiler_type = "int (char)"
       Module: file = "a.exe", arch = "x86_64"
  CompileUnit: id = {0x00000000}, file = "F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp", language = "c++14"
     Function: id = {0x0000035d}, name = "int Struct::overloaded_method(char, int, ...)", mangled = "?overloaded_method@Struct@@CAHDHZZ", decl_context = {ClassOrStruct(Struct)}, range = a.exe[0x0000000140001200-0x0000000140001210)
     FuncType: id = {0x0000035d}, byte-size = 0, decl = F:\Dev\llvm-project\lldb\test\Shell\SymbolFile\NativePDB\find-functions.cpp:77, compiler_type = "int (char, int, ...)"

I'm not sure if it's correct that DWARF returns static functions here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also not sure if dwarf plugin is correct to return static functions (need someone to verify this). Other than that, it looks good to me.


struct Struct {
int simple_method() {
Expand Down Expand Up @@ -69,6 +97,8 @@ int main(int argc, char **argv) {
// FIND-MAIN: Function: id = {{.*}}, name = "main"
// FIND-MAIN-NEXT: FuncType: id = {{.*}}, compiler_type = "int (int, char **)"

// FIND-NO-FUNCTION: Found 0 functions

// FIND-STATIC: Function: id = {{.*}}, name = "{{.*}}static_fn{{.*}}"
// FIND-STATIC-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)"

Expand All @@ -88,3 +118,7 @@ int main(int argc, char **argv) {
// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (void)"
// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char)"
// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"

// FIND-OVERLOAD-METHOD: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
// FIND-OVERLOAD-METHOD: FuncType: id = {{.*}}, compiler_type = "int (void)"
// FIND-OVERLOAD-METHOD: FuncType: id = {{.*}}, compiler_type = "int (char)"
Loading