[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/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) {