From 0fc3983c8d25ce897ac8606815eea26919aa1c02 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 3 Mar 2021 21:17:32 +0100 Subject: [PATCH] Profiler: Cache and reuse mapped ELF objects In multi-process profiles, the same ELF objects tend to occur many times (everyone has libc.so for example) so we will quickly run out of VM if we map each object once per process that uses it. Fix this by adding a "mapped object cache" that maps the path of an ELF object to a cached memory mapping and wrapping ELF::Image. --- .../DevTools/Profiler/DisassemblyModel.cpp | 2 +- Userland/DevTools/Profiler/Profile.cpp | 45 ++++++++++++++----- Userland/DevTools/Profiler/Profile.h | 10 ++++- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/Userland/DevTools/Profiler/DisassemblyModel.cpp b/Userland/DevTools/Profiler/DisassemblyModel.cpp index b058df61525..10d29b8cf11 100644 --- a/Userland/DevTools/Profiler/DisassemblyModel.cpp +++ b/Userland/DevTools/Profiler/DisassemblyModel.cpp @@ -74,7 +74,7 @@ DisassemblyModel::DisassemblyModel(Profile& profile, ProfileNode& node) dbgln("no library data"); return; } - elf = &library_data->elf; + elf = &library_data->object->elf; base_address = library_data->base; } diff --git a/Userland/DevTools/Profiler/Profile.cpp b/Userland/DevTools/Profiler/Profile.cpp index 893f02a0e54..1f61b1c3e36 100644 --- a/Userland/DevTools/Profiler/Profile.cpp +++ b/Userland/DevTools/Profiler/Profile.cpp @@ -316,7 +316,11 @@ Result, String> Profile::load_from_perfcore_file(const St library_metadata = it->library_metadata.ptr(); if (auto* library = library_metadata ? library_metadata->library_containing(ptr) : nullptr) { object_name = library->name; - symbol = library->elf.symbolicate(ptr - library->base, &offset); + if (library->object) { + symbol = library->object->elf.symbolicate(ptr - library->base, &offset); + } else { + symbol = "??"; + } } else { symbol = "??"; } @@ -401,6 +405,32 @@ GUI::Model* Profile::disassembly_model() return m_disassembly_model; } +HashMap> g_mapped_object_cache; + +static MappedObject* get_or_create_mapped_object(const String& path) +{ + if (auto it = g_mapped_object_cache.find(path); it != g_mapped_object_cache.end()) + return it->value.ptr(); + + auto file_or_error = MappedFile::map(path); + if (file_or_error.is_error()) { + g_mapped_object_cache.set(path, {}); + return nullptr; + } + auto elf = ELF::Image(file_or_error.value()->bytes()); + if (!elf.is_valid()) { + g_mapped_object_cache.set(path, {}); + return nullptr; + } + auto new_mapped_object = adopt_own(*new MappedObject { + .file = file_or_error.release_value(), + .elf = move(elf), + }); + auto* ptr = new_mapped_object.ptr(); + g_mapped_object_cache.set(path, move(new_mapped_object)); + return ptr; +} + LibraryMetadata::LibraryMetadata(JsonArray regions) : m_regions(move(regions)) { @@ -421,16 +451,11 @@ LibraryMetadata::LibraryMetadata(JsonArray regions) if (name.contains(".so")) path = String::formatted("/usr/lib/{}", path); - auto file_or_error = MappedFile::map(path); - if (file_or_error.is_error()) { - m_libraries.set(name, {}); + auto* mapped_object = get_or_create_mapped_object(path); + if (!mapped_object) continue; - } - auto elf = ELF::Image(file_or_error.value()->bytes()); - if (!elf.is_valid()) - continue; - auto library = adopt_own(*new Library { base, size, name, file_or_error.release_value(), move(elf) }); - m_libraries.set(name, move(library)); + + m_libraries.set(name, adopt_own(*new Library { base, size, name, mapped_object })); } } diff --git a/Userland/DevTools/Profiler/Profile.h b/Userland/DevTools/Profiler/Profile.h index efacf40c2f6..63728410379 100644 --- a/Userland/DevTools/Profiler/Profile.h +++ b/Userland/DevTools/Profiler/Profile.h @@ -44,6 +44,13 @@ class Profile; class ProfileModel; class SamplesModel; +struct MappedObject { + NonnullRefPtr file; + ELF::Image elf; +}; + +extern HashMap> g_mapped_object_cache; + class LibraryMetadata { public: explicit LibraryMetadata(JsonArray regions); @@ -52,8 +59,7 @@ public: FlatPtr base; size_t size; String name; - NonnullRefPtr file; - ELF::Image elf; + MappedObject* object { nullptr }; }; const Library* library_containing(FlatPtr) const;