[LLDB][NativePDB] Allow type lookup in namespaces (#149876)
Previously, `type lookup` for types in namespaces didn't work with the native PDB plugin, because `FindTypes` would only look for types whose base name was equal to their full name. PDB/CodeView does not store the base names in the TPI stream, but the types have their full name (e.g. `std::thread` instead of `thread`). So `findRecordsByName` would only return types in the top level namespace. This PR changes the lookup to go through all types and check their base name. As that could be a bit expensive, the names are first cached (similar to the function lookup in the DIA PDB plugin). Potential types are checked with `TypeQuery::ContextMatches`. To be able to handle anonymous namespaces, I changed `TypeQuery::ContextMatches`. The [`TypeQuery` constructor](9ad7edef42/lldb/source/Symbol/Type.cpp (L76-L79)) inserts all name components as `CompilerContextKind::AnyDeclContext`. To skip over anonymous namespaces, `ContextMatches` checked if a component was empty and exactly of kind `Namespace`. For our query, the last check was always false, so we never skipped anonymous namespaces. DWARF doesn't have this problem, as it [constructs the context outside](abe93d9d7e/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp (L154-L160)) and has proper information about namespaces. I'm not fully sure if my change is correct and that it doesn't break other users of `TypeQuery`. This enables `type lookup <type>` to work on types in namespaces. However, expressions don't work with this yet, because `FindNamespace` is unimplemented for native PDB.
This commit is contained in:
parent
df71243fa8
commit
d95dadff8f
@ -72,6 +72,16 @@ struct CVTagRecord {
|
||||
return cvunion.Name;
|
||||
}
|
||||
|
||||
CompilerContextKind contextKind() const {
|
||||
if (m_kind == Struct || m_kind == Class)
|
||||
return CompilerContextKind::ClassOrStruct;
|
||||
if (m_kind == Enum)
|
||||
return CompilerContextKind::Enum;
|
||||
|
||||
assert(m_kind == Union);
|
||||
return CompilerContextKind::Union;
|
||||
}
|
||||
|
||||
private:
|
||||
CVTagRecord(llvm::codeview::ClassRecord &&c);
|
||||
CVTagRecord(llvm::codeview::UnionRecord &&u);
|
||||
|
||||
@ -1720,18 +1720,22 @@ void SymbolFileNativePDB::FindTypes(const lldb_private::TypeQuery &query,
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
||||
|
||||
std::vector<TypeIndex> matches =
|
||||
m_index->tpi().findRecordsByName(query.GetTypeBasename().GetStringRef());
|
||||
// We can't query for the full name because the type might reside
|
||||
// in an anonymous namespace. Search for the basename in our map and check the
|
||||
// matching types afterwards.
|
||||
std::vector<uint32_t> matches;
|
||||
m_type_base_names.GetValues(query.GetTypeBasename(), matches);
|
||||
|
||||
for (TypeIndex type_idx : matches) {
|
||||
TypeSP type_sp = GetOrCreateType(type_idx);
|
||||
if (!type_sp)
|
||||
for (uint32_t match_idx : matches) {
|
||||
std::vector context = GetContextForType(TypeIndex(match_idx));
|
||||
if (context.empty())
|
||||
continue;
|
||||
|
||||
// We resolved a type. Get the fully qualified name to ensure it matches.
|
||||
ConstString name = type_sp->GetQualifiedName();
|
||||
TypeQuery type_match(name.GetStringRef(), TypeQueryOptions::e_exact_match);
|
||||
if (query.ContextMatches(type_match.GetContextRef())) {
|
||||
if (query.ContextMatches(context)) {
|
||||
TypeSP type_sp = GetOrCreateType(TypeIndex(match_idx));
|
||||
if (!type_sp)
|
||||
continue;
|
||||
|
||||
results.InsertUnique(type_sp);
|
||||
if (results.Done(query))
|
||||
return;
|
||||
@ -2201,11 +2205,15 @@ void SymbolFileNativePDB::BuildParentMap() {
|
||||
CVTagRecord tag = CVTagRecord::create(type);
|
||||
|
||||
RecordIndices &indices = record_indices[tag.asTag().getUniqueName()];
|
||||
if (tag.asTag().isForwardRef())
|
||||
if (tag.asTag().isForwardRef()) {
|
||||
indices.forward = *ti;
|
||||
else
|
||||
} else {
|
||||
indices.full = *ti;
|
||||
|
||||
auto base_name = MSVCUndecoratedNameParser::DropScope(tag.name());
|
||||
m_type_base_names.Append(ConstString(base_name), ti->getIndex());
|
||||
}
|
||||
|
||||
if (indices.full != TypeIndex::None() &&
|
||||
indices.forward != TypeIndex::None()) {
|
||||
forward_to_full[indices.forward] = indices.full;
|
||||
@ -2261,6 +2269,10 @@ void SymbolFileNativePDB::BuildParentMap() {
|
||||
llvm::consumeError(std::move(error));
|
||||
}
|
||||
|
||||
// After calling Append(), the type-name map needs to be sorted again to be
|
||||
// able to look up a type by its name.
|
||||
m_type_base_names.Sort();
|
||||
|
||||
// Now that we know the forward -> full mapping of all type indices, we can
|
||||
// re-write all the indices. At the end of this process, we want a mapping
|
||||
// consisting of fwd -> full and full -> full for all child -> parent indices.
|
||||
@ -2353,3 +2365,52 @@ SymbolFileNativePDB::GetParentType(llvm::codeview::TypeIndex ti) {
|
||||
return std::nullopt;
|
||||
return parent_iter->second;
|
||||
}
|
||||
|
||||
std::vector<CompilerContext>
|
||||
SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
|
||||
CVType type = m_index->tpi().getType(ti);
|
||||
if (!IsTagRecord(type))
|
||||
return {};
|
||||
|
||||
CVTagRecord tag = CVTagRecord::create(type);
|
||||
|
||||
std::optional<Type::ParsedName> parsed_name =
|
||||
Type::GetTypeScopeAndBasename(tag.name());
|
||||
if (!parsed_name)
|
||||
return {{tag.contextKind(), ConstString(tag.name())}};
|
||||
|
||||
std::vector<CompilerContext> ctx;
|
||||
// assume everything is a namespace at first
|
||||
for (llvm::StringRef scope : parsed_name->scope) {
|
||||
ctx.emplace_back(CompilerContextKind::Namespace, ConstString(scope));
|
||||
}
|
||||
// we know the kind of our own type
|
||||
ctx.emplace_back(tag.contextKind(), ConstString(parsed_name->basename));
|
||||
|
||||
// try to find the kind of parents
|
||||
for (auto &el : llvm::reverse(llvm::drop_end(ctx))) {
|
||||
std::optional<TypeIndex> parent = GetParentType(ti);
|
||||
if (!parent)
|
||||
break;
|
||||
|
||||
ti = *parent;
|
||||
type = m_index->tpi().getType(ti);
|
||||
switch (type.kind()) {
|
||||
case LF_CLASS:
|
||||
case LF_STRUCTURE:
|
||||
case LF_INTERFACE:
|
||||
el.kind = CompilerContextKind::ClassOrStruct;
|
||||
continue;
|
||||
case LF_UNION:
|
||||
el.kind = CompilerContextKind::Union;
|
||||
continue;
|
||||
case LF_ENUM:
|
||||
el.kind = CompilerContextKind::Enum;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@ -258,6 +258,8 @@ private:
|
||||
|
||||
void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr);
|
||||
|
||||
std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
|
||||
|
||||
llvm::BumpPtrAllocator m_allocator;
|
||||
|
||||
lldb::addr_t m_obj_load_address = 0;
|
||||
@ -278,6 +280,8 @@ private:
|
||||
llvm::DenseMap<lldb::user_id_t, std::shared_ptr<InlineSite>> m_inline_sites;
|
||||
llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
|
||||
m_parent_types;
|
||||
|
||||
lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
|
||||
};
|
||||
|
||||
} // namespace npdb
|
||||
|
||||
@ -817,10 +817,12 @@ Type::GetTypeScopeAndBasename(llvm::StringRef name) {
|
||||
case ':':
|
||||
if (prev_is_colon && template_depth == 0) {
|
||||
llvm::StringRef scope_name = name.slice(name_begin, pos.index() - 1);
|
||||
// The itanium demangler uses this string to represent anonymous
|
||||
// The demanglers use these strings to represent anonymous
|
||||
// namespaces. Convert it to a more language-agnostic form (which is
|
||||
// also used in DWARF).
|
||||
if (scope_name == "(anonymous namespace)")
|
||||
if (scope_name == "(anonymous namespace)" ||
|
||||
scope_name == "`anonymous namespace'" ||
|
||||
scope_name == "`anonymous-namespace'")
|
||||
scope_name = "";
|
||||
result.scope.push_back(scope_name);
|
||||
name_begin = pos.index() + 1;
|
||||
|
||||
135
lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test
Normal file
135
lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test
Normal file
@ -0,0 +1,135 @@
|
||||
# REQUIRES: target-windows
|
||||
|
||||
# Test namespace lookup.
|
||||
# RUN: split-file %s %t
|
||||
# RUN: %build --nodefaultlib -o %t.exe -- %t/main.cpp
|
||||
# RUN: %lldb -f %t.exe -s \
|
||||
# RUN: %t/commands.input 2>&1 | FileCheck %s
|
||||
|
||||
#--- main.cpp
|
||||
|
||||
struct S {
|
||||
char a[1];
|
||||
};
|
||||
|
||||
namespace Outer {
|
||||
|
||||
struct S {
|
||||
char a[2];
|
||||
};
|
||||
|
||||
namespace Inner1 {
|
||||
struct S {
|
||||
char a[3];
|
||||
};
|
||||
|
||||
namespace Inner2 {
|
||||
struct S {
|
||||
char a[4];
|
||||
};
|
||||
} // namespace Inner2
|
||||
} // namespace Inner1
|
||||
|
||||
namespace Inner2 {
|
||||
struct S {
|
||||
char a[5];
|
||||
};
|
||||
} // namespace Inner2
|
||||
|
||||
namespace {
|
||||
struct A {
|
||||
char a[6];
|
||||
};
|
||||
} // namespace
|
||||
|
||||
} // namespace Outer
|
||||
|
||||
namespace {
|
||||
struct A {
|
||||
char a[7];
|
||||
};
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
S s;
|
||||
Outer::S os;
|
||||
Outer::Inner1::S oi1s;
|
||||
Outer::Inner1::Inner2::S oi1i2s;
|
||||
Outer::Inner2::S oi2s;
|
||||
A a1;
|
||||
Outer::A a2;
|
||||
return sizeof(s) + sizeof(os) + sizeof(oi1s) + sizeof(oi1i2s) + sizeof(oi2s) + sizeof(a1) + sizeof(a2);
|
||||
}
|
||||
|
||||
#--- commands.input
|
||||
|
||||
b main
|
||||
r
|
||||
|
||||
type lookup S
|
||||
type lookup ::S
|
||||
type lookup Outer::S
|
||||
type lookup Outer::Inner1::S
|
||||
type lookup Inner1::S
|
||||
type lookup Outer::Inner1::Inner2::S
|
||||
type lookup Inner2::S
|
||||
type lookup Outer::Inner2::S
|
||||
type lookup Outer::A
|
||||
type lookup A
|
||||
type lookup ::A
|
||||
expr sizeof(S)
|
||||
expr sizeof(A)
|
||||
|
||||
quit
|
||||
|
||||
# CHECK: (lldb) type lookup S
|
||||
# CHECK: struct S {
|
||||
# CHECK: struct S {
|
||||
# CHECK: struct S {
|
||||
# CHECK: struct S {
|
||||
# CHECK: struct S {
|
||||
# CHECK: }
|
||||
# CHECK-NEXT: (lldb) type lookup ::S
|
||||
# CHECK-NEXT: struct S {
|
||||
# CHECK-NEXT: char a[1];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) type lookup Outer::S
|
||||
# CHECK-NEXT: struct S {
|
||||
# CHECK-NEXT: char a[2];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) type lookup Outer::Inner1::S
|
||||
# CHECK-NEXT: struct S {
|
||||
# CHECK-NEXT: char a[3];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) type lookup Inner1::S
|
||||
# CHECK-NEXT: struct S {
|
||||
# CHECK-NEXT: char a[3];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) type lookup Outer::Inner1::Inner2::S
|
||||
# CHECK-NEXT: struct S {
|
||||
# CHECK-NEXT: char a[4];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) type lookup Inner2::S
|
||||
# CHECK-NEXT: struct S {
|
||||
# CHECK: struct S {
|
||||
# CHECK: }
|
||||
# CHECK-NEXT: (lldb) type lookup Outer::Inner2::S
|
||||
# CHECK-NEXT: struct S {
|
||||
# CHECK-NEXT: char a[5];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) type lookup Outer::A
|
||||
# CHECK-NEXT: struct A {
|
||||
# CHECK-NEXT: char a[6];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) type lookup A
|
||||
# CHECK-NEXT: struct A {
|
||||
# CHECK: struct A {
|
||||
# CHECK: }
|
||||
# CHECK-NEXT: (lldb) type lookup ::A
|
||||
# CHECK-NEXT: struct A {
|
||||
# CHECK-NEXT: char a[7];
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: (lldb) expr sizeof(S)
|
||||
# CHECK-NEXT: (__size_t) $0 = 1
|
||||
# CHECK-NEXT: (lldb) expr sizeof(A)
|
||||
# CHECK-NEXT: (__size_t) $1 = 7
|
||||
Loading…
x
Reference in New Issue
Block a user