Initial support for reading type information from PDBs.

This implements a PDBASTParser and corresponding logic in
SymbolFilePDB to do type lookup by name.  This is just a first
pass and leaves many aspects of type lookup unimplemented, and
just focuses on laying the framework.  With this patch, you should
be able to lookup basic types by name from a PDB.

Full class definitions are not completed yet, we will instead
just return a forward declaration of the class.

Differential Revision: http://reviews.llvm.org/D18848
Reviewed by: Greg Clayton

llvm-svn: 266392
This commit is contained in:
Zachary Turner 2016-04-15 00:21:26 +00:00
parent 5ffa13c473
commit 42dff79068
11 changed files with 844 additions and 20 deletions

View File

@ -37,6 +37,7 @@
#include "lldb/lldb-enumerations.h"
class DWARFASTParserClang;
class PDBASTParser;
namespace lldb_private {
@ -524,6 +525,8 @@ public:
//------------------------------------------------------------------
DWARFASTParser *
GetDWARFParser() override;
PDBASTParser *
GetPDBParser();
//------------------------------------------------------------------
// ClangASTContext callbacks for external source lookups.
@ -696,7 +699,13 @@ public:
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 @@ protected:
std::unique_ptr<clang::SelectorTable> m_selector_table_ap;
std::unique_ptr<clang::Builtin::Context> m_builtins_ap;
std::unique_ptr<DWARFASTParserClang> m_dwarf_ast_parser_ap;
std::unique_ptr<PDBASTParser> m_pdb_ast_parser_ap;
std::unique_ptr<ClangASTSource> m_scratch_ast_source_ap;
std::unique_ptr<clang::MangleContext> m_mangle_ctx_ap;
CompleteTagDeclCallback m_callback_tag_decl;

View File

@ -2,5 +2,6 @@ set(LLVM_PRIVATE_LINK_COMPONENTS
DebugInfoPDB)
add_lldb_library(lldbPluginSymbolFilePDB
PDBASTParser.cpp
SymbolFilePDB.cpp
)

View File

@ -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<PDBSymbolTypeUDT>(&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>(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<PDBSymbolTypeEnum>(&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<PDBSymbolData>();
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>(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<PDBSymbolTypeTypedef>(&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>(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<PDBSymbolTypeFunctionSig>(&type))
{
auto arg_enum = func_sig->getArguments();
uint32_t num_args = arg_enum->getChildCount();
std::vector<CompilerType> 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<Type>(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<PDBSymbolTypeArray>(&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<Type>(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);
}

View File

@ -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_

View File

@ -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 <regex>
using namespace lldb_private;
@ -116,6 +127,10 @@ SymbolFilePDB::InitializeObject()
{
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<ClangASTContext>(type_system);
m_tu_decl_ctx_up = llvm::make_unique<CompilerDeclContext>(type_system, clang_type_system->GetTranslationUnitDecl());
}
uint32_t
@ -245,7 +260,25 @@ SymbolFilePDB::ParseVariablesForContext(const lldb_private::SymbolContext &sc)
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<ClangASTContext>(type_system);
if (!clang_type_system)
return nullptr;
PDBASTParser *pdb = llvm::dyn_cast<PDBASTParser>(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 @@ SymbolFilePDB::GetDeclForUID(lldb::user_id_t uid)
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 @@ SymbolFilePDB::FindTypes(const lldb_private::SymbolContext &sc, const lldb_priva
llvm::DenseSet<lldb_private::SymbolFile *> &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 &regex, 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<llvm::IPDBEnumSymbols> 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<llvm::PDBSymbolTypeEnum>(result.get()))
type_name = enum_type->getName();
else if (auto typedef_type = llvm::dyn_cast<llvm::PDBSymbolTypeTypedef>(result.get()))
type_name = typedef_type->getName();
else if (auto class_type = llvm::dyn_cast<llvm::PDBSymbolTypeUDT>(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<llvm::IPDBEnumSymbols> 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<lldb_private::CompilerContext> &context, bool append,
SymbolFilePDB::FindTypes(const std::vector<lldb_private::CompilerContext> &contexts, bool append,
lldb_private::TypeMap &types)
{
return size_t();
return 0;
}
lldb_private::TypeList *
@ -428,6 +570,18 @@ SymbolFilePDB::GetPluginVersion()
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 @@ SymbolFilePDB::ParseCompileUnitLineTable(const lldb_private::SymbolContext &sc,
// 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<uint32_t, uint32_t> index_map;
llvm::DenseMap<uint32_t, uint32_t> index_map;
BuildSupportFileIdToSupportFileIndexMap(*cu, index_map);
auto line_table = llvm::make_unique<LineTable>(sc.comp_unit);
@ -555,7 +709,7 @@ SymbolFilePDB::ParseCompileUnitLineTable(const lldb_private::SymbolContext &sc,
void
SymbolFilePDB::BuildSupportFileIdToSupportFileIndexMap(const llvm::PDBSymbolCompiland &cu,
std::unordered_map<uint32_t, uint32_t> &index_map) const
llvm::DenseMap<uint32_t, uint32_t> &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

View File

@ -10,11 +10,10 @@
#ifndef lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
#define lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
#include <unordered_map>
#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 @@ public:
uint32_t
GetPluginVersion() override;
llvm::IPDBSession &
GetPDBSession();
const llvm::IPDBSession &
GetPDBSession() const;
private:
lldb::CompUnitSP
ParseCompileUnitForSymIndex(uint32_t id);
@ -179,12 +184,21 @@ private:
void
BuildSupportFileIdToSupportFileIndexMap(const llvm::PDBSymbolCompiland &cu,
std::unordered_map<uint32_t, uint32_t> &index_map) const;
llvm::DenseMap<uint32_t, uint32_t> &index_map) const;
std::unordered_map<uint32_t, lldb::CompUnitSP> m_comp_units;
void
FindTypesByRegex(const std::string &regex, uint32_t max_matches, lldb_private::TypeMap &types);
void
FindTypesByName(const std::string &name, uint32_t max_matches, lldb_private::TypeMap &types);
llvm::DenseMap<uint32_t, lldb::CompUnitSP> m_comp_units;
llvm::DenseMap<uint32_t, lldb::TypeSP> m_types;
std::vector<lldb::TypeSP> m_builtin_types;
std::unique_ptr<llvm::IPDBSession> m_session_up;
uint32_t m_cached_compile_unit_count;
std::unique_ptr<lldb_private::CompilerDeclContext> m_tu_decl_ctx_up;
};
#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_

View File

@ -90,6 +90,7 @@
#include "lldb/Target/Target.h"
#include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h"
#include "Plugins/SymbolFile/PDB/PDBASTParser.h"
#include <stdio.h>
@ -3442,7 +3443,27 @@ ClangASTContext::IsObjCObjectOrInterfaceType (const CompilerType& type)
}
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)
{
@ -5204,7 +5225,7 @@ ClangASTContext::GetNumChildren (lldb::opaque_compiler_type_t type, bool omit_em
CompilerType
ClangASTContext::GetBuiltinTypeByName (const ConstString &name)
{
return GetBasicType (GetBasicTypeEnumeration (name));
return GetBasicType(GetBasicTypeEnumeration(name));
}
lldb::BasicType
@ -9472,6 +9493,13 @@ ClangASTContext::GetDWARFParser()
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,

View File

@ -5,6 +5,8 @@ add_lldb_unittest(SymbolFilePDBTests
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}")

View File

@ -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);
}

View File

@ -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 <objbase.h>
#endif
#include <algorithm>
extern const char *TestMainArgv0;
using namespace lldb_private;
@ -42,13 +48,17 @@ public:
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 @@ public:
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
{
SymbolFilePDB::Terminate();
ClangASTContext::Initialize();
SymbolFileDWARF::Terminate();
ObjectFilePECOFF::Terminate();
HostInfo::Terminate();
#if defined(_MSC_VER)
::CoUninitialize();
#endif
SymbolFilePDB::Terminate();
SymbolFileDWARF::Terminate();
ObjectFilePECOFF::Terminate();
}
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 @@ protected:
}
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<llvm::PDBSymbolData>(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 @@ TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchSpecific))
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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
const llvm::IPDBSession &session = symfile->GetPDBSession();
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
const llvm::IPDBSession &session = symfile->GetPDBSession();
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
const llvm::IPDBSession &session = symfile->GetPDBSession();
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
const llvm::IPDBSession &session = symfile->GetPDBSession();
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
const llvm::IPDBSession &session = symfile->GetPDBSession();
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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<ClangASTContext>(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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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<Module>(fspec, aspec);
SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile());
SymbolContext sc;
llvm::DenseSet<SymbolFile *> 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());
}