diff --git a/src/symbols/dwarf/dwarf_resolver.cpp b/src/symbols/dwarf/dwarf_resolver.cpp index 3f95917..be3b73c 100644 --- a/src/symbols/dwarf/dwarf_resolver.cpp +++ b/src/symbols/dwarf/dwarf_resolver.cpp @@ -39,12 +39,6 @@ namespace libdwarf { constexpr bool dump_dwarf = false; constexpr bool trace_dwarf = false; - struct subprogram_entry { - die_object die; - Dwarf_Addr low; - Dwarf_Addr high; - }; - struct cu_entry { die_object die; Dwarf_Half dwversion; @@ -52,6 +46,77 @@ namespace libdwarf { Dwarf_Addr high; }; + // sorted range entries for dies + template + class die_cache { + public: + struct die_handle { + std::size_t die_index; + }; + private: + struct basic_range_entry { + die_handle die; + Dwarf_Addr low; + Dwarf_Addr high; + }; + static_assert( + sizeof(basic_range_entry) == 3 * sizeof(void*), + "Expected basic_range_entry to be smaller (this is memory critical)" + ); + struct annotated_range_entry { + die_handle die; + Dwarf_Addr low; + Dwarf_Addr high; + T data; + }; + using range_entry = typename std::conditional< + std::is_same::value, + basic_range_entry, + annotated_range_entry + >::type; + std::vector dies; + std::vector range_entries; + public: + die_handle add_die(die_object&& die) { + dies.push_back(std::move(die)); + return die_handle{dies.size() - 1}; + } + template + auto insert(die_handle die, Dwarf_Addr low, Dwarf_Addr high) + -> typename std::enable_if::value, Void>::type + { + range_entries.push_back({die, low, high}); + } + template + auto insert(die_handle die, Dwarf_Addr low, Dwarf_Addr high, const T& t) + -> typename std::enable_if::value, Void>::type + { + range_entries.push_back({die, low, high, t}); + } + void finalize() { + std::sort(range_entries.begin(), range_entries.end(), [] (const range_entry& a, const range_entry& b) { + return a.low < b.low; + }); + } + std::size_t ranges_count() const { + return range_entries.size(); + } + optional lookup(Dwarf_Addr pc) const { + auto vec_it = first_less_than_or_equal( + range_entries.begin(), + range_entries.end(), + pc, + [] (Dwarf_Addr pc, const range_entry& entry) { + return pc < entry.low; + } + ); + if(vec_it == range_entries.end()) { + return nullopt; + } + return dies.at(vec_it->die.die_index); + } + }; + struct line_entry { Dwarf_Addr low; // Dwarf_Addr high; @@ -113,7 +178,7 @@ namespace libdwarf { // Map from CU -> Line context lru_cache line_tables{get_dwarf_resolver_line_table_cache_size()}; // Map from CU -> Sorted subprograms vector - std::unordered_map> subprograms_cache; + std::unordered_map> subprograms_cache; // Vector of ranges and their corresponding CU offsets std::vector cu_cache; bool generated_cu_cache = false; @@ -545,26 +610,28 @@ namespace libdwarf { const die_object& cu_die, const die_object& die, Dwarf_Half dwversion, - std::vector& vec + die_cache& subprogram_cache ) { walk_die_list( die, - [this, &cu_die, dwversion, &vec] (const die_object& die) { + [this, &cu_die, dwversion, &subprogram_cache] (const die_object& die) { switch(die.get_tag()) { case DW_TAG_subprogram: { auto ranges_vec = die.get_rangelist_entries(cu_die, dwversion); // TODO: Feels super inefficient and some day should maybe use an interval tree. - for(auto range : ranges_vec) { - // TODO: Reduce cloning here - vec.push_back({ die.clone(), range.first, range.second }); + if(!ranges_vec.empty()) { + auto die_handle = subprogram_cache.add_die(die.clone()); + for(auto range : ranges_vec) { + subprogram_cache.insert(die_handle, range.first, range.second); + } } // Walk children to get things like lambdas // TODO: Somehow find a way to get better names here? For gcc it's just "operator()" // On clang it's better auto child = die.get_child(); if(child) { - preprocess_subprograms(cu_die, child, dwversion, vec); + preprocess_subprograms(cu_die, child, dwversion, subprogram_cache); } } break; @@ -577,7 +644,7 @@ namespace libdwarf { { auto child = die.get_child(); if(child) { - preprocess_subprograms(cu_die, child, dwversion, vec); + preprocess_subprograms(cu_die, child, dwversion, subprogram_cache); } } break; @@ -607,30 +674,21 @@ namespace libdwarf { auto it = subprograms_cache.find(off); if(it == subprograms_cache.end()) { // TODO: Refactor. Do the sort in the preprocess function and return the vec directly. - std::vector vec; - preprocess_subprograms(cu_die, cu_die, dwversion, vec); - std::sort(vec.begin(), vec.end(), [] (const subprogram_entry& a, const subprogram_entry& b) { - return a.low < b.low; - }); - subprograms_cache.emplace(off, std::move(vec)); + die_cache subprogram_cache; + preprocess_subprograms(cu_die, cu_die, dwversion, subprogram_cache); + subprogram_cache.finalize(); + subprograms_cache.emplace(off, std::move(subprogram_cache)); it = subprograms_cache.find(off); } - auto& vec = it->second; - auto vec_it = first_less_than_or_equal( - vec.begin(), - vec.end(), - pc, - [] (Dwarf_Addr pc, const subprogram_entry& entry) { - return pc < entry.low; - } - ); + const auto& subprogram_cache = it->second; + auto maybe_die = subprogram_cache.lookup(pc); // If the vector has been empty this can happen - if(vec_it != vec.end()) { - if(vec_it->die.pc_in_die(cu_die, dwversion, pc)) { - frame.symbol = retrieve_symbol_for_subprogram(cu_die, vec_it->die, pc, dwversion, inlines); + if(maybe_die.has_value()) { + if(maybe_die.unwrap().pc_in_die(cu_die, dwversion, pc)) { + frame.symbol = retrieve_symbol_for_subprogram(cu_die, maybe_die.unwrap(), pc, dwversion, inlines); } } else { - ASSERT(vec.size() == 0, "Vec should be empty?"); + ASSERT(subprogram_cache.ranges_count() == 0, "subprogram_cache.ranges_count() should be 0?"); } } }