in hphp/tools/debug-parser/debug-parser-dwarf.cpp [2416:2665]
void visit_die_for_symbols(const DwarfState& dwarf,
const Dwarf_Die die,
SymbolMap& symbols,
SpecMap& spec_names,
std::string parent_name,
uint32_t language,
uint32_t cu_index) const {
bool is_declaration = false;
bool is_external = false;
std::string name;
bool full_name = false;
bool is_inlined = false;
bool has_location = false;
bool in_specification = false;
auto specification = GlobalOff::fromRaw(0);
auto collect_attributes = [&] (Dwarf_Attribute attr) {
switch (dwarf.getAttributeType(attr)) {
case DW_AT_declaration:
if (!in_specification) {
is_declaration = dwarf.getAttributeValueFlag(attr);
}
break;
case DW_AT_external:
is_external = dwarf.getAttributeValueFlag(attr);
break;
case DW_AT_linkage_name:
is_external = true;
break;
case DW_AT_location:
has_location = true;
break;
case DW_AT_name:
if (!full_name) {
name = dwarf.getAttributeValueString(attr);
}
break;
case DW_AT_inline: {
auto const val = dwarf.getAttributeValueUData(attr);
is_inlined =
(val == DW_INL_inlined) ||
(val == DW_INL_declared_inlined);
break;
}
case DW_AT_language:
language = dwarf.getAttributeValueUData(attr);
break;
case DW_AT_specification: {
specification = dwarf.getAttributeValueRef(attr);
auto const it = spec_names.find(specification);
if (it != spec_names.end()) {
name = it->second;
auto const pos = name.rfind("::");
if (pos != std::string::npos) {
parent_name = name.substr(0, pos);
}
full_name = true;
}
break;
}
default:
return true;
}
return true;
};
dwarf.forEachAttribute(die, collect_attributes);
if (specification.raw()) {
dwarf.onDIEAtOffset(
specification,
[&] (Dwarf_Die d) {
in_specification = true;
dwarf.forEachAttribute(d, collect_attributes);
}
);
}
struct IndexAndFlags {
IndexAndFlags(uint32_t index, uint32_t kind, uint32_t is_static) {
assertx((index >> 24) == 0);
// Bits 0-23 is CU index
// Bits 24-27 are reserved and must be 0
// Bits 28-30 The kind of the symbol in the CU.
// Bit 31 is zero if the value is global and one if it is static.
m_data = (is_static << 31) | (kind << 28) | index;
}
explicit IndexAndFlags(uint32_t data) : m_data(data) {}
uint32_t m_data;
uint32_t get_kind() const { return (m_data >> 28) & 7; }
uint32_t get_is_static() const { return m_data >> 31; }
};
constexpr int TYPE = 1;
constexpr int VARIABLE = 2;
//constexpr int ENUM = 2;
constexpr int FUNCTION = 3;
// constexpr int OTHER = 4;
auto const index_and_flags = [&] {
uint32_t kind = 0;
auto is_static = false;
switch (dwarf.getTag(die)) {
case DW_TAG_typedef:
case DW_TAG_base_type:
case DW_TAG_subrange_type:
kind = TYPE;
is_static = 1;
break;
case DW_TAG_enumerator:
kind = VARIABLE;
is_static = language != DW_LANG_C_plus_plus;
break;
case DW_TAG_subprogram:
kind = FUNCTION;
is_static = !(is_external || language == DW_LANG_Ada83 ||
language == DW_LANG_Ada95);
break;
case DW_TAG_constant:
kind = VARIABLE;
is_static = !is_external;
break;
case DW_TAG_variable:
kind = VARIABLE;
is_static = !is_external;
break;
case DW_TAG_namespace:
kind = TYPE;
is_static = 0;
break;
case DW_TAG_class_type:
case DW_TAG_interface_type:
case DW_TAG_structure_type:
case DW_TAG_union_type:
case DW_TAG_enumeration_type:
kind = TYPE;
is_static = language != DW_LANG_C_plus_plus;
break;
default:
throw Exception{"Invalid tag"};
}
return IndexAndFlags{cu_index, kind, is_static}.m_data;
};
auto const hasSameFlags = [&](std::vector<uint32_t> v, uint32_t input) {
auto const flags = IndexAndFlags{input};
for (auto const e : v) {
auto const f = IndexAndFlags{e};
if (f.get_kind() == flags.get_kind()) {
if ((f.get_kind() == TYPE &&
f.get_is_static() == flags.get_is_static()) ||
(!f.get_is_static() && !flags.get_is_static())) {
return true;
}
}
}
return false;
};
auto const addSymbol = [&](std::string name) {
auto value = index_and_flags();
SymbolMap::accessor acc;
if (symbols.insert(acc, name) || !hasSameFlags(acc->second, value)) {
acc->second.push_back(value);
}
};
auto const addParent = [&] {
if (full_name) return;
if (name.empty()) return;
if (!parent_name.empty()) {
name = folly::sformat("{}::{}", parent_name, name);
}
if (is_declaration) {
spec_names.emplace(dwarf.getDIEOffset(die), name);
}
};
auto const visitChildren = [&](std::string name) {
dwarf.forEachChild(
die,
[&](Dwarf_Die child) {
visit_die_for_symbols(dwarf, child, symbols, spec_names, name,
language, cu_index);
return true;
}
);
};
auto const tag = dwarf.getTag(die);
switch (tag) {
case DW_TAG_base_type:
// don't canonicalize!
addSymbol(name);
break;
case DW_TAG_member:
// static members appear first here as a declaration, then
// later as a DW_TAG_variable whose specification points
// here. We need to note the name just in case.
if (is_declaration) addParent();
break;
case DW_TAG_subprogram:
if (is_inlined) break;
case DW_TAG_constant:
case DW_TAG_enumerator:
if (name.empty()) break;
addParent();
if (is_declaration) break;
addSymbol(name);
break;
case DW_TAG_variable:
if (name.empty() || (!is_external && !has_location)) break;
addParent();
if (is_declaration) break;
addSymbol(name);
break;
case DW_TAG_namespace:
if (name.empty()) name = "(anonymous namespace)";
addParent();
visitChildren(name);
break;
case DW_TAG_typedef:
case DW_TAG_subrange_type:
addParent();
if (is_declaration || name.empty()) break;
addSymbol(name);
break;
case DW_TAG_union_type:
case DW_TAG_class_type:
case DW_TAG_interface_type:
case DW_TAG_structure_type:
case DW_TAG_enumeration_type:
addParent();
if (!is_declaration && !name.empty()) {
addSymbol(name);
}
if (tag == DW_TAG_enumeration_type || !name.empty()) {
visitChildren(tag == DW_TAG_enumeration_type ? parent_name : name);
}
break;
case DW_TAG_compile_unit:
case DW_TAG_type_unit:
visitChildren(parent_name);
break;
default:
break;
}
}