Cache elf and mach-o file objects

This commit is contained in:
Jeremy Rifkin 2025-02-19 22:46:53 -06:00
parent 69875cde19
commit daed105fef
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
10 changed files with 96 additions and 20 deletions

View File

@ -7,7 +7,9 @@
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <mutex>
#include <type_traits> #include <type_traits>
#include <unordered_map>
#include <elf.h> #include <elf.h>
@ -410,6 +412,25 @@ namespace detail {
} }
return symbol_table; return symbol_table;
} }
Result<maybe_owned<elf>, internal_error> open_elf_cached(const std::string& object_path) {
if(get_cache_mode() == cache_mode::prioritize_memory) {
return elf::open_elf(object_path)
.transform([](elf&& obj) { return maybe_owned<elf>{detail::make_unique<elf>(std::move(obj))}; });
} else {
std::mutex m;
std::unique_lock<std::mutex> lock{m};
// TODO: Re-evaluate storing the error
static std::unordered_map<std::string, Result<elf, internal_error>> cache;
auto it = cache.find(object_path);
if(it == cache.end()) {
auto res = cache.insert({ object_path, elf::open_elf(object_path) });
VERIFY(res.second);
it = res.first;
}
return it->second.transform([](elf& obj) { return maybe_owned<elf>(&obj); });
}
}
} }
} }

View File

@ -73,6 +73,8 @@ namespace detail {
public: public:
static NODISCARD Result<elf, internal_error> open_elf(const std::string& object_path); static NODISCARD Result<elf, internal_error> open_elf(const std::string& object_path);
elf(elf&&) = default;
public: public:
Result<std::uintptr_t, internal_error> get_module_image_base(); Result<std::uintptr_t, internal_error> get_module_image_base();
private: private:
@ -117,6 +119,8 @@ namespace detail {
template<std::size_t Bits> template<std::size_t Bits>
Result<optional<symtab_info>, internal_error> get_symtab_impl(bool dynamic); Result<optional<symtab_info>, internal_error> get_symtab_impl(bool dynamic);
}; };
NODISCARD Result<maybe_owned<elf>, internal_error> open_elf_cached(const std::string& object_path);
} }
} }

View File

@ -669,6 +669,27 @@ namespace detail {
return is_fat_magic(magic.unwrap_value()); return is_fat_magic(magic.unwrap_value());
} }
} }
Result<maybe_owned<mach_o>, internal_error> open_mach_o_cached(const std::string& object_path) {
if(get_cache_mode() == cache_mode::prioritize_memory) {
return mach_o::open_mach_o(object_path)
.transform([](mach_o&& obj) {
return maybe_owned<mach_o>{detail::make_unique<mach_o>(std::move(obj))};
});
} else {
std::mutex m;
std::unique_lock<std::mutex> lock{m};
// TODO: Re-evaluate storing the error
static std::unordered_map<std::string, Result<mach_o, internal_error>> cache;
auto it = cache.find(object_path);
if(it == cache.end()) {
auto res = cache.insert({ object_path, mach_o::open_mach_o(object_path) });
VERIFY(res.second);
it = res.first;
}
return it->second.transform([](mach_o& obj) { return maybe_owned<mach_o>(&obj); });
}
}
} }
} }

View File

@ -137,6 +137,8 @@ namespace detail {
}; };
Result<bool, internal_error> macho_is_fat(const std::string& object_path); Result<bool, internal_error> macho_is_fat(const std::string& object_path);
NODISCARD Result<maybe_owned<mach_o>, internal_error> open_mach_o_cached(const std::string& object_path);
} }
} }

View File

@ -30,12 +30,12 @@ namespace detail {
if(it == cache.end()) { if(it == cache.end()) {
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
// have two threads try to do the same computation // have two threads try to do the same computation
auto obj = elf::open_elf(object_path); auto elf_object = open_elf_cached(object_path);
// TODO: Cache the error // TODO: Cache the error
if(!obj) { if(!elf_object) {
return obj.unwrap_error(); return elf_object.unwrap_error();
} }
auto base = obj.unwrap_value().get_module_image_base(); auto base = elf_object.unwrap_value()->get_module_image_base();
if(base.is_error()) { if(base.is_error()) {
return std::move(base).unwrap_error(); return std::move(base).unwrap_error();
} }
@ -57,12 +57,12 @@ namespace detail {
if(it == cache.end()) { if(it == cache.end()) {
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
// have two threads try to do the same computation // have two threads try to do the same computation
auto obj = mach_o::open_mach_o(object_path); auto mach_o_object = open_mach_o_cached(object_path);
// TODO: Cache the error // TODO: Cache the error
if(!obj) { if(!mach_o_object) {
return obj.unwrap_error(); return mach_o_object.unwrap_error();
} }
auto base = obj.unwrap_value().get_text_vmaddr(); auto base = mach_o_object.unwrap_value()->get_text_vmaddr();
if(!base) { if(!base) {
return std::move(base).unwrap_error(); return std::move(base).unwrap_error();
} }

View File

@ -46,11 +46,11 @@ namespace libdwarf {
// the path doesn't exist // the path doesn't exist
std::unordered_map<std::string, uint64_t> symbols; std::unordered_map<std::string, uint64_t> symbols;
this->symbols = symbols; this->symbols = symbols;
auto obj = mach_o::open_mach_o(object_path); auto mach_o_object = open_mach_o_cached(object_path);
if(!obj) { if(!mach_o_object) {
return this->symbols.unwrap(); return this->symbols.unwrap();
} }
const auto& symbol_table = obj.unwrap_value().symbol_table(); const auto& symbol_table = mach_o_object.unwrap_value()->symbol_table();
if(!symbol_table) { if(!symbol_table) {
return this->symbols.unwrap(); return this->symbols.unwrap();
} }
@ -110,11 +110,11 @@ namespace libdwarf {
debug_map_resolver(const std::string& source_object_path) { debug_map_resolver(const std::string& source_object_path) {
// load mach-o // load mach-o
// TODO: Cache somehow? // TODO: Cache somehow?
auto obj = mach_o::open_mach_o(source_object_path); auto mach_o_object = open_mach_o_cached(source_object_path);
if(!obj) { if(!mach_o_object) {
return; return;
} }
mach_o& source_mach = obj.unwrap_value(); mach_o& source_mach = *mach_o_object.unwrap_value();
auto source_debug_map = source_mach.get_debug_map(); auto source_debug_map = source_mach.get_debug_map();
if(!source_debug_map) { if(!source_debug_map) {
return; return;

View File

@ -127,12 +127,12 @@ namespace libdwarf {
if(result.is_error()) { if(result.is_error()) {
result.drop_error(); result.drop_error();
} else if(result.unwrap_value()) { } else if(result.unwrap_value()) {
auto obj = mach_o::open_mach_o(object_path); auto mach_o_object = open_mach_o_cached(object_path);
if(!obj) { if(!mach_o_object) {
ok = false; ok = false;
return; return;
} }
universal_number = obj.unwrap_value().get_fat_index(); universal_number = mach_o_object.unwrap_value()->get_fat_index();
} }
#endif #endif

View File

@ -96,9 +96,9 @@ namespace libdwarf {
const auto& object_name = group.first; const auto& object_name = group.first;
// TODO PERF: Potentially a duplicate open and parse with module base stuff (and debug map resolver) // TODO PERF: Potentially a duplicate open and parse with module base stuff (and debug map resolver)
#if IS_LINUX #if IS_LINUX
auto object = elf::open_elf(object_name); auto object = open_elf_cached(object_name);
#elif IS_APPLE #elif IS_APPLE
auto object = mach_o::open_mach_o(object_name); auto object = open_mach_o_cached(object_name);
#endif #endif
auto resolver = get_resolver(object_name); auto resolver = get_resolver(object_name);
for(const auto& entry : group.second) { for(const auto& entry : group.second) {
@ -116,7 +116,9 @@ namespace libdwarf {
} }
#if IS_LINUX || IS_APPLE #if IS_LINUX || IS_APPLE
if(frame.frame.symbol.empty() && object.has_value()) { if(frame.frame.symbol.empty() && object.has_value()) {
frame.frame.symbol = object.unwrap_value().lookup_symbol(dlframe.object_address).value_or(""); frame.frame.symbol = object
.unwrap_value()
->lookup_symbol(dlframe.object_address).value_or("");
} }
#endif #endif
} }

View File

@ -127,6 +127,24 @@ namespace detail {
return has_value() ? static_cast<T>(std::move(value_)) : static_cast<T>(std::forward<U>(default_value)); return has_value() ? static_cast<T>(std::move(value_)) : static_cast<T>(std::forward<U>(default_value));
} }
template<typename F>
NODISCARD auto transform(F&& f) & -> Result<decltype(f(std::declval<T&>())), E> {
if(has_value()) {
return f(unwrap_value());
} else {
return unwrap_error();
}
}
template<typename F>
NODISCARD auto transform(F&& f) && -> Result<decltype(f(std::declval<T&&>())), E> {
if(has_value()) {
return f(std::move(unwrap_value()));
} else {
return unwrap_error();
}
}
void drop_error() const { void drop_error() const {
if(is_error()) { if(is_error()) {
std::fprintf(stderr, "%s\n", unwrap_error().what()); std::fprintf(stderr, "%s\n", unwrap_error().what());

View File

@ -279,6 +279,11 @@ namespace detail {
using file_wrapper = raii_wrapper<std::FILE*, void(*)(std::FILE*)>; using file_wrapper = raii_wrapper<std::FILE*, void(*)(std::FILE*)>;
template<class T, class... Args>
auto make_unique(Args&&... args) -> typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<typename T> template<typename T>
class maybe_owned { class maybe_owned {
std::unique_ptr<T> owned; std::unique_ptr<T> owned;
@ -289,6 +294,9 @@ namespace detail {
T* operator->() { T* operator->() {
return ptr; return ptr;
} }
T& operator*() {
return *ptr;
}
}; };
} }
} }