瀏覽代碼

ProfileViewer: Add basic support for loading "perfcore" files

"perfcore" is the file that the kernel generates after a process that
was recording performance events has exited.

This patch teaches ProfileViewer how to load (and symbolicate!) those
files so that we can look at them. This will need a bunch more work
to make it truly useful.
Andreas Kling 5 年之前
父節點
當前提交
266d7ca268
共有 3 個文件被更改,包括 74 次插入1 次删除
  1. 66 0
      DevTools/ProfileViewer/Profile.cpp
  2. 1 0
      DevTools/ProfileViewer/Profile.h
  3. 7 1
      DevTools/ProfileViewer/main.cpp

+ 66 - 0
DevTools/ProfileViewer/Profile.cpp

@@ -26,8 +26,10 @@
 
 
 #include "Profile.h"
 #include "Profile.h"
 #include "ProfileModel.h"
 #include "ProfileModel.h"
+#include <AK/MappedFile.h>
 #include <AK/QuickSort.h>
 #include <AK/QuickSort.h>
 #include <LibCore/CFile.h>
 #include <LibCore/CFile.h>
+#include <LibELF/ELFLoader.h>
 #include <stdio.h>
 #include <stdio.h>
 
 
 static void sort_profile_nodes(Vector<NonnullRefPtr<ProfileNode>>& nodes)
 static void sort_profile_nodes(Vector<NonnullRefPtr<ProfileNode>>& nodes)
@@ -50,6 +52,7 @@ Profile::Profile(const JsonArray& json)
 
 
     m_samples.ensure_capacity(m_json.size());
     m_samples.ensure_capacity(m_json.size());
     for (auto& sample_value : m_json.values()) {
     for (auto& sample_value : m_json.values()) {
+
         auto& sample_object = sample_value.as_object();
         auto& sample_object = sample_value.as_object();
 
 
         Sample sample;
         Sample sample;
@@ -155,6 +158,69 @@ void Profile::rebuild_tree()
     m_model->update();
     m_model->update();
 }
 }
 
 
+OwnPtr<Profile> Profile::load_from_perfcore_file(const StringView& path)
+{
+    auto file = Core::File::construct(path);
+    if (!file->open(Core::IODevice::ReadOnly)) {
+        fprintf(stderr, "Unable to open %s, error: %s\n", String(path).characters(), file->error_string());
+        return nullptr;
+    }
+
+    auto json = JsonValue::from_string(file->read_all());
+    if (!json.is_object()) {
+        fprintf(stderr, "Invalid perfcore format (not a JSON object)\n");
+        return nullptr;
+    }
+
+    auto& object = json.as_object();
+    auto executable_path = object.get("executable").to_string();
+
+    MappedFile elf_file(executable_path);
+    if (!elf_file.is_valid()) {
+        fprintf(stderr, "Unable to open executable '%s' for symbolication.\n", executable_path.characters());
+        return nullptr;
+    }
+
+    auto elf_loader = make<ELFLoader>(static_cast<const u8*>(elf_file.data()), elf_file.size());
+
+    auto events_value = object.get("events");
+    if (!events_value.is_array())
+        return nullptr;
+
+    auto& perf_events = events_value.as_array();
+    if (perf_events.is_empty())
+        return nullptr;
+
+    JsonArray profile_events;
+
+    for (auto& perf_event_value : perf_events.values()) {
+        auto& perf_event = perf_event_value.as_object();
+
+        JsonObject object;
+        object.set("timestamp", perf_event.get("timestamp"));
+
+        JsonArray frames_array;
+        auto stack_array = perf_event.get("stack").as_array();
+
+        for (auto& frame : stack_array.values()) {
+            auto ptr = frame.to_number<u32>();
+            u32 offset = 0;
+            auto symbol = elf_loader->symbolicate(ptr, &offset);
+
+            JsonObject frame_object;
+            frame_object.set("address", ptr);
+            frame_object.set("symbol", symbol);
+            frame_object.set("offset", offset);
+            frames_array.append(move(frame_object));
+        }
+
+        object.set("frames", move(frames_array));
+        profile_events.append(move(object));
+    }
+
+    return NonnullOwnPtr<Profile>(NonnullOwnPtr<Profile>::Adopt, *new Profile(move(profile_events)));
+}
+
 OwnPtr<Profile> Profile::load_from_file(const StringView& path)
 OwnPtr<Profile> Profile::load_from_file(const StringView& path)
 {
 {
     auto file = Core::File::construct(path);
     auto file = Core::File::construct(path);

+ 1 - 0
DevTools/ProfileViewer/Profile.h

@@ -105,6 +105,7 @@ private:
 class Profile {
 class Profile {
 public:
 public:
     static OwnPtr<Profile> load_from_file(const StringView& path);
     static OwnPtr<Profile> load_from_file(const StringView& path);
+    static OwnPtr<Profile> load_from_perfcore_file(const StringView& path);
     ~Profile();
     ~Profile();
 
 
     GUI::Model& model();
     GUI::Model& model();

+ 7 - 1
DevTools/ProfileViewer/main.cpp

@@ -43,8 +43,14 @@ int main(int argc, char** argv)
     }
     }
 
 
     const char* path = argv[1];
     const char* path = argv[1];
+    OwnPtr<Profile> profile;
+
+    if (!strcmp(path, "perfcore")) {
+        profile = Profile::load_from_perfcore_file(path);
+    } else {
+        profile = Profile::load_from_file(path);
+    }
 
 
-    auto profile = Profile::load_from_file(path);
     if (!profile) {
     if (!profile) {
         fprintf(stderr, "Unable to load profile '%s'\n", path);
         fprintf(stderr, "Unable to load profile '%s'\n", path);
         return 1;
         return 1;