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 <cstdio>
#include <cstring>
#include <mutex>
#include <type_traits>
#include <unordered_map>
#include <elf.h>
@ -410,6 +412,25 @@ namespace detail {
}
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:
static NODISCARD Result<elf, internal_error> open_elf(const std::string& object_path);
elf(elf&&) = default;
public:
Result<std::uintptr_t, internal_error> get_module_image_base();
private:
@ -117,6 +119,8 @@ namespace detail {
template<std::size_t Bits>
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());
}
}
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);
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()) {
// 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
auto obj = elf::open_elf(object_path);
auto elf_object = open_elf_cached(object_path);
// TODO: Cache the error
if(!obj) {
return obj.unwrap_error();
if(!elf_object) {
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()) {
return std::move(base).unwrap_error();
}
@ -57,12 +57,12 @@ namespace detail {
if(it == cache.end()) {
// 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
auto obj = mach_o::open_mach_o(object_path);
auto mach_o_object = open_mach_o_cached(object_path);
// TODO: Cache the error
if(!obj) {
return obj.unwrap_error();
if(!mach_o_object) {
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) {
return std::move(base).unwrap_error();
}

View File

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

View File

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

View File

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

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));
}
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 {
if(is_error()) {
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*)>;
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>
class maybe_owned {
std::unique_ptr<T> owned;
@ -289,6 +294,9 @@ namespace detail {
T* operator->() {
return ptr;
}
T& operator*() {
return *ptr;
}
};
}
}