void visit_die_for_symbols()

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;
    }
  }