Index: include/lldb/Symbol/ClangASTContext.h =================================================================== --- include/lldb/Symbol/ClangASTContext.h +++ include/lldb/Symbol/ClangASTContext.h @@ -37,6 +37,7 @@ #include "lldb/lldb-enumerations.h" class DWARFASTParserClang; +class PDBASTParser; namespace lldb_private { @@ -524,6 +525,8 @@ //------------------------------------------------------------------ DWARFASTParser * GetDWARFParser() override; + PDBASTParser * + GetPDBParser(); //------------------------------------------------------------------ // ClangASTContext callbacks for external source lookups. @@ -696,7 +699,13 @@ bool IsPolymorphicClass (lldb::opaque_compiler_type_t type) override; - + + static bool + IsClassType(lldb::opaque_compiler_type_t type); + + static bool + IsEnumType(lldb::opaque_compiler_type_t type); + bool IsPossibleDynamicType(lldb::opaque_compiler_type_t type, CompilerType *target_type, // Can pass nullptr @@ -1189,6 +1198,7 @@ std::unique_ptr m_selector_table_ap; std::unique_ptr m_builtins_ap; std::unique_ptr m_dwarf_ast_parser_ap; + std::unique_ptr m_pdb_ast_parser_ap; std::unique_ptr m_scratch_ast_source_ap; std::unique_ptr m_mangle_ctx_ap; CompleteTagDeclCallback m_callback_tag_decl; Index: source/Plugins/SymbolFile/PDB/CMakeLists.txt =================================================================== --- source/Plugins/SymbolFile/PDB/CMakeLists.txt +++ source/Plugins/SymbolFile/PDB/CMakeLists.txt @@ -2,5 +2,6 @@ DebugInfoPDB) add_lldb_library(lldbPluginSymbolFilePDB + PDBASTParser.cpp SymbolFilePDB.cpp ) Index: source/Plugins/SymbolFile/PDB/PDBASTParser.h =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/PDB/PDBASTParser.h @@ -0,0 +1,55 @@ +//===-- PDBASTParser.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H +#define LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H + +#include "lldb/lldb-forward.h" + +#include "lldb/Symbol/ClangASTImporter.h" + +namespace clang +{ +class CharUnits; +class CXXRecordDecl; +class FieldDecl; +class RecordDecl; +} + +namespace lldb_private +{ +class ClangASTContext; +class CompilerType; +} + +namespace llvm +{ +class PDBSymbol; +class PDBSymbolData; +class PDBSymbolTypeBuiltin; +} + +class PDBASTParser +{ +public: + PDBASTParser(lldb_private::ClangASTContext &ast); + ~PDBASTParser(); + + lldb::TypeSP + CreateLLDBTypeFromPDBType(const llvm::PDBSymbol &type); + +private: + bool + AddEnumValue(lldb_private::CompilerType enum_type, const llvm::PDBSymbolData &data) const; + + lldb_private::ClangASTContext &m_ast; + lldb_private::ClangASTImporter m_ast_importer; +}; + +#endif // SymbolFileDWARF_DWARFASTParserClang_h_ Index: source/Plugins/SymbolFile/PDB/PDBASTParser.cpp =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -0,0 +1,236 @@ +//===-- PDBASTParser.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PDBASTParser.h" + +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/Declaration.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeSystem.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace +{ +int +TranslateUdtKind(PDB_UdtType pdb_kind) +{ + switch (pdb_kind) + { + case PDB_UdtType::Class: + return clang::TTK_Class; + case PDB_UdtType::Struct: + return clang::TTK_Struct; + case PDB_UdtType::Union: + return clang::TTK_Union; + case PDB_UdtType::Interface: + return clang::TTK_Interface; + } + return clang::TTK_Class; +} + +lldb::Encoding +TranslateBuiltinEncoding(PDB_BuiltinType type) +{ + switch (type) + { + case PDB_BuiltinType::Float: + return lldb::eEncodingIEEE754; + case PDB_BuiltinType::Int: + case PDB_BuiltinType::Long: + case PDB_BuiltinType::Char: + return lldb::eEncodingSint; + case PDB_BuiltinType::Bool: + case PDB_BuiltinType::UInt: + case PDB_BuiltinType::ULong: + case PDB_BuiltinType::HResult: + return lldb::eEncodingUint; + default: + return lldb::eEncodingInvalid; + } +} +} + +PDBASTParser::PDBASTParser(lldb_private::ClangASTContext &ast) : m_ast(ast) +{ +} + +PDBASTParser::~PDBASTParser() +{ +} + +// DebugInfoASTParser interface + +lldb::TypeSP +PDBASTParser::CreateLLDBTypeFromPDBType(const llvm::PDBSymbol &type) +{ + // PDB doesn't maintain enough information to robustly rebuild the entire + // tree, and this is most problematic when it comes to figure out the + // right DeclContext to put a type in. So for now, everything goes in + // the translation unit decl as a fully qualified type. + clang::DeclContext *tu_decl_ctx = m_ast.GetTranslationUnitDecl(); + Declaration decl; + + if (auto udt = llvm::dyn_cast(&type)) + { + AccessType access = lldb::eAccessPublic; + PDB_UdtType udt_kind = udt->getUdtKind(); + + if (udt_kind == PDB_UdtType::Class) + access = lldb::eAccessPrivate; + + CompilerType clang_type = + m_ast.CreateRecordType(tu_decl_ctx, access, udt->getName().c_str(), TranslateUdtKind(udt_kind), + lldb::eLanguageTypeC_plus_plus, nullptr); + + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); + + return std::make_shared(type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(udt->getName()), + udt->getLength(), nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + clang_type, Type::eResolveStateForward); + } + else if (auto enum_type = llvm::dyn_cast(&type)) + { + std::string name = enum_type->getName(); + lldb::Encoding encoding = TranslateBuiltinEncoding(enum_type->getBuiltinType()); + uint64_t bytes = enum_type->getLength(); + CompilerType builtin_type = m_ast.GetBuiltinTypeForEncodingAndBitSize(encoding, bytes * 8); + + CompilerType ast_enum = m_ast.CreateEnumerationType(name.c_str(), tu_decl_ctx, decl, builtin_type); + auto enum_values = enum_type->findAllChildren(); + while (auto enum_value = enum_values->getNext()) + { + if (enum_value->getDataKind() != PDB_DataKind::Constant) + continue; + AddEnumValue(ast_enum, *enum_value); + } + + return std::make_shared(type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name), bytes, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ast_enum, Type::eResolveStateFull); + } + else if (auto type_def = llvm::dyn_cast(&type)) + { + Type *target_type = m_ast.GetSymbolFile()->ResolveTypeUID(type_def->getTypeId()); + std::string name = type_def->getName(); + uint64_t bytes = type_def->getLength(); + if (!target_type) + return nullptr; + CompilerType target_ast_type = target_type->GetFullCompilerType(); + CompilerDeclContext target_decl_ctx = m_ast.GetSymbolFile()->GetDeclContextForUID(target_type->GetID()); + CompilerType ast_typedef = m_ast.CreateTypedefType(target_ast_type, name.c_str(), target_decl_ctx); + return std::make_shared(type_def->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name), bytes, + nullptr, target_type->GetID(), Type::eEncodingIsTypedefUID, decl, ast_typedef, + Type::eResolveStateFull); + } + else if (auto func_sig = llvm::dyn_cast(&type)) + { + auto arg_enum = func_sig->getArguments(); + uint32_t num_args = arg_enum->getChildCount(); + std::vector arg_list(num_args); + while (auto arg = arg_enum->getNext()) + { + Type *arg_type = m_ast.GetSymbolFile()->ResolveTypeUID(arg->getSymIndexId()); + // If there's some error looking up one of the dependent types of this function signature, bail. + if (!arg_type) + return nullptr; + CompilerType arg_ast_type = arg_type->GetFullCompilerType(); + arg_list.push_back(arg_ast_type); + } + auto pdb_return_type = func_sig->getReturnType(); + Type *return_type = m_ast.GetSymbolFile()->ResolveTypeUID(pdb_return_type->getSymIndexId()); + // If there's some error looking up one of the dependent types of this function signature, bail. + if (!return_type) + return nullptr; + CompilerType return_ast_type = return_type->GetFullCompilerType(); + uint32_t type_quals = 0; + if (func_sig->isConstType()) + type_quals |= clang::Qualifiers::Const; + if (func_sig->isVolatileType()) + type_quals |= clang::Qualifiers::Volatile; + CompilerType func_sig_ast_type = + m_ast.CreateFunctionType(return_ast_type, &arg_list[0], num_args, false, type_quals); + + return std::make_shared(func_sig->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(), 0, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, func_sig_ast_type, + Type::eResolveStateFull); + } + else if (auto array_type = llvm::dyn_cast(&type)) + { + uint32_t num_elements = array_type->getCount(); + uint32_t element_uid = array_type->getElementType()->getSymIndexId(); + uint32_t bytes = array_type->getLength(); + + Type *element_type = m_ast.GetSymbolFile()->ResolveTypeUID(element_uid); + CompilerType element_ast_type = element_type->GetFullCompilerType(); + CompilerType array_ast_type = m_ast.CreateArrayType(element_ast_type, num_elements, false); + return std::make_shared(array_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(), bytes, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, array_ast_type, + Type::eResolveStateFull); + } + return nullptr; +} + +bool +PDBASTParser::AddEnumValue(CompilerType enum_type, const llvm::PDBSymbolData &enum_value) const +{ + Declaration decl; + Variant v = enum_value.getValue(); + std::string name = enum_value.getName(); + int64_t raw_value; + switch (v.Type) + { + case PDB_VariantType::Int8: + raw_value = v.Value.Int8; + break; + case PDB_VariantType::Int16: + raw_value = v.Value.Int16; + break; + case PDB_VariantType::Int32: + raw_value = v.Value.Int32; + break; + case PDB_VariantType::Int64: + raw_value = v.Value.Int64; + break; + case PDB_VariantType::UInt8: + raw_value = v.Value.UInt8; + break; + case PDB_VariantType::UInt16: + raw_value = v.Value.UInt16; + break; + case PDB_VariantType::UInt32: + raw_value = v.Value.UInt32; + break; + case PDB_VariantType::UInt64: + raw_value = v.Value.UInt64; + break; + default: + return false; + } + CompilerType underlying_type = m_ast.GetEnumerationIntegerType(enum_type.GetOpaqueQualType()); + uint32_t byte_size = m_ast.getASTContext()->getTypeSize(ClangUtil::GetQualType(underlying_type)); + return m_ast.AddEnumerationValueToEnumerationType(enum_type.GetOpaqueQualType(), underlying_type, decl, + name.c_str(), raw_value, byte_size * 8); +} Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.h =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.h +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.h @@ -10,11 +10,10 @@ #ifndef lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ #define lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ -#include - #include "lldb/Core/UserID.h" #include "lldb/Symbol/SymbolFile.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDB.h" @@ -170,6 +169,12 @@ uint32_t GetPluginVersion() override; + llvm::IPDBSession & + GetPDBSession(); + + const llvm::IPDBSession & + GetPDBSession() const; + private: lldb::CompUnitSP ParseCompileUnitForSymIndex(uint32_t id); @@ -179,12 +184,21 @@ void BuildSupportFileIdToSupportFileIndexMap(const llvm::PDBSymbolCompiland &cu, - std::unordered_map &index_map) const; + llvm::DenseMap &index_map) const; + + void + FindTypesByRegex(const std::string ®ex, uint32_t max_matches, lldb_private::TypeMap &types); + + void + FindTypesByName(const std::string &name, uint32_t max_matches, lldb_private::TypeMap &types); - std::unordered_map m_comp_units; + llvm::DenseMap m_comp_units; + llvm::DenseMap m_types; + std::vector m_builtin_types; std::unique_ptr m_session_up; uint32_t m_cached_compile_unit_count; + std::unique_ptr m_tu_decl_ctx_up; }; #endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -9,12 +9,16 @@ #include "SymbolFilePDB.h" +#include "clang/Lex/Lexer.h" + #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/TypeMap.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBLineNumber.h" @@ -26,6 +30,13 @@ #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +#include "Plugins/SymbolFile/PDB/PDBASTParser.h" + +#include using namespace lldb_private; @@ -116,6 +127,10 @@ { lldb::addr_t obj_load_address = m_obj_file->GetFileOffset(); m_session_up->setLoadAddress(obj_load_address); + + TypeSystem *type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(type_system); + m_tu_decl_ctx_up = llvm::make_unique(type_system, clang_type_system->GetTranslationUnitDecl()); } uint32_t @@ -245,7 +260,25 @@ lldb_private::Type * SymbolFilePDB::ResolveTypeUID(lldb::user_id_t type_uid) { - return nullptr; + auto find_result = m_types.find(type_uid); + if (find_result != m_types.end()) + return find_result->second.get(); + + TypeSystem *type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(type_system); + if (!clang_type_system) + return nullptr; + PDBASTParser *pdb = llvm::dyn_cast(clang_type_system->GetPDBParser()); + if (!pdb) + return nullptr; + + auto pdb_type = m_session_up->getSymbolById(type_uid); + if (pdb_type == nullptr) + return nullptr; + + lldb::TypeSP result = pdb->CreateLLDBTypeFromPDBType(*pdb_type); + m_types.insert(std::make_pair(type_uid, result)); + return result.get(); } bool @@ -264,13 +297,15 @@ lldb_private::CompilerDeclContext SymbolFilePDB::GetDeclContextForUID(lldb::user_id_t uid) { - return lldb_private::CompilerDeclContext(); + // PDB always uses the translation unit decl context for everything. We can improve this later + // but it's not easy because PDB doesn't provide a high enough level of type fidelity in this area. + return *m_tu_decl_ctx_up; } lldb_private::CompilerDeclContext SymbolFilePDB::GetDeclContextContainingUID(lldb::user_id_t uid) { - return lldb_private::CompilerDeclContext(); + return *m_tu_decl_ctx_up; } void @@ -376,14 +411,121 @@ llvm::DenseSet &searched_symbol_files, lldb_private::TypeMap &types) { - return uint32_t(); + if (!append) + types.Clear(); + if (!name) + return 0; + + searched_symbol_files.clear(); + searched_symbol_files.insert(this); + + std::string name_str = name.AsCString(); + + // If this might be a regex, we have to return EVERY symbol and process them one by one, which is going + // to destroy performance on large PDB files. So try really hard not to use a regex match. + if (name_str.find_first_of("[]?*.-+\\") != std::string::npos) + FindTypesByRegex(name_str, max_matches, types); + else + FindTypesByName(name_str, max_matches, types); + return types.GetSize(); +} + +void +SymbolFilePDB::FindTypesByRegex(const std::string ®ex, uint32_t max_matches, lldb_private::TypeMap &types) +{ + // When searching by regex, we need to go out of our way to limit the search space as much as possible, since + // the way this is implemented is by searching EVERYTHING in the PDB and manually doing a regex compare. PDB + // library isn't optimized for regex searches or searches across multiple symbol types at the same time, so the + // best we can do is to search enums, then typedefs, then classes one by one, and do a regex compare against all + // of them. + llvm::PDB_SymType tags_to_search[] = {llvm::PDB_SymType::Enum, llvm::PDB_SymType::Typedef, llvm::PDB_SymType::UDT}; + auto global = m_session_up->getGlobalScope(); + std::unique_ptr results; + + std::regex re(regex); + + uint32_t matches = 0; + + for (auto tag : tags_to_search) + { + results = global->findAllChildren(tag); + while (auto result = results->getNext()) + { + if (max_matches > 0 && matches >= max_matches) + break; + + std::string type_name; + if (auto enum_type = llvm::dyn_cast(result.get())) + type_name = enum_type->getName(); + else if (auto typedef_type = llvm::dyn_cast(result.get())) + type_name = typedef_type->getName(); + else if (auto class_type = llvm::dyn_cast(result.get())) + type_name = class_type->getName(); + else + { + // We're only looking for types that have names. Skip symbols, as well as + // unnamed types such as arrays, pointers, etc. + continue; + } + + if (!std::regex_match(type_name, re)) + continue; + + // This should cause the type to get cached and stored in the `m_types` lookup. + if (!ResolveTypeUID(result->getSymIndexId())) + continue; + + auto iter = m_types.find(result->getSymIndexId()); + if (iter == m_types.end()) + continue; + types.Insert(iter->second); + ++matches; + } + } +} + +void +SymbolFilePDB::FindTypesByName(const std::string &name, uint32_t max_matches, lldb_private::TypeMap &types) +{ + auto global = m_session_up->getGlobalScope(); + std::unique_ptr results; + results = global->findChildren(llvm::PDB_SymType::None, name.c_str(), llvm::PDB_NameSearchFlags::NS_Default); + + uint32_t matches = 0; + + while (auto result = results->getNext()) + { + if (max_matches > 0 && matches >= max_matches) + break; + switch (result->getSymTag()) + { + case llvm::PDB_SymType::Enum: + case llvm::PDB_SymType::UDT: + case llvm::PDB_SymType::Typedef: + break; + default: + // We're only looking for types that have names. Skip symbols, as well as + // unnamed types such as arrays, pointers, etc. + continue; + } + + // This should cause the type to get cached and stored in the `m_types` lookup. + if (!ResolveTypeUID(result->getSymIndexId())) + continue; + + auto iter = m_types.find(result->getSymIndexId()); + if (iter == m_types.end()) + continue; + types.Insert(iter->second); + ++matches; + } } size_t -SymbolFilePDB::FindTypes(const std::vector &context, bool append, +SymbolFilePDB::FindTypes(const std::vector &contexts, bool append, lldb_private::TypeMap &types) { - return size_t(); + return 0; } lldb_private::TypeList * @@ -428,6 +570,18 @@ return 1; } +llvm::IPDBSession & +SymbolFilePDB::GetPDBSession() +{ + return *m_session_up; +} + +const llvm::IPDBSession & +SymbolFilePDB::GetPDBSession() const +{ + return *m_session_up; +} + lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitForSymIndex(uint32_t id) { @@ -470,7 +624,7 @@ // ParseCompileUnitSupportFiles. But the underlying SDK gives us a globally unique // idenfitifier in the namespace of the PDB. So, we have to do a mapping so that we // can hand out indices. - std::unordered_map index_map; + llvm::DenseMap index_map; BuildSupportFileIdToSupportFileIndexMap(*cu, index_map); auto line_table = llvm::make_unique(sc.comp_unit); @@ -555,7 +709,7 @@ void SymbolFilePDB::BuildSupportFileIdToSupportFileIndexMap(const llvm::PDBSymbolCompiland &cu, - std::unordered_map &index_map) const + llvm::DenseMap &index_map) const { // This is a hack, but we need to convert the source id into an index into the support // files array. We don't want to do path comparisons to avoid basename / full path Index: source/Symbol/ClangASTContext.cpp =================================================================== --- source/Symbol/ClangASTContext.cpp +++ source/Symbol/ClangASTContext.cpp @@ -91,6 +91,7 @@ #include "lldb/Target/Target.h" #include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" +#include "Plugins/SymbolFile/PDB/PDBASTParser.h" #include @@ -3443,7 +3444,27 @@ } bool -ClangASTContext::IsPolymorphicClass (lldb::opaque_compiler_type_t type) +ClangASTContext::IsClassType(lldb::opaque_compiler_type_t type) +{ + if (!type) + return false; + clang::QualType qual_type(GetCanonicalQualType(type)); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + return (type_class == clang::Type::Record); +} + +bool +ClangASTContext::IsEnumType(lldb::opaque_compiler_type_t type) +{ + if (!type) + return false; + clang::QualType qual_type(GetCanonicalQualType(type)); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + return (type_class == clang::Type::Enum); +} + +bool +ClangASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { if (type) { @@ -5205,7 +5226,7 @@ CompilerType ClangASTContext::GetBuiltinTypeByName (const ConstString &name) { - return GetBasicType (GetBasicTypeEnumeration (name)); + return GetBasicType(GetBasicTypeEnumeration(name)); } lldb::BasicType @@ -9465,6 +9486,13 @@ return m_dwarf_ast_parser_ap.get(); } +PDBASTParser * +ClangASTContext::GetPDBParser() +{ + if (!m_pdb_ast_parser_ap) + m_pdb_ast_parser_ap.reset(new PDBASTParser(*this)); + return m_pdb_ast_parser_ap.get(); +} bool ClangASTContext::LayoutRecordType(void *baton, Index: unittests/SymbolFile/PDB/CMakeLists.txt =================================================================== --- unittests/SymbolFile/PDB/CMakeLists.txt +++ unittests/SymbolFile/PDB/CMakeLists.txt @@ -5,6 +5,8 @@ set(test_inputs test-pdb.exe test-pdb.pdb - test-dwarf.exe) + test-dwarf.exe + test-pdb-types.exe + test-pdb-types.pdb) add_unittest_inputs(SymbolFilePDBTests "${test_inputs}") Index: unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp =================================================================== --- /dev/null +++ unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp @@ -0,0 +1,86 @@ +// Compile with "cl /c /Zi /GR- /EHsc test-pdb-types.cpp" +// Link with "link test-pdb-types.obj /debug /nodefaultlib /entry:main /out:test-pdb-types.exe" + +using namespace std; + +// Sizes of builtin types +static const int sizeof_char = sizeof(char); +static const int sizeof_uchar = sizeof(unsigned char); +static const int sizeof_short = sizeof(short); +static const int sizeof_ushort = sizeof(unsigned short); +static const int sizeof_int = sizeof(int); +static const int sizeof_uint = sizeof(unsigned int); +static const int sizeof_long = sizeof(long); +static const int sizeof_ulong = sizeof(unsigned long); +static const int sizeof_longlong = sizeof(long long); +static const int sizeof_ulonglong = sizeof(unsigned long long); +static const int sizeof_int64 = sizeof(__int64); +static const int sizeof_uint64 = sizeof(unsigned __int64); +static const int sizeof_float = sizeof(float); +static const int sizeof_double = sizeof(double); +static const int sizeof_bool = sizeof(bool); +static const int sizeof_wchar = sizeof(wchar_t); + +enum Enum +{ + EValue1 = 1, + EValue2 = 2, +}; + +enum ShortEnum : short +{ + ESValue1 = 1, + ESValue2 = 2 +}; + +namespace NS +{ +class NSClass +{ + float f; + double d; +}; +} + +class Class +{ +public: + class NestedClass + { + Enum e; + }; + ShortEnum se; +}; + +int +test_func(int a, int b) +{ + return a + b; +} + +typedef Class ClassTypedef; +typedef NS::NSClass NSClassTypedef; +int GlobalArray[10]; + +static const int sizeof_NSClass = sizeof(NS::NSClass); +static const int sizeof_Class = sizeof(Class); +static const int sizeof_NestedClass = sizeof(Class::NestedClass); +static const int sizeof_Enum = sizeof(Enum); +static const int sizeof_ShortEnum = sizeof(ShortEnum); +static const int sizeof_ClassTypedef = sizeof(ClassTypedef); +static const int sizeof_NSClassTypedef = sizeof(NSClassTypedef); +static const int sizeof_GlobalArray = sizeof(GlobalArray); + +int +main(int argc, char **argv) +{ + ShortEnum e1; + Enum e2; + Class c1; + Class::NestedClass c2; + NS::NSClass c3; + + ClassTypedef t1; + NSClassTypedef t2; + return test_func(1, 2); +} Index: unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp =================================================================== --- unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp +++ unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp @@ -11,6 +11,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -20,6 +22,7 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/SymbolVendor.h" @@ -29,9 +32,12 @@ #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" #if defined(_MSC_VER) +#include "lldb/Host/windows/windows.h" #include #endif +#include + extern const char *TestMainArgv0; using namespace lldb_private; @@ -42,13 +48,17 @@ void SetUp() override { +// Initialize and TearDown the plugin every time, so we get a brand new +// AST every time so that modifications to the AST from each test don't +// leak into the next test. #if defined(_MSC_VER) ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif - HostInfoBase::Initialize(); + HostInfo::Initialize(); ObjectFilePECOFF::Initialize(); SymbolFileDWARF::Initialize(); + ClangASTContext::Initialize(); SymbolFilePDB::Initialize(); llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); @@ -57,24 +67,30 @@ m_pdb_test_exe = inputs_folder; m_dwarf_test_exe = inputs_folder; + m_types_test_exe = inputs_folder; llvm::sys::path::append(m_pdb_test_exe, "test-pdb.exe"); llvm::sys::path::append(m_dwarf_test_exe, "test-dwarf.exe"); + llvm::sys::path::append(m_types_test_exe, "test-pdb-types.exe"); } void TearDown() override { -#if defined(_MSC_VER) - ::CoUninitialize(); -#endif SymbolFilePDB::Terminate(); + ClangASTContext::Initialize(); SymbolFileDWARF::Terminate(); ObjectFilePECOFF::Terminate(); + HostInfo::Terminate(); + +#if defined(_MSC_VER) + ::CoUninitialize(); +#endif } protected: llvm::SmallString<128> m_pdb_test_exe; llvm::SmallString<128> m_dwarf_test_exe; + llvm::SmallString<128> m_types_test_exe; bool FileSpecMatchesAsBaseOrFull(const FileSpec &left, const FileSpec &right) const @@ -116,6 +132,35 @@ } return false; } + + int + GetGlobalConstantInteger(const llvm::IPDBSession &session, llvm::StringRef var) const + { + auto global = session.getGlobalScope(); + auto results = global->findChildren(llvm::PDB_SymType::Data, var, llvm::PDB_NameSearchFlags::NS_Default); + uint32_t count = results->getChildCount(); + if (count == 0) + return -1; + + auto item = results->getChildAtIndex(0); + auto symbol = llvm::dyn_cast(item.get()); + if (!symbol) + return -1; + llvm::Variant value = symbol->getValue(); + switch (value.Type) + { + case llvm::PDB_VariantType::Int16: + return value.Value.Int16; + case llvm::PDB_VariantType::Int32: + return value.Value.Int32; + case llvm::PDB_VariantType::UInt16: + return value.Value.UInt16; + case llvm::PDB_VariantType::UInt32: + return value.Value.UInt32; + default: + return 0; + } + } }; #if defined(HAVE_DIA_SDK) @@ -342,3 +387,196 @@ VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); } + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestSimpleClassTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString("Class"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_Class"), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNestedClassTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString("Class::NestedClass"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class::NestedClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NestedClass"), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestClassInNamespace)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString("NS::NSClass"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("NS::NSClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NSClass"), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestEnumTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet searched_files; + const char *EnumsToCheck[] = {"Enum", "ShortEnum"}; + for (auto Enum : EnumsToCheck) + { + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString(Enum), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP enum_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Enum), enum_type->GetName()); + CompilerType compiler_type = enum_type->GetFullCompilerType(); + EXPECT_TRUE(ClangASTContext::IsEnumType(compiler_type.GetOpaqueQualType())); + clang::EnumDecl *enum_decl = ClangASTContext::GetAsEnumDecl(compiler_type); + EXPECT_NE(nullptr, enum_decl); + EXPECT_EQ(2, std::distance(enum_decl->enumerator_begin(), enum_decl->enumerator_end())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Enum); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), enum_type->GetByteSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestArrayTypes)) +{ + // In order to get this test working, we need to support lookup by symbol name. Because array + // types themselves do not have names, only the symbols have names (i.e. the name of the array). +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestFunctionTypes)) +{ + // In order to get this test working, we need to support lookup by symbol name. Because array + // types themselves do not have names, only the symbols have names (i.e. the name of the array). +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestTypedefs)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + const llvm::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + + const char *TypedefsToCheck[] = {"ClassTypedef", "NSClassTypedef"}; + for (auto Typedef : TypedefsToCheck) + { + TypeMap results; + EXPECT_EQ(1, symfile->FindTypes(sc, ConstString(Typedef), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1, results.GetSize()); + lldb::TypeSP typedef_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Typedef), typedef_type->GetName()); + CompilerType compiler_type = typedef_type->GetFullCompilerType(); + ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(compiler_type.GetTypeSystem()); + EXPECT_TRUE(clang_type_system->IsTypedefType(compiler_type.GetOpaqueQualType())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Typedef); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), typedef_type->GetByteSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestRegexNameMatch)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + int num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); + EXPECT_GT(num_results, 1); + EXPECT_EQ(num_results, results.GetSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestMaxMatches)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + int num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); + // Try to limit ourselves from 1 to 10 results, otherwise we could be doing this thousands of times. + // The idea is just to make sure that for a variety of values, the number of limited results always + // comes out to the number we are expecting. + int iterations = std::min(num_results, 10); + for (int i = 1; i <= iterations; ++i) + { + int num_limited_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, i, searched_files, results); + EXPECT_EQ(i, num_limited_results); + EXPECT_EQ(num_limited_results, results.GetSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNullName)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet searched_files; + TypeMap results; + int num_results = symfile->FindTypes(sc, ConstString(), nullptr, false, 0, searched_files, results); + EXPECT_EQ(0, num_results); + EXPECT_EQ(0, results.GetSize()); +}