diff --git a/CMakeLists.txt b/CMakeLists.txt index a7d549b..4f8e963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,7 @@ target_sources( src/demangle/demangle_with_winapi.cpp src/snippets/snippet.cpp src/symbols/dwarf/debug_map_resolver.cpp + src/symbols/dwarf/dwarf_options.cpp src/symbols/dwarf/dwarf_resolver.cpp src/symbols/symbols_core.cpp src/symbols/symbols_with_addr2line.cpp diff --git a/include/cpptrace/utils.hpp b/include/cpptrace/utils.hpp index 5ecf736..0f8af6c 100644 --- a/include/cpptrace/utils.hpp +++ b/include/cpptrace/utils.hpp @@ -43,6 +43,11 @@ namespace cpptrace { namespace experimental { CPPTRACE_EXPORT void set_cache_mode(cache_mode mode); } + + // dwarf options + namespace experimental { + CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable max_entries); + } } #endif diff --git a/src/symbols/dwarf/dwarf_options.cpp b/src/symbols/dwarf/dwarf_options.cpp new file mode 100644 index 0000000..218571b --- /dev/null +++ b/src/symbols/dwarf/dwarf_options.cpp @@ -0,0 +1,22 @@ +#include "symbols/dwarf/dwarf_options.hpp" + +#include + +#include + +namespace cpptrace { + namespace detail { + std::atomic> dwarf_resolver_line_table_cache_size{nullable::null()}; + + optional get_dwarf_resolver_line_table_cache_size() { + auto max_entries = dwarf_resolver_line_table_cache_size.load(); + return max_entries.has_value() ? optional(max_entries.value()) : nullopt; + } + } + + namespace experimental { + void set_dwarf_resolver_line_table_cache_size(nullable max_entries) { + detail::dwarf_resolver_line_table_cache_size.store(max_entries); + } + } +} diff --git a/src/symbols/dwarf/dwarf_options.hpp b/src/symbols/dwarf/dwarf_options.hpp new file mode 100644 index 0000000..580aa02 --- /dev/null +++ b/src/symbols/dwarf/dwarf_options.hpp @@ -0,0 +1,14 @@ +#ifndef DWARF_OPTIONS_HPP +#define DWARF_OPTIONS_HPP + +#include "utils/optional.hpp" + +#include + +namespace cpptrace { +namespace detail { + optional get_dwarf_resolver_line_table_cache_size(); +} +} + +#endif diff --git a/src/symbols/dwarf/dwarf_resolver.cpp b/src/symbols/dwarf/dwarf_resolver.cpp index c989ef0..3f95917 100644 --- a/src/symbols/dwarf/dwarf_resolver.cpp +++ b/src/symbols/dwarf/dwarf_resolver.cpp @@ -4,10 +4,12 @@ #include #include "symbols/dwarf/dwarf.hpp" // has dwarf #includes +#include "symbols/dwarf/dwarf_options.hpp" #include "symbols/symbols.hpp" #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/utils.hpp" +#include "utils/lru_cache.hpp" #include "platform/path.hpp" #include "platform/program_name.hpp" // For CPPTRACE_MAX_PATH @@ -109,7 +111,7 @@ namespace libdwarf { Dwarf_Arange* aranges = nullptr; Dwarf_Signed arange_count = 0; // Map from CU -> Line context - std::unordered_map line_tables; + lru_cache line_tables{get_dwarf_resolver_line_table_cache_size()}; // Map from CU -> Sorted subprograms vector std::unordered_map> subprograms_cache; // Vector of ranges and their corresponding CU offsets @@ -409,7 +411,7 @@ namespace libdwarf { if(file_i) { // for dwarf 2, 3, 4, and experimental line table version 0xfe06 1-indexing is used // for dwarf 5 0-indexing is used - optional> line_table_opt; + optional line_table_opt; if(skeleton) { line_table_opt = skeleton.unwrap().resolver.get_line_table( skeleton.unwrap().cu_die @@ -418,7 +420,7 @@ namespace libdwarf { line_table_opt = get_line_table(cu_die); } if(line_table_opt) { - auto& line_table = line_table_opt.unwrap().get(); + auto& line_table = line_table_opt.unwrap(); if(line_table.version != 5) { if(file_i.unwrap() == 0) { file_i.reset(); // 0 means no name to be found @@ -635,11 +637,11 @@ namespace libdwarf { // returns a reference to a CU's line table, may be invalidated if the line_tables map is modified CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING - optional> get_line_table(const die_object& cu_die) { + optional get_line_table(const die_object& cu_die) { auto off = cu_die.get_global_offset(); - auto it = line_tables.find(off); - if(it != line_tables.end()) { - return it->second; + auto res = line_tables.maybe_get(off); + if(res) { + return res; } else { Dwarf_Unsigned version; Dwarf_Small table_count; @@ -706,8 +708,7 @@ namespace libdwarf { }); } - it = line_tables.insert({off, line_table_info{version, line_context, std::move(line_entries)}}).first; - return it->second; + return line_tables.insert(off, line_table_info{version, line_context, std::move(line_entries)}); } } @@ -725,7 +726,7 @@ namespace libdwarf { if(!table_info_opt) { return; // failing silently for now } - auto& table_info = table_info_opt.unwrap().get(); + auto& table_info = table_info_opt.unwrap(); if(get_cache_mode() == cache_mode::prioritize_speed) { // Lookup in the table auto& line_entries = table_info.line_entries; diff --git a/src/utils/lru_cache.hpp b/src/utils/lru_cache.hpp new file mode 100644 index 0000000..ea11775 --- /dev/null +++ b/src/utils/lru_cache.hpp @@ -0,0 +1,107 @@ +#ifndef LRU_CACHE_HPP +#define LRU_CACHE_HPP + +#include "utils/error.hpp" +#include "utils/optional.hpp" + +#include +#include + +namespace cpptrace { +namespace detail { + template + class lru_cache { + struct kvp { + K key; + V value; + }; + using list_type = std::list; + using list_iterator = typename list_type::iterator; + mutable list_type lru; + std::unordered_map map; + optional max_size; + public: + lru_cache() = default; + lru_cache(optional max_size) : max_size(max_size) { + VERIFY(!max_size || max_size.unwrap() > 0); + } + + void set_max_size(optional max_size) { + VERIFY(!max_size || max_size.unwrap() > 0); + this->max_size = max_size; + maybe_trim(); + } + + void maybe_touch(const K& key) { + auto it = map.find(key); + if(it == map.end()) { + return; + } + auto list_it = it->second; + touch(list_it); + } + + optional maybe_get(const K& key) { + auto it = map.find(key); + if(it == map.end()) { + return nullopt; + } else { + touch(it->second); + return it->second->value; + } + } + + optional maybe_get(const K& key) const { + auto it = map.find(key); + if(it == map.end()) { + return nullopt; + } else { + touch(it->second); + return it->second->value; + } + } + + void set(const K& key, V value) { + auto it = map.find(key); + if(it == map.end()) { + insert(key, std::move(value)); + } else { + touch(it->second); + it->second->value = std::move(value); + } + } + + optional insert(const K& key, V value) { + auto pair = map.insert({key, lru.end()}); + if(!pair.second) { + // didn't insert + return nullopt; + } + auto map_it = pair.first; + lru.push_front({key, std::move(value)}); + map_it->second = lru.begin(); + maybe_trim(); + return lru.front().value; + } + + std::size_t size() const { + return lru.size(); + } + + private: + void touch(list_iterator list_it) const { + lru.splice(lru.begin(), lru, list_it); + } + + void maybe_trim() { + while(max_size && lru.size() > max_size.unwrap()) { + const auto& to_remove = lru.back(); + map.erase(to_remove.key); + lru.pop_back(); + } + } + }; +} +} + +#endif