[NativePDB] Add support for local variables.
This patch adds support for parsing and evaluating local variables.
using the native pdb plugin.
Differential Revision: https://reviews.llvm.org/D55575
llvm-svn: 349067
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 898b430..5db4c00 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -66,6 +66,15 @@
using namespace llvm::codeview;
using namespace llvm::pdb;
+namespace {
+struct VariableInfo {
+ llvm::StringRef name;
+ TypeIndex type;
+ llvm::Optional<DWARFExpression> location;
+ llvm::Optional<Variable::RangeList> ranges;
+};
+} // namespace
+
static lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
switch (lang) {
case PDB_Lang::Cpp:
@@ -314,6 +323,94 @@
}
}
+static Variable::RangeList
+MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range,
+ llvm::ArrayRef<LocalVariableAddrGap> gaps) {
+ lldb::addr_t start =
+ index.MakeVirtualAddress(range.ISectStart, range.OffsetStart);
+ lldb::addr_t end = start + range.Range;
+
+ Variable::RangeList result;
+ while (!gaps.empty()) {
+ const LocalVariableAddrGap &gap = gaps.front();
+
+ lldb::addr_t size = gap.GapStartOffset - start;
+ result.Append(start, size);
+ start += gap.Range;
+ gaps = gaps.drop_front();
+ }
+
+ result.Append(start, end);
+ return result;
+}
+
+static VariableInfo GetVariableInformation(PdbIndex &index,
+ PdbCompilandSymId var_id,
+ ModuleSP module,
+ bool get_location_info) {
+ VariableInfo result;
+ CVSymbol sym = index.ReadSymbolRecord(var_id);
+
+ if (sym.kind() == S_REGREL32) {
+ RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
+ result.type = reg.Type;
+ result.name = reg.Name;
+ if (get_location_info) {
+ result.location =
+ MakeRegRelLocationExpression(reg.Register, reg.Offset, module);
+ result.ranges.emplace();
+ }
+ return result;
+ }
+
+ if (sym.kind() == S_REGISTER) {
+ RegisterSym reg(SymbolRecordKind::RegisterSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
+ result.type = reg.Index;
+ result.name = reg.Name;
+ if (get_location_info) {
+ result.location =
+ MakeEnregisteredLocationExpression(reg.Register, module);
+ result.ranges.emplace();
+ }
+ return result;
+ }
+
+ if (sym.kind() == S_LOCAL) {
+ LocalSym local(SymbolRecordKind::LocalSym);
+ cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
+ result.type = local.Type;
+ result.name = local.Name;
+
+ if (!get_location_info)
+ return result;
+
+ PdbCompilandSymId loc_specifier_id(var_id.modi,
+ var_id.offset + sym.RecordData.size());
+ CVSymbol loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id);
+ if (loc_specifier_cvs.kind() == S_DEFRANGE_FRAMEPOINTER_REL) {
+ DefRangeFramePointerRelSym loc(
+ SymbolRecordKind::DefRangeFramePointerRelSym);
+ cantFail(SymbolDeserializer::deserializeAs<DefRangeFramePointerRelSym>(
+ loc_specifier_cvs, loc));
+ // FIXME: The register needs to come from the S_FRAMEPROC symbol.
+ result.location =
+ MakeRegRelLocationExpression(RegisterId::RSP, loc.Offset, module);
+ result.ranges = MakeRangeList(index, loc.Range, loc.Gaps);
+ } else {
+ // FIXME: Handle other kinds
+ llvm::APSInt value;
+ value = 42;
+ result.location = MakeConstantLocationExpression(
+ TypeIndex::Int32(), index.tpi(), value, module);
+ }
+ return result;
+ }
+ llvm_unreachable("Symbol is not a local variable!");
+ return result;
+}
+
void SymbolFileNativePDB::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance,
@@ -520,8 +617,45 @@
return count;
}
+Block &SymbolFileNativePDB::CreateBlock(PdbCompilandSymId block_id) {
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
+ CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
+ clang::DeclContext *parent_decl_ctx = m_clang->GetTranslationUnitDecl();
+
+ if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) {
+ // This is a function. It must be global. Creating the Function entry for
+ // it automatically creates a block for it.
+ CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii);
+ return GetOrCreateFunction(block_id, *comp_unit)->GetBlock(false);
+ }
+
+ lldbassert(sym.kind() == S_BLOCK32);
+
+ // This is a block. Its parent is either a function or another block. In
+ // either case, its parent can be viewed as a block (e.g. a function contains
+ // 1 big block. So just get the parent block and add this block to it.
+ BlockSym block(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<BlockSym>(sym, block));
+ lldbassert(block.Parent != 0);
+ PdbCompilandSymId parent_id(block_id.modi, block.Parent);
+ Block &parent_block = GetOrCreateBlock(parent_id);
+
+ lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id);
+ BlockSP child_block = std::make_shared<Block>(opaque_block_uid);
+ parent_block.AddChild(child_block);
+ CompilerDeclContext cdc = GetDeclContextForUID(parent_block.GetID());
+ parent_decl_ctx =
+ static_cast<clang::DeclContext *>(cdc.GetOpaqueDeclContext());
+ clang::BlockDecl *block_decl =
+ m_clang->CreateBlockDeclaration(parent_decl_ctx);
+
+ m_blocks.insert({opaque_block_uid, child_block});
+ m_uid_to_decl.insert({opaque_block_uid, block_decl});
+ return *child_block;
+}
+
lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbCompilandSymId func_id,
- const SymbolContext &sc) {
+ CompileUnit &comp_unit) {
const CompilandIndexItem *cci =
m_index->compilands().GetCompiland(func_id.modi);
lldbassert(cci);
@@ -535,7 +669,7 @@
return nullptr;
AddressRange func_range(file_vm_addr, sol.length,
- sc.module_sp->GetSectionList());
+ comp_unit.GetModule()->GetSectionList());
if (!func_range.GetBaseAddress().IsValid())
return nullptr;
@@ -546,27 +680,22 @@
PdbTypeSymId sig_id(proc.FunctionType, false);
Mangled mangled(proc.Name);
FunctionSP func_sp = std::make_shared<Function>(
- sc.comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled,
+ &comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled,
func_type.get(), func_range);
- sc.comp_unit->AddFunction(func_sp);
+ comp_unit.AddFunction(func_sp);
+
+ user_id_t opaque_func_uid = toOpaqueUid(func_id);
clang::StorageClass storage = clang::SC_None;
if (sym_record.kind() == S_LPROC32)
storage = clang::SC_Static;
- // There are two ways we could retrieve the parameter list. The first is by
- // iterating the arguments on the function signature type, however that would
- // only tell us the types of the arguments and not the names. The second is
- // to iterate the CVSymbol records that follow the S_GPROC32 / S_LPROC32 until
- // we have the correct number of arguments as stated by the function
- // signature. The latter has more potential to go wrong in the face of
- // improper debug info simply because we're assuming more about the layout of
- // the records, but it is the only way to get argument names.
+ // The function signature only tells us the number of types of arguments, but
+ // not the names. So we need to iterate the symbol stream looking for the
+ // corresponding symbol records to properly construct the AST.
CVType sig_cvt;
- CVType arg_list_cvt;
ProcedureRecord sig_record;
- ArgListRecord arg_list_record;
sig_cvt = m_index->tpi().getType(proc.FunctionType);
if (sig_cvt.kind() != LF_PROCEDURE)
@@ -574,8 +703,7 @@
cantFail(
TypeDeserializer::deserializeAs<ProcedureRecord>(sig_cvt, sig_record));
- CompilerDeclContext context =
- GetDeclContextContainingUID(toOpaqueUid(func_id));
+ CompilerDeclContext context = GetDeclContextContainingUID(opaque_func_uid);
clang::DeclContext *decl_context =
static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext());
@@ -583,8 +711,9 @@
decl_context, proc.Name.str().c_str(),
func_type->GetForwardCompilerType(), storage, false);
- lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0);
- m_uid_to_decl[toOpaqueUid(func_id)] = function_decl;
+ lldbassert(m_uid_to_decl.count(opaque_func_uid) == 0);
+ m_uid_to_decl[opaque_func_uid] = function_decl;
+
CVSymbolArray scope = limitSymbolArrayToScope(
cci->m_debug_stream.getSymbolArray(), func_id.offset);
@@ -593,57 +722,34 @@
auto end = scope.end();
std::vector<clang::ParmVarDecl *> params;
while (begin != end && params_remaining > 0) {
- uint32_t record_offset = begin.offset();
- CVSymbol sym = *begin++;
-
- TypeIndex param_type;
- llvm::StringRef param_name;
+ CVSymbol sym = *begin;
switch (sym.kind()) {
- case S_REGREL32: {
- RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
- cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
- param_type = reg.Type;
- param_name = reg.Name;
+ case S_REGREL32:
+ case S_REGISTER:
+ case S_LOCAL:
break;
- }
- case S_REGISTER: {
- RegisterSym reg(SymbolRecordKind::RegisterSym);
- cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
- param_type = reg.Index;
- param_name = reg.Name;
- break;
- }
- case S_LOCAL: {
- LocalSym local(SymbolRecordKind::LocalSym);
- cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
- if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None)
- continue;
- param_type = local.Type;
- param_name = local.Name;
- break;
- }
case S_BLOCK32:
- // All parameters should come before the first block. If that isn't the
- // case, then perhaps this is bad debug info that doesn't contain
- // information about all parameters.
params_remaining = 0;
continue;
default:
+ ++begin;
continue;
}
+ PdbCompilandSymId param_uid(func_id.modi, begin.offset());
+ VariableInfo var_info = GetVariableInformation(
+ *m_index, param_uid, GetObjectFile()->GetModule(), false);
- PdbCompilandSymId param_uid(func_id.modi, record_offset);
- TypeSP type_sp = GetOrCreateType(param_type);
+ TypeSP type_sp = GetOrCreateType(var_info.type);
clang::ParmVarDecl *param = m_clang->CreateParameterDeclaration(
- function_decl, param_name.str().c_str(),
+ function_decl, var_info.name.str().c_str(),
type_sp->GetForwardCompilerType(), clang::SC_None);
lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0);
m_uid_to_decl[toOpaqueUid(param_uid)] = param;
params.push_back(param);
--params_remaining;
+ ++begin;
}
-
if (!params.empty())
m_clang->SetFunctionParameters(function_decl, params.data(), params.size());
@@ -1243,10 +1349,10 @@
}
FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbCompilandSymId func_id,
- const SymbolContext &sc) {
+ CompileUnit &comp_unit) {
auto emplace_result = m_functions.try_emplace(toOpaqueUid(func_id), nullptr);
if (emplace_result.second)
- emplace_result.first->second = CreateFunction(func_id, sc);
+ emplace_result.first->second = CreateFunction(func_id, comp_unit);
lldbassert(emplace_result.first->second);
return emplace_result.first->second;
@@ -1264,6 +1370,14 @@
return emplace_result.first->second;
}
+Block &SymbolFileNativePDB::GetOrCreateBlock(PdbCompilandSymId block_id) {
+ auto iter = m_blocks.find(toOpaqueUid(block_id));
+ if (iter != m_blocks.end())
+ return *iter->second;
+
+ return CreateBlock(block_id);
+}
+
lldb::CompUnitSP SymbolFileNativePDB::ParseCompileUnitAtIndex(uint32_t index) {
if (index >= GetNumCompileUnits())
return CompUnitSP();
@@ -1326,19 +1440,35 @@
resolved_flags |= eSymbolContextCompUnit;
}
- if (resolve_scope & eSymbolContextFunction) {
+ if (resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock) {
lldbassert(sc.comp_unit);
std::vector<SymbolAndUid> matches = m_index->FindSymbolsByVa(file_addr);
- for (const auto &match : matches) {
+ // Search the matches in reverse. This way if there are multiple matches
+ // (for example we are 3 levels deep in a nested scope) it will find the
+ // innermost one first.
+ for (const auto &match : llvm::reverse(matches)) {
if (match.uid.kind() != PdbSymUidKind::CompilandSym)
continue;
+
PdbCompilandSymId csid = match.uid.asCompilandSym();
CVSymbol cvs = m_index->ReadSymbolRecord(csid);
- if (CVSymToPDBSym(cvs.kind()) != PDB_SymType::Function)
+ PDB_SymType type = CVSymToPDBSym(cvs.kind());
+ if (type != PDB_SymType::Function && type != PDB_SymType::Block)
continue;
- sc.function = GetOrCreateFunction(csid, sc).get();
- }
+ if (type == PDB_SymType::Function) {
+ sc.function = GetOrCreateFunction(csid, *sc.comp_unit).get();
+ sc.block = sc.GetFunctionBlock();
+ }
+
+ if (type == PDB_SymType::Block) {
+ sc.block = &GetOrCreateBlock(csid);
+ sc.function = sc.block->CalculateSymbolContextFunction();
+ }
resolved_flags |= eSymbolContextFunction;
+ resolved_flags |= eSymbolContextBlock;
+ break;
+ }
}
if (resolve_scope & eSymbolContextLineEntry) {
@@ -1496,7 +1626,9 @@
size_t SymbolFileNativePDB::ParseFunctionBlocks(const SymbolContext &sc) {
lldbassert(sc.comp_unit && sc.function);
- return 0;
+ GetOrCreateBlock(PdbSymUid(sc.function->GetID()).asCompilandSym());
+ // FIXME: Parse child blocks
+ return 1;
}
void SymbolFileNativePDB::DumpClangAST(Stream &s) {
@@ -1558,9 +1690,8 @@
SymbolContext sc;
sc.comp_unit = GetOrCreateCompileUnit(cci).get();
- sc.module_sp = sc.comp_unit->GetModule();
PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
- sc.function = GetOrCreateFunction(func_id, sc).get();
+ sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
sc_list.Append(sc);
}
@@ -1621,6 +1752,188 @@
size_t SymbolFileNativePDB::ParseTypes(const SymbolContext &sc) { return 0; }
+size_t
+SymbolFileNativePDB::ParseVariablesForCompileUnit(CompileUnit &comp_unit,
+ VariableList &variables) {
+ PdbSymUid sym_uid(comp_unit.GetID());
+ lldbassert(sym_uid.kind() == PdbSymUidKind::Compiland);
+ return 0;
+}
+
+VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id,
+ bool is_param) {
+ ModuleSP module = GetObjectFile()->GetModule();
+ VariableInfo var_info =
+ GetVariableInformation(*m_index, var_id, module, true);
+
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(var_id.modi);
+ CompUnitSP comp_unit_sp = GetOrCreateCompileUnit(*cii);
+ TypeSP type_sp = GetOrCreateType(var_info.type);
+ std::string name = var_info.name.str();
+ Declaration decl;
+ SymbolFileTypeSP sftype =
+ std::make_shared<SymbolFileType>(*this, type_sp->GetID());
+
+ ValueType var_scope =
+ is_param ? eValueTypeVariableArgument : eValueTypeVariableLocal;
+ VariableSP var_sp = std::make_shared<Variable>(
+ toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope,
+ comp_unit_sp.get(), *var_info.ranges, &decl, *var_info.location, false,
+ false, false);
+
+ CompilerDeclContext cdc = GetDeclContextForUID(toOpaqueUid(scope_id));
+ clang::DeclContext *decl_ctx =
+ static_cast<clang::DeclContext *>(cdc.GetOpaqueDeclContext());
+
+ // Parameter info should have already been added to the AST.
+ if (!is_param) {
+ clang::QualType qt =
+ ClangUtil::GetCanonicalQualType(type_sp->GetForwardCompilerType());
+
+ clang::VarDecl *var_decl =
+ m_clang->CreateVariableDeclaration(decl_ctx, name.c_str(), qt);
+ lldbassert(m_uid_to_decl.count(toOpaqueUid(var_id)) == 0);
+ m_uid_to_decl[toOpaqueUid(var_id)] = var_decl;
+ }
+
+ m_local_variables[toOpaqueUid(var_id)] = var_sp;
+ return var_sp;
+}
+
+VariableSP SymbolFileNativePDB::GetOrCreateLocalVariable(
+ PdbCompilandSymId scope_id, PdbCompilandSymId var_id, bool is_param) {
+ auto iter = m_local_variables.find(toOpaqueUid(var_id));
+ if (iter != m_local_variables.end())
+ return iter->second;
+
+ return CreateLocalVariable(scope_id, var_id, is_param);
+}
+
+size_t SymbolFileNativePDB::ParseVariablesForBlock(PdbCompilandSymId block_id) {
+ Block &block = GetOrCreateBlock(block_id);
+
+ size_t count = 0;
+
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
+ CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
+ uint32_t params_remaining = 0;
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32: {
+ ProcSym proc(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym, proc));
+ CVType signature = m_index->tpi().getType(proc.FunctionType);
+ ProcedureRecord sig;
+ cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(signature, sig));
+ params_remaining = sig.getParameterCount();
+ break;
+ }
+ case S_BLOCK32:
+ break;
+ default:
+ lldbassert(false && "Symbol is not a block!");
+ return 0;
+ }
+
+ VariableListSP variables = block.GetBlockVariableList(false);
+ if (!variables) {
+ variables = std::make_shared<VariableList>();
+ block.SetVariableList(variables);
+ }
+
+ CVSymbolArray syms = limitSymbolArrayToScope(
+ cii->m_debug_stream.getSymbolArray(), block_id.offset);
+
+ // Skip the first record since it's a PROC32 or BLOCK32, and there's
+ // no point examining it since we know it's not a local variable.
+ syms.drop_front();
+ auto iter = syms.begin();
+ auto end = syms.end();
+
+ while (iter != end) {
+ uint32_t record_offset = iter.offset();
+ CVSymbol variable_cvs = *iter;
+ PdbCompilandSymId child_sym_id(block_id.modi, record_offset);
+ ++iter;
+
+ // If this is a block, recurse into its children and then skip it.
+ if (variable_cvs.kind() == S_BLOCK32) {
+ uint32_t block_end = getScopeEndOffset(variable_cvs);
+ count += ParseVariablesForBlock(child_sym_id);
+ iter = syms.at(block_end);
+ continue;
+ }
+
+ bool is_param = params_remaining > 0;
+ VariableSP variable;
+ switch (variable_cvs.kind()) {
+ case S_REGREL32:
+ case S_REGISTER:
+ case S_LOCAL:
+ variable = GetOrCreateLocalVariable(block_id, child_sym_id, is_param);
+ if (is_param)
+ --params_remaining;
+ variables->AddVariableIfUnique(variable);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Pass false for set_children, since we call this recursively so that the
+ // children will call this for themselves.
+ block.SetDidParseVariables(true, false);
+
+ return count;
+}
+
+size_t SymbolFileNativePDB::ParseVariablesForContext(const SymbolContext &sc) {
+ lldbassert(sc.function || sc.comp_unit);
+
+ VariableListSP variables;
+ PdbSymUid sym_uid;
+ if (sc.block) {
+ PdbSymUid block_id(sc.block->GetID());
+
+ size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
+ return count;
+ }
+
+ if (sc.function) {
+ PdbSymUid block_id(sc.function->GetID());
+
+ size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
+ return count;
+ }
+
+ if (sc.comp_unit) {
+ variables = sc.comp_unit->GetVariableList(false);
+ if (!variables) {
+ variables = std::make_shared<VariableList>();
+ sc.comp_unit->SetVariableList(variables);
+ }
+ return ParseVariablesForCompileUnit(*sc.comp_unit, *variables);
+ }
+
+ llvm_unreachable("Unreachable!");
+}
+
+CompilerDecl SymbolFileNativePDB::GetDeclForUID(lldb::user_id_t uid) {
+ auto iter = m_uid_to_decl.find(uid);
+ if (iter == m_uid_to_decl.end())
+ return CompilerDecl();
+
+ return {m_clang, iter->second};
+}
+
+CompilerDeclContext
+SymbolFileNativePDB::GetDeclContextForUID(lldb::user_id_t uid) {
+ CompilerDecl compiler_decl = GetDeclForUID(uid);
+ clang::Decl *decl = static_cast<clang::Decl *>(compiler_decl.GetOpaqueDecl());
+ return {m_clang, clang::Decl::castToDeclContext(decl)};
+}
+
CompilerDeclContext
SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) {
// FIXME: This should look up the uid, decide if it's a symbol or a type, and