[lldb] Reduce intentation in SymbolFileDWARF::ParseVariableDIE

using early exits. NFC.
This commit is contained in:
Pavel Labath 2020-08-27 15:30:12 +02:00
parent 9300ca5411
commit 1f9595ede4
1 changed files with 315 additions and 328 deletions

View File

@ -3091,363 +3091,350 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
if (die.GetDWARF() != this) if (die.GetDWARF() != this)
return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc); return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc);
VariableSP var_sp;
if (!die) if (!die)
return var_sp; return nullptr;
var_sp = GetDIEToVariable()[die.GetDIE()]; if (VariableSP var_sp = GetDIEToVariable()[die.GetDIE()])
if (var_sp)
return var_sp; // Already been parsed! return var_sp; // Already been parsed!
const dw_tag_t tag = die.Tag(); const dw_tag_t tag = die.Tag();
ModuleSP module = GetObjectFile()->GetModule(); ModuleSP module = GetObjectFile()->GetModule();
if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || if (tag != DW_TAG_variable && tag != DW_TAG_constant &&
(tag == DW_TAG_formal_parameter && sc.function)) { (tag != DW_TAG_formal_parameter || !sc.function))
DWARFAttributes attributes; return nullptr;
const size_t num_attributes = die.GetAttributes(attributes);
DWARFDIE spec_die;
if (num_attributes > 0) {
const char *name = nullptr;
const char *mangled = nullptr;
Declaration decl;
DWARFFormValue type_die_form;
DWARFExpression location;
bool is_external = false;
bool is_artificial = false;
DWARFFormValue const_value_form, location_form;
Variable::RangeList scope_ranges;
// AccessType accessibility = eAccessNone;
for (size_t i = 0; i < num_attributes; ++i) { DWARFAttributes attributes;
dw_attr_t attr = attributes.AttributeAtIndex(i); const size_t num_attributes = die.GetAttributes(attributes);
DWARFFormValue form_value; DWARFDIE spec_die;
VariableSP var_sp;
const char *name = nullptr;
const char *mangled = nullptr;
Declaration decl;
DWARFFormValue type_die_form;
DWARFExpression location;
bool is_external = false;
bool is_artificial = false;
DWARFFormValue const_value_form, location_form;
Variable::RangeList scope_ranges;
if (attributes.ExtractFormValueAtIndex(i, form_value)) { for (size_t i = 0; i < num_attributes; ++i) {
switch (attr) { dw_attr_t attr = attributes.AttributeAtIndex(i);
case DW_AT_decl_file: DWARFFormValue form_value;
decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(
form_value.Unsigned())); if (!attributes.ExtractFormValueAtIndex(i, form_value))
break; continue;
case DW_AT_decl_line: switch (attr) {
decl.SetLine(form_value.Unsigned()); case DW_AT_decl_file:
break; decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(
case DW_AT_decl_column: form_value.Unsigned()));
decl.SetColumn(form_value.Unsigned()); break;
break; case DW_AT_decl_line:
case DW_AT_name: decl.SetLine(form_value.Unsigned());
name = form_value.AsCString(); break;
break; case DW_AT_decl_column:
case DW_AT_linkage_name: decl.SetColumn(form_value.Unsigned());
case DW_AT_MIPS_linkage_name: break;
mangled = form_value.AsCString(); case DW_AT_name:
break; name = form_value.AsCString();
case DW_AT_type: break;
type_die_form = form_value; case DW_AT_linkage_name:
break; case DW_AT_MIPS_linkage_name:
case DW_AT_external: mangled = form_value.AsCString();
is_external = form_value.Boolean(); break;
break; case DW_AT_type:
case DW_AT_const_value: type_die_form = form_value;
const_value_form = form_value; break;
break; case DW_AT_external:
case DW_AT_location: is_external = form_value.Boolean();
location_form = form_value; break;
break; case DW_AT_const_value:
case DW_AT_specification: const_value_form = form_value;
spec_die = form_value.Reference(); break;
break; case DW_AT_location:
case DW_AT_start_scope: location_form = form_value;
// TODO: Implement this. break;
break; case DW_AT_specification:
case DW_AT_artificial: spec_die = form_value.Reference();
is_artificial = form_value.Boolean(); break;
break; case DW_AT_start_scope:
case DW_AT_accessibility: // TODO: Implement this.
break; // accessibility = break;
// DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_artificial:
case DW_AT_declaration: is_artificial = form_value.Boolean();
case DW_AT_description: break;
case DW_AT_endianity: case DW_AT_declaration:
case DW_AT_segment: case DW_AT_description:
case DW_AT_visibility: case DW_AT_endianity:
default: case DW_AT_segment:
case DW_AT_abstract_origin: case DW_AT_visibility:
case DW_AT_sibling: default:
break; case DW_AT_abstract_origin:
} case DW_AT_sibling:
} break;
}
}
// Prefer DW_AT_location over DW_AT_const_value. Both can be emitted e.g.
// for static constexpr member variables -- DW_AT_const_value will be
// present in the class declaration and DW_AT_location in the DIE defining
// the member.
bool location_is_const_value_data = false;
bool has_explicit_location = false;
bool use_type_size_for_value = false;
if (location_form.IsValid()) {
has_explicit_location = true;
if (DWARFFormValue::IsBlockForm(location_form.Form())) {
const DWARFDataExtractor &data = die.GetData();
uint32_t block_offset = location_form.BlockData() - data.GetDataStart();
uint32_t block_length = location_form.Unsigned();
location = DWARFExpression(
module, DataExtractor(data, block_offset, block_length), die.GetCU());
} else {
DataExtractor data = die.GetCU()->GetLocationData();
dw_offset_t offset = location_form.Unsigned();
if (location_form.Form() == DW_FORM_loclistx)
offset = die.GetCU()->GetLoclistOffset(offset).getValueOr(-1);
if (data.ValidOffset(offset)) {
data = DataExtractor(data, offset, data.GetByteSize() - offset);
location = DWARFExpression(module, data, die.GetCU());
assert(func_low_pc != LLDB_INVALID_ADDRESS);
location.SetLocationListAddresses(
location_form.GetUnit()->GetBaseAddress(), func_low_pc);
} }
}
} else if (const_value_form.IsValid()) {
location_is_const_value_data = true;
// The constant value will be either a block, a data value or a
// string.
const DWARFDataExtractor &debug_info_data = die.GetData();
if (DWARFFormValue::IsBlockForm(const_value_form.Form())) {
// Retrieve the value as a block expression.
uint32_t block_offset =
const_value_form.BlockData() - debug_info_data.GetDataStart();
uint32_t block_length = const_value_form.Unsigned();
location = DWARFExpression(
module, DataExtractor(debug_info_data, block_offset, block_length),
die.GetCU());
} else if (DWARFFormValue::IsDataForm(const_value_form.Form())) {
// Constant value size does not have to match the size of the
// variable. We will fetch the size of the type after we create
// it.
use_type_size_for_value = true;
} else if (const char *str = const_value_form.AsCString()) {
uint32_t string_length = strlen(str) + 1;
location = DWARFExpression(
module,
DataExtractor(str, string_length, die.GetCU()->GetByteOrder(),
die.GetCU()->GetAddressByteSize()),
die.GetCU());
}
}
// Prefer DW_AT_location over DW_AT_const_value. Both can be emitted e.g. const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die);
// for static constexpr member variables -- DW_AT_const_value will be const dw_tag_t parent_tag = die.GetParent().Tag();
// present in the class declaration and DW_AT_location in the DIE defining bool is_static_member = (parent_tag == DW_TAG_compile_unit ||
// the member. parent_tag == DW_TAG_partial_unit) &&
bool location_is_const_value_data = false; (parent_context_die.Tag() == DW_TAG_class_type ||
bool has_explicit_location = false; parent_context_die.Tag() == DW_TAG_structure_type);
bool use_type_size_for_value = false;
if (location_form.IsValid()) {
has_explicit_location = true;
if (DWARFFormValue::IsBlockForm(location_form.Form())) {
const DWARFDataExtractor &data = die.GetData();
uint32_t block_offset = ValueType scope = eValueTypeInvalid;
location_form.BlockData() - data.GetDataStart();
uint32_t block_length = location_form.Unsigned(); const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die);
location = DWARFExpression( SymbolContextScope *symbol_context_scope = nullptr;
module, DataExtractor(data, block_offset, block_length),
die.GetCU()); bool has_explicit_mangled = mangled != nullptr;
} else { if (!mangled) {
DataExtractor data = die.GetCU()->GetLocationData(); // LLDB relies on the mangled name (DW_TAG_linkage_name or
dw_offset_t offset = location_form.Unsigned(); // DW_AT_MIPS_linkage_name) to generate fully qualified names
if (location_form.Form() == DW_FORM_loclistx) // of global variables with commands like "frame var j". For
offset = die.GetCU()->GetLoclistOffset(offset).getValueOr(-1); // example, if j were an int variable holding a value 4 and
if (data.ValidOffset(offset)) { // declared in a namespace B which in turn is contained in a
data = DataExtractor(data, offset, data.GetByteSize() - offset); // namespace A, the command "frame var j" returns
location = DWARFExpression(module, data, die.GetCU()); // "(int) A::B::j = 4".
assert(func_low_pc != LLDB_INVALID_ADDRESS); // If the compiler does not emit a linkage name, we should be
location.SetLocationListAddresses( // able to generate a fully qualified name from the
location_form.GetUnit()->GetBaseAddress(), func_low_pc); // declaration context.
} if ((parent_tag == DW_TAG_compile_unit ||
} parent_tag == DW_TAG_partial_unit) &&
} else if (const_value_form.IsValid()) { Language::LanguageIsCPlusPlus(GetLanguage(*die.GetCU())))
location_is_const_value_data = true; mangled =
// The constant value will be either a block, a data value or a GetDWARFDeclContext(die).GetQualifiedNameAsConstString().GetCString();
// string. }
const DWARFDataExtractor &debug_info_data = die.GetData();
if (DWARFFormValue::IsBlockForm(const_value_form.Form())) { if (tag == DW_TAG_formal_parameter)
// Retrieve the value as a block expression. scope = eValueTypeVariableArgument;
uint32_t block_offset = else {
const_value_form.BlockData() - debug_info_data.GetDataStart(); // DWARF doesn't specify if a DW_TAG_variable is a local, global
uint32_t block_length = const_value_form.Unsigned(); // or static variable, so we have to do a little digging:
location = DWARFExpression( // 1) DW_AT_linkage_name implies static lifetime (but may be missing)
module, // 2) An empty DW_AT_location is an (optimized-out) static lifetime var.
DataExtractor(debug_info_data, block_offset, block_length), // 3) DW_AT_location containing a DW_OP_addr implies static lifetime.
die.GetCU()); // Clang likes to combine small global variables into the same symbol
} else if (DWARFFormValue::IsDataForm(const_value_form.Form())) { // with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus
// Constant value size does not have to match the size of the // so we need to look through the whole expression.
// variable. We will fetch the size of the type after we create bool is_static_lifetime =
// it. has_explicit_mangled || (has_explicit_location && !location.IsValid());
use_type_size_for_value = true; // Check if the location has a DW_OP_addr with any address value...
} else if (const char *str = const_value_form.AsCString()) { lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS;
uint32_t string_length = strlen(str) + 1; if (!location_is_const_value_data) {
location = DWARFExpression( bool op_error = false;
module, location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error);
DataExtractor(str, string_length, die.GetCU()->GetByteOrder(), if (op_error) {
die.GetCU()->GetAddressByteSize()), StreamString strm;
die.GetCU()); location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0,
} nullptr);
GetObjectFile()->GetModule()->ReportError(
"0x%8.8x: %s has an invalid location: %s", die.GetOffset(),
die.GetTagAsCString(), strm.GetData());
} }
if (location_DW_OP_addr != LLDB_INVALID_ADDRESS)
is_static_lifetime = true;
}
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
if (debug_map_symfile)
// Set the module of the expression to the linked module
// instead of the oject file so the relocated address can be
// found there.
location.SetModule(debug_map_symfile->GetObjectFile()->GetModule());
const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die); if (is_static_lifetime) {
const dw_tag_t parent_tag = die.GetParent().Tag(); if (is_external)
bool is_static_member = scope = eValueTypeVariableGlobal;
(parent_tag == DW_TAG_compile_unit || else
parent_tag == DW_TAG_partial_unit) && scope = eValueTypeVariableStatic;
(parent_context_die.Tag() == DW_TAG_class_type ||
parent_context_die.Tag() == DW_TAG_structure_type);
ValueType scope = eValueTypeInvalid; if (debug_map_symfile) {
// When leaving the DWARF in the .o files on darwin, when we have a
const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die); // global variable that wasn't initialized, the .o file might not
SymbolContextScope *symbol_context_scope = nullptr; // have allocated a virtual address for the global variable. In
// this case it will have created a symbol for the global variable
bool has_explicit_mangled = mangled != nullptr; // that is undefined/data and external and the value will be the
if (!mangled) { // byte size of the variable. When we do the address map in
// LLDB relies on the mangled name (DW_TAG_linkage_name or // SymbolFileDWARFDebugMap we rely on having an address, we need to
// DW_AT_MIPS_linkage_name) to generate fully qualified names // do some magic here so we can get the correct address for our
// of global variables with commands like "frame var j". For // global variable. The address for all of these entries will be
// example, if j were an int variable holding a value 4 and // zero, and there will be an undefined symbol in this object file,
// declared in a namespace B which in turn is contained in a // and the executable will have a matching symbol with a good
// namespace A, the command "frame var j" returns // address. So here we dig up the correct address and replace it in
// "(int) A::B::j = 4". // the location for the variable, and set the variable's symbol
// If the compiler does not emit a linkage name, we should be // context scope to be that of the main executable so the file
// able to generate a fully qualified name from the // address will resolve correctly.
// declaration context. bool linked_oso_file_addr = false;
if ((parent_tag == DW_TAG_compile_unit || if (is_external && location_DW_OP_addr == 0) {
parent_tag == DW_TAG_partial_unit) && // we have a possible uninitialized extern global
Language::LanguageIsCPlusPlus(GetLanguage(*die.GetCU()))) ConstString const_name(mangled ? mangled : name);
mangled = GetDWARFDeclContext(die) ObjectFile *debug_map_objfile = debug_map_symfile->GetObjectFile();
.GetQualifiedNameAsConstString() if (debug_map_objfile) {
.GetCString(); Symtab *debug_map_symtab = debug_map_objfile->GetSymtab();
} if (debug_map_symtab) {
Symbol *exe_symbol =
if (tag == DW_TAG_formal_parameter) debug_map_symtab->FindFirstSymbolWithNameAndType(
scope = eValueTypeVariableArgument; const_name, eSymbolTypeData, Symtab::eDebugYes,
else { Symtab::eVisibilityExtern);
// DWARF doesn't specify if a DW_TAG_variable is a local, global if (exe_symbol) {
// or static variable, so we have to do a little digging: if (exe_symbol->ValueIsAddress()) {
// 1) DW_AT_linkage_name implies static lifetime (but may be missing) const addr_t exe_file_addr =
// 2) An empty DW_AT_location is an (optimized-out) static lifetime var. exe_symbol->GetAddressRef().GetFileAddress();
// 3) DW_AT_location containing a DW_OP_addr implies static lifetime. if (exe_file_addr != LLDB_INVALID_ADDRESS) {
// Clang likes to combine small global variables into the same symbol if (location.Update_DW_OP_addr(exe_file_addr)) {
// with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus linked_oso_file_addr = true;
// so we need to look through the whole expression. symbol_context_scope = exe_symbol;
bool is_static_lifetime =
has_explicit_mangled ||
(has_explicit_location && !location.IsValid());
// Check if the location has a DW_OP_addr with any address value...
lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS;
if (!location_is_const_value_data) {
bool op_error = false;
location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error);
if (op_error) {
StreamString strm;
location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0,
nullptr);
GetObjectFile()->GetModule()->ReportError(
"0x%8.8x: %s has an invalid location: %s", die.GetOffset(),
die.GetTagAsCString(), strm.GetData());
}
if (location_DW_OP_addr != LLDB_INVALID_ADDRESS)
is_static_lifetime = true;
}
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
if (debug_map_symfile)
// Set the module of the expression to the linked module
// instead of the oject file so the relocated address can be
// found there.
location.SetModule(debug_map_symfile->GetObjectFile()->GetModule());
if (is_static_lifetime) {
if (is_external)
scope = eValueTypeVariableGlobal;
else
scope = eValueTypeVariableStatic;
if (debug_map_symfile) {
// When leaving the DWARF in the .o files on darwin, when we have a
// global variable that wasn't initialized, the .o file might not
// have allocated a virtual address for the global variable. In
// this case it will have created a symbol for the global variable
// that is undefined/data and external and the value will be the
// byte size of the variable. When we do the address map in
// SymbolFileDWARFDebugMap we rely on having an address, we need to
// do some magic here so we can get the correct address for our
// global variable. The address for all of these entries will be
// zero, and there will be an undefined symbol in this object file,
// and the executable will have a matching symbol with a good
// address. So here we dig up the correct address and replace it in
// the location for the variable, and set the variable's symbol
// context scope to be that of the main executable so the file
// address will resolve correctly.
bool linked_oso_file_addr = false;
if (is_external && location_DW_OP_addr == 0) {
// we have a possible uninitialized extern global
ConstString const_name(mangled ? mangled : name);
ObjectFile *debug_map_objfile =
debug_map_symfile->GetObjectFile();
if (debug_map_objfile) {
Symtab *debug_map_symtab = debug_map_objfile->GetSymtab();
if (debug_map_symtab) {
Symbol *exe_symbol =
debug_map_symtab->FindFirstSymbolWithNameAndType(
const_name, eSymbolTypeData, Symtab::eDebugYes,
Symtab::eVisibilityExtern);
if (exe_symbol) {
if (exe_symbol->ValueIsAddress()) {
const addr_t exe_file_addr =
exe_symbol->GetAddressRef().GetFileAddress();
if (exe_file_addr != LLDB_INVALID_ADDRESS) {
if (location.Update_DW_OP_addr(exe_file_addr)) {
linked_oso_file_addr = true;
symbol_context_scope = exe_symbol;
}
}
} }
} }
} }
} }
} }
if (!linked_oso_file_addr) {
// The DW_OP_addr is not zero, but it contains a .o file address
// which needs to be linked up correctly.
const lldb::addr_t exe_file_addr =
debug_map_symfile->LinkOSOFileAddress(this,
location_DW_OP_addr);
if (exe_file_addr != LLDB_INVALID_ADDRESS) {
// Update the file address for this variable
location.Update_DW_OP_addr(exe_file_addr);
} else {
// Variable didn't make it into the final executable
return var_sp;
}
}
} }
} else { }
if (location_is_const_value_data &&
die.GetDIE()->IsGlobalOrStaticScopeVariable()) if (!linked_oso_file_addr) {
scope = eValueTypeVariableStatic; // The DW_OP_addr is not zero, but it contains a .o file address
else { // which needs to be linked up correctly.
scope = eValueTypeVariableLocal; const lldb::addr_t exe_file_addr =
if (debug_map_symfile) { debug_map_symfile->LinkOSOFileAddress(this, location_DW_OP_addr);
// We need to check for TLS addresses that we need to fixup if (exe_file_addr != LLDB_INVALID_ADDRESS) {
if (location.ContainsThreadLocalStorage()) { // Update the file address for this variable
location.LinkThreadLocalStorage( location.Update_DW_OP_addr(exe_file_addr);
debug_map_symfile->GetObjectFile()->GetModule(), } else {
[this, debug_map_symfile]( // Variable didn't make it into the final executable
lldb::addr_t unlinked_file_addr) -> lldb::addr_t { return var_sp;
return debug_map_symfile->LinkOSOFileAddress(
this, unlinked_file_addr);
});
scope = eValueTypeVariableThreadLocal;
}
}
} }
} }
} }
} else {
if (symbol_context_scope == nullptr) { if (location_is_const_value_data &&
switch (parent_tag) { die.GetDIE()->IsGlobalOrStaticScopeVariable())
case DW_TAG_subprogram: scope = eValueTypeVariableStatic;
case DW_TAG_inlined_subroutine: else {
case DW_TAG_lexical_block: scope = eValueTypeVariableLocal;
if (sc.function) { if (debug_map_symfile) {
symbol_context_scope = sc.function->GetBlock(true).FindBlockByID( // We need to check for TLS addresses that we need to fixup
sc_parent_die.GetID()); if (location.ContainsThreadLocalStorage()) {
if (symbol_context_scope == nullptr) location.LinkThreadLocalStorage(
symbol_context_scope = sc.function; debug_map_symfile->GetObjectFile()->GetModule(),
[this, debug_map_symfile](
lldb::addr_t unlinked_file_addr) -> lldb::addr_t {
return debug_map_symfile->LinkOSOFileAddress(
this, unlinked_file_addr);
});
scope = eValueTypeVariableThreadLocal;
} }
break;
default:
symbol_context_scope = sc.comp_unit;
break;
} }
} }
if (symbol_context_scope) {
auto type_sp = std::make_shared<SymbolFileType>(
*this, GetUID(type_die_form.Reference()));
if (use_type_size_for_value && type_sp->GetType())
location.UpdateValue(
const_value_form.Unsigned(),
type_sp->GetType()->GetByteSize(nullptr).getValueOr(0),
die.GetCU()->GetAddressByteSize());
var_sp = std::make_shared<Variable>(
die.GetID(), name, mangled, type_sp, scope, symbol_context_scope,
scope_ranges, &decl, location, is_external, is_artificial,
is_static_member);
var_sp->SetLocationIsConstantValueData(location_is_const_value_data);
} else {
// Not ready to parse this variable yet. It might be a global or static
// variable that is in a function scope and the function in the symbol
// context wasn't filled in yet
return var_sp;
}
} }
// Cache var_sp even if NULL (the variable was just a specification or was
// missing vital information to be able to be displayed in the debugger
// (missing location due to optimization, etc)) so we don't re-parse this
// DIE over and over later...
GetDIEToVariable()[die.GetDIE()] = var_sp;
if (spec_die)
GetDIEToVariable()[spec_die.GetDIE()] = var_sp;
} }
if (symbol_context_scope == nullptr) {
switch (parent_tag) {
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine:
case DW_TAG_lexical_block:
if (sc.function) {
symbol_context_scope =
sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID());
if (symbol_context_scope == nullptr)
symbol_context_scope = sc.function;
}
break;
default:
symbol_context_scope = sc.comp_unit;
break;
}
}
if (symbol_context_scope) {
auto type_sp = std::make_shared<SymbolFileType>(
*this, GetUID(type_die_form.Reference()));
if (use_type_size_for_value && type_sp->GetType())
location.UpdateValue(
const_value_form.Unsigned(),
type_sp->GetType()->GetByteSize(nullptr).getValueOr(0),
die.GetCU()->GetAddressByteSize());
var_sp = std::make_shared<Variable>(
die.GetID(), name, mangled, type_sp, scope, symbol_context_scope,
scope_ranges, &decl, location, is_external, is_artificial,
is_static_member);
var_sp->SetLocationIsConstantValueData(location_is_const_value_data);
} else {
// Not ready to parse this variable yet. It might be a global or static
// variable that is in a function scope and the function in the symbol
// context wasn't filled in yet
return var_sp;
}
// Cache var_sp even if NULL (the variable was just a specification or was
// missing vital information to be able to be displayed in the debugger
// (missing location due to optimization, etc)) so we don't re-parse this
// DIE over and over later...
GetDIEToVariable()[die.GetDIE()] = var_sp;
if (spec_die)
GetDIEToVariable()[spec_die.GetDIE()] = var_sp;
return var_sp; return var_sp;
} }