mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:07:35 +00:00
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.
This commit is contained in:
parent
65a341b82f
commit
8a5c78e93b
5 changed files with 49 additions and 27 deletions
|
@ -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());
|
kernel_elf = make<ELF::Image>((const u8*)m_kernel_file->data(), m_kernel_file->size());
|
||||||
elf = kernel_elf.ptr();
|
elf = kernel_elf.ptr();
|
||||||
} else {
|
} else {
|
||||||
auto process = node.process(profile, node.timestamp());
|
auto& process = node.process();
|
||||||
if (!process) {
|
auto library_data = process.library_metadata.library_containing(node.address());
|
||||||
dbgln("no process for address {:p}", node.address());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto library_data = process->library_metadata.library_containing(node.address());
|
|
||||||
if (!library_data) {
|
if (!library_data) {
|
||||||
dbgln("no library data for address {:p}", node.address());
|
dbgln("no library data for address {:p}", node.address());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -54,6 +54,7 @@ struct Thread {
|
||||||
struct Process {
|
struct Process {
|
||||||
pid_t pid {};
|
pid_t pid {};
|
||||||
String executable;
|
String executable;
|
||||||
|
String basename;
|
||||||
HashMap<int, Vector<Thread>> threads {};
|
HashMap<int, Vector<Thread>> threads {};
|
||||||
LibraryMetadata library_metadata {};
|
LibraryMetadata library_metadata {};
|
||||||
u64 start_valid;
|
u64 start_valid;
|
||||||
|
|
|
@ -61,13 +61,13 @@ void Profile::rebuild_tree()
|
||||||
{
|
{
|
||||||
Vector<NonnullRefPtr<ProfileNode>> roots;
|
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) {
|
for (auto root : roots) {
|
||||||
if (root->symbol() == symbol) {
|
if (&root->process() == &process)
|
||||||
return root;
|
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);
|
roots.append(new_root);
|
||||||
return new_root;
|
return new_root;
|
||||||
};
|
};
|
||||||
|
@ -119,6 +119,8 @@ void Profile::rebuild_tree()
|
||||||
|
|
||||||
if (!m_show_top_functions) {
|
if (!m_show_top_functions) {
|
||||||
ProfileNode* node = nullptr;
|
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) {
|
for_each_frame([&](const Frame& frame, bool is_innermost_frame) {
|
||||||
auto& object_name = frame.object_name;
|
auto& object_name = frame.object_name;
|
||||||
auto& symbol = frame.symbol;
|
auto& symbol = frame.symbol;
|
||||||
|
@ -130,9 +132,8 @@ void Profile::rebuild_tree()
|
||||||
|
|
||||||
// FIXME: More cheating with intentional mixing of TID/PID here:
|
// FIXME: More cheating with intentional mixing of TID/PID here:
|
||||||
if (!node)
|
if (!node)
|
||||||
node = &find_or_create_root(object_name, symbol, address, offset, event.timestamp, event.pid);
|
node = &process_node;
|
||||||
else
|
node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid);
|
||||||
node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid);
|
|
||||||
|
|
||||||
node->increment_event_count();
|
node->increment_event_count();
|
||||||
if (is_innermost_frame) {
|
if (is_innermost_frame) {
|
||||||
|
@ -142,6 +143,8 @@ void Profile::rebuild_tree()
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
} else {
|
} 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) {
|
for (size_t i = 0; i < event.frames.size(); ++i) {
|
||||||
ProfileNode* node = nullptr;
|
ProfileNode* node = nullptr;
|
||||||
ProfileNode* root = nullptr;
|
ProfileNode* root = nullptr;
|
||||||
|
@ -156,7 +159,8 @@ void Profile::rebuild_tree()
|
||||||
|
|
||||||
// FIXME: More PID/TID mixing cheats here:
|
// FIXME: More PID/TID mixing cheats here:
|
||||||
if (!node) {
|
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 = node;
|
||||||
root->will_track_seen_events(m_events.size());
|
root->will_track_seen_events(m_events.size());
|
||||||
} else {
|
} else {
|
||||||
|
@ -248,6 +252,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
|
||||||
auto sampled_process = adopt_own(*new Process {
|
auto sampled_process = adopt_own(*new Process {
|
||||||
.pid = event.pid,
|
.pid = event.pid,
|
||||||
.executable = event.executable,
|
.executable = event.executable,
|
||||||
|
.basename = LexicalPath(event.executable).basename(),
|
||||||
.start_valid = event.timestamp,
|
.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 {
|
auto sampled_process = adopt_own(*new Process {
|
||||||
.pid = event.pid,
|
.pid = event.pid,
|
||||||
.executable = event.executable,
|
.executable = event.executable,
|
||||||
|
.basename = LexicalPath(event.executable).basename(),
|
||||||
.start_valid = event.timestamp });
|
.start_valid = event.timestamp });
|
||||||
|
|
||||||
current_processes.set(sampled_process->pid, sampled_process);
|
current_processes.set(sampled_process->pid, sampled_process);
|
||||||
|
@ -463,8 +469,15 @@ GUI::Model* Profile::disassembly_model()
|
||||||
return m_disassembly_model;
|
return m_disassembly_model;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileNode::ProfileNode(const String& object_name, String symbol, u32 address, u32 offset, u64 timestamp, pid_t pid)
|
ProfileNode::ProfileNode(Process const& process)
|
||||||
: m_symbol(move(symbol))
|
: 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_pid(pid)
|
||||||
, m_address(address)
|
, m_address(address)
|
||||||
, m_offset(offset)
|
, m_offset(offset)
|
||||||
|
@ -479,9 +492,4 @@ ProfileNode::ProfileNode(const String& object_name, String symbol, u32 address,
|
||||||
m_object_name = LexicalPath(object).basename();
|
m_object_name = LexicalPath(object).basename();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Process* ProfileNode::process(Profile& profile, u64 timestamp) const
|
|
||||||
{
|
|
||||||
return profile.find_process(m_pid, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,14 @@ namespace Profiler {
|
||||||
|
|
||||||
class ProfileNode : public RefCounted<ProfileNode> {
|
class ProfileNode : public RefCounted<ProfileNode> {
|
||||||
public:
|
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
|
// These functions are only relevant for root nodes
|
||||||
|
@ -71,7 +76,7 @@ public:
|
||||||
return child;
|
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);
|
add_child(new_child);
|
||||||
return new_child;
|
return new_child;
|
||||||
};
|
};
|
||||||
|
@ -96,11 +101,15 @@ public:
|
||||||
|
|
||||||
pid_t pid() const { return m_pid; }
|
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:
|
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 };
|
ProfileNode* m_parent { nullptr };
|
||||||
FlyString m_object_name;
|
FlyString m_object_name;
|
||||||
String m_symbol;
|
String m_symbol;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "ProfileModel.h"
|
#include "ProfileModel.h"
|
||||||
#include "Profile.h"
|
#include "Profile.h"
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
|
#include <LibGUI/FileIconProvider.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.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 (role == GUI::ModelRole::Icon) {
|
||||||
if (index.column() == Column::StackFrame) {
|
if (index.column() == Column::StackFrame) {
|
||||||
|
if (node->is_root()) {
|
||||||
|
return GUI::FileIconProvider::icon_for_executable(node->process().executable);
|
||||||
|
}
|
||||||
if (node->address() >= 0xc0000000)
|
if (node->address() >= 0xc0000000)
|
||||||
return m_kernel_frame_icon;
|
return m_kernel_frame_icon;
|
||||||
return m_user_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)
|
if (index.column() == Column::ObjectName)
|
||||||
return node->object_name();
|
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 node->symbol();
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue