[NativePDB] Add the ability to dump dump global variables.
LLDB has the ability to display global variables, even without a running
process, via the target variable command. This is because global
variables are linker initialized, so their values are embedded directly
into the executables. This gives us great power for testing native PDB
functionality in a cross-platform manner, because we don't actually need
a running process. We can just create a target using an EXE file, and
display global variables. And global variables can have arbitrarily
complex types, so in theory we can fully exercise the type system,
record layout, and data formatters for native PDB files and PE/COFF
executables on any host platform, as long as our type does not require a
dynamic initializer.
This patch adds basic support for finding variables by name, and adds an
exhaustive test for fundamental data types and pointers / references to
fundamental data types.
Subsequent patches will extend this to typedefs, classes, pointers to
functions, and other cases.
Differential Revision: https://reviews.llvm.org/D53731
llvm-svn: 345373
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h
index eb303ad..f43e0f9 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h
@@ -32,7 +32,6 @@
// the beginning so that the types are all layout-compatible with each
// other, which is necessary in order to be able to safely access the tag
// member through any union member.
-
struct PdbCompilandId {
uint64_t tag : 8; // PDB_SymType::Compiland
uint64_t modi : 16; // 0-based index of module in PDB
@@ -41,11 +40,14 @@
struct PdbCuSymId {
uint64_t tag : 8; // PDB_SymType::Data, Function, Block, etc.
uint64_t
- offset : 32; // Offset of symbol's record in module stream. This is
- // offset by 4 from the CVSymbolArray's notion of offset
- // due to the debug magic at the beginning of the stream.
- uint64_t modi : 16; // 0-based index of module in PDB
+ offset : 30; // Offset of symbol's record in module stream. This is
+ // offset by 4 from the CVSymbolArray's notion of offset
+ // due to the debug magic at the beginning of the stream.
+ uint64_t global : 1; // True if this is from the globals stream.
+ uint64_t unused : 1;
+ uint64_t modi : 16; // For non-global, this is the 0-based index of module.
};
+
struct PdbTypeSymId {
uint64_t tag : 8; // PDB_SymType::FunctionSig, Enum, PointerType, etc.
uint64_t is_ipi : 8; // 1 if this value is from the IPI stream, 0 for TPI.
@@ -125,10 +127,21 @@
PdbSymUid uid;
uid.m_uid.cu_sym.modi = modi;
uid.m_uid.cu_sym.offset = offset;
+ uid.m_uid.cu_sym.global = false;
uid.m_uid.cu_sym.tag = static_cast<uint8_t>(type);
return uid;
}
+ static PdbSymUid makeGlobalVariableUid(uint32_t offset) {
+ PdbSymUid uid = {};
+ uid.m_uid.cu_sym.modi = 0;
+ uid.m_uid.cu_sym.offset = offset;
+ uid.m_uid.cu_sym.global = 1;
+ uid.m_uid.cu_sym.unused = 0;
+ uid.m_uid.cu_sym.tag = static_cast<uint8_t>(llvm::pdb::PDB_SymType::Data);
+ return uid;
+ }
+
static PdbSymUid makeCompilandId(llvm::codeview::ProcRefSym sym) {
// S_PROCREF symbols are 1-based
lldbassert(sym.Module > 0);
@@ -172,6 +185,11 @@
bool isCompiland() const {
return tag() == llvm::pdb::PDB_SymType::Compiland;
}
+ bool isGlobalVariable() const {
+ if (tag() != llvm::pdb::PDB_SymType::Data)
+ return false;
+ return static_cast<bool>(asCuSym().global);
+ }
llvm::pdb::PDB_SymType tag() const {
return static_cast<llvm::pdb::PDB_SymType>(m_uid.comp_id.tag);
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index fdc9330..77b99ef4 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -16,6 +16,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamBuffer.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/ClangASTImporter.h"
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
@@ -24,6 +25,8 @@
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
@@ -887,6 +890,131 @@
return CreateAndCacheType(type_uid);
}
+static DWARFExpression MakeGlobalLocationExpression(uint16_t section,
+ uint32_t offset,
+ ModuleSP module) {
+ assert(section > 0);
+ assert(module);
+
+ const ArchSpec &architecture = module->GetArchitecture();
+ ByteOrder byte_order = architecture.GetByteOrder();
+ uint32_t address_size = architecture.GetAddressByteSize();
+ uint32_t byte_size = architecture.GetDataByteSize();
+ assert(byte_order != eByteOrderInvalid && address_size != 0);
+
+ RegisterKind register_kind = eRegisterKindDWARF;
+ StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order);
+ stream.PutHex8(DW_OP_addr);
+
+ SectionList *section_list = module->GetSectionList();
+ assert(section_list);
+
+ // Section indices in PDB are 1-based, but in DWARF they are 0-based, so we
+ // need to subtract 1.
+ uint32_t section_idx = section - 1;
+ if (section_idx >= section_list->GetSize())
+ return DWARFExpression(nullptr);
+
+ auto section_ptr = section_list->GetSectionAtIndex(section_idx);
+ if (!section_ptr)
+ return DWARFExpression(nullptr);
+
+ stream.PutMaxHex64(section_ptr->GetFileAddress() + offset, address_size,
+ byte_order);
+ DataBufferSP buffer =
+ std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize());
+ DataExtractor extractor(buffer, byte_order, address_size, byte_size);
+ DWARFExpression result(module, extractor, nullptr, 0, buffer->GetByteSize());
+ result.SetRegisterKind(register_kind);
+ return result;
+}
+
+VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbSymUid var_uid) {
+ const PdbCuSymId &cu_sym = var_uid.asCuSym();
+ lldbassert(cu_sym.global);
+ CVSymbol sym = m_index->symrecords().readRecord(cu_sym.offset);
+ lldb::ValueType scope = eValueTypeInvalid;
+ TypeIndex ti;
+ llvm::StringRef name;
+ lldb::addr_t addr = 0;
+ uint16_t section = 0;
+ uint32_t offset = 0;
+ bool is_external = false;
+ switch (sym.kind()) {
+ case S_GDATA32:
+ is_external = true;
+ LLVM_FALLTHROUGH;
+ case S_LDATA32: {
+ DataSym ds(sym.kind());
+ llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, ds));
+ ti = ds.Type;
+ scope = (sym.kind() == S_GDATA32) ? eValueTypeVariableGlobal
+ : eValueTypeVariableStatic;
+ name = ds.Name;
+ section = ds.Segment;
+ offset = ds.DataOffset;
+ addr = m_index->MakeVirtualAddress(ds.Segment, ds.DataOffset);
+ break;
+ }
+ case S_GTHREAD32:
+ is_external = true;
+ LLVM_FALLTHROUGH;
+ case S_LTHREAD32: {
+ ThreadLocalDataSym tlds(sym.kind());
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, tlds));
+ ti = tlds.Type;
+ name = tlds.Name;
+ section = tlds.Segment;
+ offset = tlds.DataOffset;
+ addr = m_index->MakeVirtualAddress(tlds.Segment, tlds.DataOffset);
+ scope = eValueTypeVariableThreadLocal;
+ break;
+ }
+ default:
+ llvm_unreachable("unreachable!");
+ }
+
+ CompUnitSP comp_unit;
+ llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(addr);
+ if (modi) {
+ PdbSymUid cuid = PdbSymUid::makeCompilandId(*modi);
+ CompilandIndexItem &cci = m_index->compilands().GetOrCreateCompiland(cuid);
+ comp_unit = GetOrCreateCompileUnit(cci);
+ }
+
+ Declaration decl;
+ PDB_SymType pdbst = GetPdbSymType(m_index->tpi(), ti);
+ PdbSymUid tuid = PdbSymUid::makeTypeSymId(pdbst, ti, false);
+ SymbolFileTypeSP type_sp =
+ std::make_shared<SymbolFileType>(*this, tuid.toOpaqueId());
+ Variable::RangeList ranges;
+
+ DWARFExpression location = MakeGlobalLocationExpression(
+ section, offset, GetObjectFile()->GetModule());
+
+ std::string global_name("::");
+ global_name += name;
+ VariableSP var_sp = std::make_shared<Variable>(
+ var_uid.toOpaqueId(), name.str().c_str(), global_name.c_str(), type_sp,
+ scope, comp_unit.get(), ranges, &decl, location, is_external, false,
+ false);
+ var_sp->SetLocationIsConstantValueData(false);
+
+ return var_sp;
+}
+
+VariableSP SymbolFileNativePDB::GetOrCreateGlobalVariable(PdbSymUid var_uid) {
+ lldbassert(var_uid.isGlobalVariable());
+
+ auto emplace_result =
+ m_global_vars.try_emplace(var_uid.toOpaqueId(), nullptr);
+ if (emplace_result.second)
+ emplace_result.first->second = CreateGlobalVariable(var_uid);
+
+ return emplace_result.first->second;
+}
+
lldb::TypeSP
SymbolFileNativePDB::GetOrCreateType(llvm::codeview::TypeIndex ti) {
PDB_SymType pdbst = GetPdbSymType(m_index->tpi(), ti);
@@ -1146,6 +1274,32 @@
return 0;
}
+uint32_t SymbolFileNativePDB::FindGlobalVariables(
+ const ConstString &name, const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+ using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
+
+ std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName(
+ name.GetStringRef(), m_index->symrecords());
+ for (const SymbolAndOffset &result : results) {
+ VariableSP var;
+ switch (result.second.kind()) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GTHREAD32:
+ case SymbolKind::S_LTHREAD32: {
+ PdbSymUid uid = PdbSymUid::makeGlobalVariableUid(result.first);
+ var = GetOrCreateGlobalVariable(uid);
+ variables.AddVariable(var);
+ break;
+ }
+ default:
+ continue;
+ }
+ }
+ return variables.GetSize();
+}
+
uint32_t SymbolFileNativePDB::FindFunctions(
const ConstString &name, const CompilerDeclContext *parent_decl_ctx,
FunctionNameType name_type_mask, bool include_inlines, bool append,
@@ -1239,19 +1393,21 @@
auto iter = m_types.find(type_uid);
// lldb should not be passing us non-sensical type uids. the only way it
// could have a type uid in the first place is if we handed it out, in which
- // case we should know about the type. So this is not a get-or-create type
- // operation, it is strictly a get, and the type is guaranteed to exist.
- //
- // However, since the implementation is not yet complete, we don't currently
- // support all possible use cases. For example, we currently create all
- // functions with indices of 0 for the signature type simply because this is
- // not yet implemented. At the time the function object is created we should
- // be creating an lldb::TypeSP for this, adding it to the m_types, and
- // returning a valid Type object for it and putting it in this map. Once all
- // cases like this are handled, we can promote this to an assert.
- if (iter == m_types.end())
+ // case we should know about the type. However, that doesn't mean we've
+ // instantiated it yet. We can vend out a UID for a future type. So if the
+ // type doesn't exist, let's instantiate it now.
+ if (iter != m_types.end())
+ return &*iter->second;
+
+ PdbSymUid uid = PdbSymUid::fromOpaqueId(type_uid);
+ lldbassert(uid.isTypeSym(uid.tag()));
+ const PdbTypeSymId &type_id = uid.asTypeSym();
+ TypeIndex ti(type_id.index);
+ if (ti.isNoneType())
return nullptr;
- return &*iter->second;
+
+ TypeSP type_sp = CreateAndCacheType(uid);
+ return &*type_sp;
}
bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) {
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index 9a67740c..67006b2 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -103,6 +103,11 @@
size_t ParseFunctionBlocks(const SymbolContext &sc) override;
+ uint32_t FindGlobalVariables(const ConstString &name,
+ const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ VariableList &variables) override;
+
size_t ParseTypes(const SymbolContext &sc) override;
size_t ParseVariablesForContext(const SymbolContext &sc) override {
return 0;
@@ -175,11 +180,13 @@
lldb::CompUnitSP GetOrCreateCompileUnit(const CompilandIndexItem &cci);
lldb::TypeSP GetOrCreateType(PdbSymUid type_uid);
lldb::TypeSP GetOrCreateType(llvm::codeview::TypeIndex ti);
+ lldb::VariableSP GetOrCreateGlobalVariable(PdbSymUid var_uid);
lldb::FunctionSP CreateFunction(PdbSymUid func_uid, const SymbolContext &sc);
lldb::CompUnitSP CreateCompileUnit(const CompilandIndexItem &cci);
lldb::TypeSP CreateType(PdbSymUid type_uid);
lldb::TypeSP CreateAndCacheType(PdbSymUid type_uid);
+ lldb::VariableSP CreateGlobalVariable(PdbSymUid var_uid);
llvm::BumpPtrAllocator m_allocator;
@@ -193,6 +200,7 @@
llvm::DenseMap<lldb::user_id_t, clang::TagDecl *> m_uid_to_decl;
+ llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_global_vars;
llvm::DenseMap<lldb::user_id_t, lldb::FunctionSP> m_functions;
llvm::DenseMap<lldb::user_id_t, lldb::CompUnitSP> m_compilands;
llvm::DenseMap<lldb::user_id_t, lldb::TypeSP> m_types;