Add a configurable cache size for the line tables cache, related to #193

This commit is contained in:
Jeremy Rifkin 2025-02-17 19:06:45 -06:00
parent 8c7b1dc6aa
commit 1d79dbcf42
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
6 changed files with 160 additions and 10 deletions

View File

@ -121,6 +121,7 @@ target_sources(
src/demangle/demangle_with_winapi.cpp src/demangle/demangle_with_winapi.cpp
src/snippets/snippet.cpp src/snippets/snippet.cpp
src/symbols/dwarf/debug_map_resolver.cpp src/symbols/dwarf/debug_map_resolver.cpp
src/symbols/dwarf/dwarf_options.cpp
src/symbols/dwarf/dwarf_resolver.cpp src/symbols/dwarf/dwarf_resolver.cpp
src/symbols/symbols_core.cpp src/symbols/symbols_core.cpp
src/symbols/symbols_with_addr2line.cpp src/symbols/symbols_with_addr2line.cpp

View File

@ -43,6 +43,11 @@ namespace cpptrace {
namespace experimental { namespace experimental {
CPPTRACE_EXPORT void set_cache_mode(cache_mode mode); CPPTRACE_EXPORT void set_cache_mode(cache_mode mode);
} }
// dwarf options
namespace experimental {
CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable<std::size_t> max_entries);
}
} }
#endif #endif

View File

@ -0,0 +1,22 @@
#include "symbols/dwarf/dwarf_options.hpp"
#include <cpptrace/utils.hpp>
#include <atomic>
namespace cpptrace {
namespace detail {
std::atomic<nullable<std::size_t>> dwarf_resolver_line_table_cache_size{nullable<std::size_t>::null()};
optional<std::size_t> get_dwarf_resolver_line_table_cache_size() {
auto max_entries = dwarf_resolver_line_table_cache_size.load();
return max_entries.has_value() ? optional<std::size_t>(max_entries.value()) : nullopt;
}
}
namespace experimental {
void set_dwarf_resolver_line_table_cache_size(nullable<std::size_t> max_entries) {
detail::dwarf_resolver_line_table_cache_size.store(max_entries);
}
}
}

View File

@ -0,0 +1,14 @@
#ifndef DWARF_OPTIONS_HPP
#define DWARF_OPTIONS_HPP
#include "utils/optional.hpp"
#include <cstddef>
namespace cpptrace {
namespace detail {
optional<std::size_t> get_dwarf_resolver_line_table_cache_size();
}
}
#endif

View File

@ -4,10 +4,12 @@
#include <cpptrace/basic.hpp> #include <cpptrace/basic.hpp>
#include "symbols/dwarf/dwarf.hpp" // has dwarf #includes #include "symbols/dwarf/dwarf.hpp" // has dwarf #includes
#include "symbols/dwarf/dwarf_options.hpp"
#include "symbols/symbols.hpp" #include "symbols/symbols.hpp"
#include "utils/common.hpp" #include "utils/common.hpp"
#include "utils/error.hpp" #include "utils/error.hpp"
#include "utils/utils.hpp" #include "utils/utils.hpp"
#include "utils/lru_cache.hpp"
#include "platform/path.hpp" #include "platform/path.hpp"
#include "platform/program_name.hpp" // For CPPTRACE_MAX_PATH #include "platform/program_name.hpp" // For CPPTRACE_MAX_PATH
@ -109,7 +111,7 @@ namespace libdwarf {
Dwarf_Arange* aranges = nullptr; Dwarf_Arange* aranges = nullptr;
Dwarf_Signed arange_count = 0; Dwarf_Signed arange_count = 0;
// Map from CU -> Line context // Map from CU -> Line context
std::unordered_map<Dwarf_Off, line_table_info> line_tables; lru_cache<Dwarf_Off, line_table_info> line_tables{get_dwarf_resolver_line_table_cache_size()};
// Map from CU -> Sorted subprograms vector // Map from CU -> Sorted subprograms vector
std::unordered_map<Dwarf_Off, std::vector<subprogram_entry>> subprograms_cache; std::unordered_map<Dwarf_Off, std::vector<subprogram_entry>> subprograms_cache;
// Vector of ranges and their corresponding CU offsets // Vector of ranges and their corresponding CU offsets
@ -409,7 +411,7 @@ namespace libdwarf {
if(file_i) { if(file_i) {
// for dwarf 2, 3, 4, and experimental line table version 0xfe06 1-indexing is used // for dwarf 2, 3, 4, and experimental line table version 0xfe06 1-indexing is used
// for dwarf 5 0-indexing is used // for dwarf 5 0-indexing is used
optional<std::reference_wrapper<line_table_info>> line_table_opt; optional<line_table_info&> line_table_opt;
if(skeleton) { if(skeleton) {
line_table_opt = skeleton.unwrap().resolver.get_line_table( line_table_opt = skeleton.unwrap().resolver.get_line_table(
skeleton.unwrap().cu_die skeleton.unwrap().cu_die
@ -418,7 +420,7 @@ namespace libdwarf {
line_table_opt = get_line_table(cu_die); line_table_opt = get_line_table(cu_die);
} }
if(line_table_opt) { 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(line_table.version != 5) {
if(file_i.unwrap() == 0) { if(file_i.unwrap() == 0) {
file_i.reset(); // 0 means no name to be found 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 // 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 CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
optional<std::reference_wrapper<line_table_info>> get_line_table(const die_object& cu_die) { optional<line_table_info&> get_line_table(const die_object& cu_die) {
auto off = cu_die.get_global_offset(); auto off = cu_die.get_global_offset();
auto it = line_tables.find(off); auto res = line_tables.maybe_get(off);
if(it != line_tables.end()) { if(res) {
return it->second; return res;
} else { } else {
Dwarf_Unsigned version; Dwarf_Unsigned version;
Dwarf_Small table_count; 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 line_tables.insert(off, line_table_info{version, line_context, std::move(line_entries)});
return it->second;
} }
} }
@ -725,7 +726,7 @@ namespace libdwarf {
if(!table_info_opt) { if(!table_info_opt) {
return; // failing silently for now 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) { if(get_cache_mode() == cache_mode::prioritize_speed) {
// Lookup in the table // Lookup in the table
auto& line_entries = table_info.line_entries; auto& line_entries = table_info.line_entries;

107
src/utils/lru_cache.hpp Normal file
View File

@ -0,0 +1,107 @@
#ifndef LRU_CACHE_HPP
#define LRU_CACHE_HPP
#include "utils/error.hpp"
#include "utils/optional.hpp"
#include <list>
#include <unordered_map>
namespace cpptrace {
namespace detail {
template<typename K, typename V>
class lru_cache {
struct kvp {
K key;
V value;
};
using list_type = std::list<kvp>;
using list_iterator = typename list_type::iterator;
mutable list_type lru;
std::unordered_map<K, list_iterator> map;
optional<std::size_t> max_size;
public:
lru_cache() = default;
lru_cache(optional<std::size_t> max_size) : max_size(max_size) {
VERIFY(!max_size || max_size.unwrap() > 0);
}
void set_max_size(optional<std::size_t> 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<V&> 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<const V&> 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<V&> 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