Browse Source

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.
Andreas Kling 4 years ago
parent
commit
0fc3983c8d

+ 1 - 1
Userland/DevTools/Profiler/DisassemblyModel.cpp

@@ -74,7 +74,7 @@ DisassemblyModel::DisassemblyModel(Profile& profile, ProfileNode& node)
             dbgln("no library data");
             dbgln("no library data");
             return;
             return;
         }
         }
-        elf = &library_data->elf;
+        elf = &library_data->object->elf;
         base_address = library_data->base;
         base_address = library_data->base;
     }
     }
 
 

+ 35 - 10
Userland/DevTools/Profiler/Profile.cpp

@@ -316,7 +316,11 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
                     library_metadata = it->library_metadata.ptr();
                     library_metadata = it->library_metadata.ptr();
                 if (auto* library = library_metadata ? library_metadata->library_containing(ptr) : nullptr) {
                 if (auto* library = library_metadata ? library_metadata->library_containing(ptr) : nullptr) {
                     object_name = library->name;
                     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 {
                 } else {
                     symbol = "??";
                     symbol = "??";
                 }
                 }
@@ -401,6 +405,32 @@ GUI::Model* Profile::disassembly_model()
     return m_disassembly_model;
     return m_disassembly_model;
 }
 }
 
 
+HashMap<String, OwnPtr<MappedObject>> 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)
 LibraryMetadata::LibraryMetadata(JsonArray regions)
     : m_regions(move(regions))
     : m_regions(move(regions))
 {
 {
@@ -421,16 +451,11 @@ LibraryMetadata::LibraryMetadata(JsonArray regions)
         if (name.contains(".so"))
         if (name.contains(".so"))
             path = String::formatted("/usr/lib/{}", path);
             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;
             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 }));
     }
     }
 }
 }
 
 

+ 8 - 2
Userland/DevTools/Profiler/Profile.h

@@ -44,6 +44,13 @@ class Profile;
 class ProfileModel;
 class ProfileModel;
 class SamplesModel;
 class SamplesModel;
 
 
+struct MappedObject {
+    NonnullRefPtr<MappedFile> file;
+    ELF::Image elf;
+};
+
+extern HashMap<String, OwnPtr<MappedObject>> g_mapped_object_cache;
+
 class LibraryMetadata {
 class LibraryMetadata {
 public:
 public:
     explicit LibraryMetadata(JsonArray regions);
     explicit LibraryMetadata(JsonArray regions);
@@ -52,8 +59,7 @@ public:
         FlatPtr base;
         FlatPtr base;
         size_t size;
         size_t size;
         String name;
         String name;
-        NonnullRefPtr<MappedFile> file;
-        ELF::Image elf;
+        MappedObject* object { nullptr };
     };
     };
 
 
     const Library* library_containing(FlatPtr) const;
     const Library* library_containing(FlatPtr) const;