Selaa lähdekoodia

Profiler: Split the call tree into one subtree per process

This patch adds an additional level of hierarchy to the call tree:
Every process gets its own top-level node. :^)

Before this, selecting multiple processes would get quite confusing
as all the call stacks from different processes were combined together
into one big tree.
Andreas Kling 4 vuotta sitten
vanhempi
commit
8a5c78e93b

+ 2 - 6
Userland/DevTools/Profiler/DisassemblyModel.cpp

@@ -50,12 +50,8 @@ DisassemblyModel::DisassemblyModel(Profile& profile, ProfileNode& node)
         kernel_elf = make<ELF::Image>((const u8*)m_kernel_file->data(), m_kernel_file->size());
         elf = kernel_elf.ptr();
     } else {
-        auto process = node.process(profile, node.timestamp());
-        if (!process) {
-            dbgln("no process for address {:p}", node.address());
-            return;
-        }
-        auto library_data = process->library_metadata.library_containing(node.address());
+        auto& process = node.process();
+        auto library_data = process.library_metadata.library_containing(node.address());
         if (!library_data) {
             dbgln("no library data for address {:p}", node.address());
             return;

+ 1 - 0
Userland/DevTools/Profiler/Process.h

@@ -54,6 +54,7 @@ struct Thread {
 struct Process {
     pid_t pid {};
     String executable;
+    String basename;
     HashMap<int, Vector<Thread>> threads {};
     LibraryMetadata library_metadata {};
     u64 start_valid;

+ 23 - 15
Userland/DevTools/Profiler/Profile.cpp

@@ -61,13 +61,13 @@ void Profile::rebuild_tree()
 {
     Vector<NonnullRefPtr<ProfileNode>> roots;
 
-    auto find_or_create_root = [&roots](FlyString object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t pid) -> ProfileNode& {
+    auto find_or_create_process_node = [this, &roots](pid_t pid, u64 timestamp) -> ProfileNode& {
+        auto& process = *find_process(pid, timestamp);
         for (auto root : roots) {
-            if (root->symbol() == symbol) {
+            if (&root->process() == &process)
                 return root;
-            }
         }
-        auto new_root = ProfileNode::create(move(object_name), move(symbol), address, offset, timestamp, pid);
+        auto new_root = ProfileNode::create_process_node(process);
         roots.append(new_root);
         return new_root;
     };
@@ -119,6 +119,8 @@ void Profile::rebuild_tree()
 
         if (!m_show_top_functions) {
             ProfileNode* node = nullptr;
+            auto& process_node = find_or_create_process_node(event.pid, event.timestamp);
+            process_node.increment_event_count();
             for_each_frame([&](const Frame& frame, bool is_innermost_frame) {
                 auto& object_name = frame.object_name;
                 auto& symbol = frame.symbol;
@@ -130,9 +132,8 @@ void Profile::rebuild_tree()
 
                 // FIXME: More cheating with intentional mixing of TID/PID here:
                 if (!node)
-                    node = &find_or_create_root(object_name, symbol, address, offset, event.timestamp, event.pid);
-                else
-                    node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid);
+                    node = &process_node;
+                node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid);
 
                 node->increment_event_count();
                 if (is_innermost_frame) {
@@ -142,6 +143,8 @@ void Profile::rebuild_tree()
                 return IterationDecision::Continue;
             });
         } else {
+            auto& process_node = find_or_create_process_node(event.pid, event.timestamp);
+            process_node.increment_event_count();
             for (size_t i = 0; i < event.frames.size(); ++i) {
                 ProfileNode* node = nullptr;
                 ProfileNode* root = nullptr;
@@ -156,7 +159,8 @@ void Profile::rebuild_tree()
 
                     // FIXME: More PID/TID mixing cheats here:
                     if (!node) {
-                        node = &find_or_create_root(object_name, symbol, address, offset, event.timestamp, event.pid);
+                        node = &find_or_create_process_node(event.pid, event.timestamp);
+                        node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid);
                         root = node;
                         root->will_track_seen_events(m_events.size());
                     } else {
@@ -248,6 +252,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
             auto sampled_process = adopt_own(*new Process {
                 .pid = event.pid,
                 .executable = event.executable,
+                .basename = LexicalPath(event.executable).basename(),
                 .start_valid = event.timestamp,
             });
 
@@ -265,6 +270,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
             auto sampled_process = adopt_own(*new Process {
                 .pid = event.pid,
                 .executable = event.executable,
+                .basename = LexicalPath(event.executable).basename(),
                 .start_valid = event.timestamp });
 
             current_processes.set(sampled_process->pid, sampled_process);
@@ -463,8 +469,15 @@ GUI::Model* Profile::disassembly_model()
     return m_disassembly_model;
 }
 
-ProfileNode::ProfileNode(const String& object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t pid)
-    : m_symbol(move(symbol))
+ProfileNode::ProfileNode(Process const& process)
+    : m_root(true)
+    , m_process(process)
+{
+}
+
+ProfileNode::ProfileNode(Process const& process, const String& object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t pid)
+    : m_process(process)
+    , m_symbol(move(symbol))
     , m_pid(pid)
     , m_address(address)
     , m_offset(offset)
@@ -479,9 +492,4 @@ ProfileNode::ProfileNode(const String& object_name, String symbol, u32 address,
     m_object_name = LexicalPath(object).basename();
 }
 
-const Process* ProfileNode::process(Profile& profile, u64 timestamp) const
-{
-    return profile.find_process(m_pid, timestamp);
-}
-
 }

+ 14 - 5
Userland/DevTools/Profiler/Profile.h

@@ -28,9 +28,14 @@ namespace Profiler {
 
 class ProfileNode : public RefCounted<ProfileNode> {
 public:
-    static NonnullRefPtr<ProfileNode> create(FlyString object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t pid)
+    static NonnullRefPtr<ProfileNode> create(Process const& process, FlyString object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t pid)
     {
-        return adopt_ref(*new ProfileNode(move(object_name), move(symbol), address, offset, timestamp, pid));
+        return adopt_ref(*new ProfileNode(process, move(object_name), move(symbol), address, offset, timestamp, pid));
+    }
+
+    static NonnullRefPtr<ProfileNode> create_process_node(Process const& process)
+    {
+        return adopt_ref(*new ProfileNode(process));
     }
 
     // These functions are only relevant for root nodes
@@ -71,7 +76,7 @@ public:
                 return child;
             }
         }
-        auto new_child = ProfileNode::create(move(object_name), move(symbol), address, offset, timestamp, pid);
+        auto new_child = ProfileNode::create(m_process, move(object_name), move(symbol), address, offset, timestamp, pid);
         add_child(new_child);
         return new_child;
     };
@@ -96,11 +101,15 @@ public:
 
     pid_t pid() const { return m_pid; }
 
-    const Process* process(Profile&, u64 timestamp) const;
+    Process const& process() const { return m_process; }
+    bool is_root() const { return m_root; }
 
 private:
-    explicit ProfileNode(const String& object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t);
+    explicit ProfileNode(Process const&);
+    explicit ProfileNode(Process const&, const String& object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t);
 
+    bool m_root { false };
+    Process const& m_process;
     ProfileNode* m_parent { nullptr };
     FlyString m_object_name;
     String m_symbol;

+ 9 - 1
Userland/DevTools/Profiler/ProfileModel.cpp

@@ -7,6 +7,7 @@
 #include "ProfileModel.h"
 #include "Profile.h"
 #include <AK/StringBuilder.h>
+#include <LibGUI/FileIconProvider.h>
 #include <ctype.h>
 #include <stdio.h>
 
@@ -101,6 +102,9 @@ GUI::Variant ProfileModel::data(const GUI::ModelIndex& index, GUI::ModelRole rol
     }
     if (role == GUI::ModelRole::Icon) {
         if (index.column() == Column::StackFrame) {
+            if (node->is_root()) {
+                return GUI::FileIconProvider::icon_for_executable(node->process().executable);
+            }
             if (node->address() >= 0xc0000000)
                 return m_kernel_frame_icon;
             return m_user_frame_icon;
@@ -120,8 +124,12 @@ GUI::Variant ProfileModel::data(const GUI::ModelIndex& index, GUI::ModelRole rol
         }
         if (index.column() == Column::ObjectName)
             return node->object_name();
-        if (index.column() == Column::StackFrame)
+        if (index.column() == Column::StackFrame) {
+            if (node->is_root()) {
+                return String::formatted("{} ({})", node->process().basename, node->process().pid);
+            }
             return node->symbol();
+        }
         return {};
     }
     return {};