1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:17:44 +00:00

ProfileViewer: Add the ability to invert the profile tree

Inverting the tree turns all of the innermost stack frames into roots,
allowing them to accumulate their total sample counts with other
instances of the same frame being innermost. This is an essential
feature of any cool profiler, and now we have it. :^)
This commit is contained in:
Andreas Kling 2019-12-16 18:21:05 +01:00
parent 5ddb747038
commit fe421bd7b4
3 changed files with 57 additions and 16 deletions

View file

@ -4,6 +4,16 @@
#include <LibCore/CFile.h> #include <LibCore/CFile.h>
#include <stdio.h> #include <stdio.h>
static void sort_profile_nodes(Vector<NonnullRefPtr<ProfileNode>>& nodes)
{
quick_sort(nodes.begin(), nodes.end(), [](auto& a, auto& b) {
return a->sample_count() >= b->sample_count();
});
for (auto& child : nodes)
child->sort_children();
}
Profile::Profile(const JsonArray& json) Profile::Profile(const JsonArray& json)
: m_json(json) : m_json(json)
{ {
@ -22,7 +32,7 @@ Profile::Profile(const JsonArray& json)
auto frames_value = sample_object.get("frames"); auto frames_value = sample_object.get("frames");
auto& frames_array = frames_value.as_array(); auto& frames_array = frames_value.as_array();
for (int i = frames_array.size() - 1; i >= 0; --i) { for (int i = frames_array.size() - 1; i >= 1; --i) {
auto& frame_value = frames_array.at(i); auto& frame_value = frames_array.at(i);
auto& frame_object = frame_value.as_object(); auto& frame_object = frame_value.as_object();
Frame frame; Frame frame;
@ -51,12 +61,12 @@ GModel& Profile::model()
void Profile::rebuild_tree() void Profile::rebuild_tree()
{ {
NonnullRefPtrVector<ProfileNode> roots; Vector<NonnullRefPtr<ProfileNode>> roots;
auto find_or_create_root = [&roots](const String& symbol, u32 address, u32 offset, u64 timestamp) -> ProfileNode& { auto find_or_create_root = [&roots](const String& symbol, u32 address, u32 offset, u64 timestamp) -> ProfileNode& {
for (int i = 0; i < roots.size(); ++i) { for (int i = 0; i < roots.size(); ++i) {
auto& root = roots[i]; auto& root = roots[i];
if (root.symbol() == symbol) { if (root->symbol() == symbol) {
return root; return root;
} }
} }
@ -73,15 +83,29 @@ void Profile::rebuild_tree()
} }
ProfileNode* node = nullptr; ProfileNode* node = nullptr;
for (int i = 0; i < sample.frames.size(); ++i) {
auto& frame = sample.frames.at(i);
auto for_each_frame = [&]<typename Callback>(Callback callback)
{
if (!m_inverted) {
for (int i = 0; i < sample.frames.size(); ++i) {
if (callback(sample.frames.at(i)) == IterationDecision::Break)
break;
}
} else {
for (int i = sample.frames.size() - 1; i >= 0; --i) {
if (callback(sample.frames.at(i)) == IterationDecision::Break)
break;
}
}
};
for_each_frame([&](const Frame& frame) {
auto& symbol = frame.symbol; auto& symbol = frame.symbol;
auto& address = frame.address; auto& address = frame.address;
auto& offset = frame.offset; auto& offset = frame.offset;
if (symbol.is_empty()) if (symbol.is_empty())
break; return IterationDecision::Break;
if (!node) if (!node)
node = &find_or_create_root(symbol, address, offset, sample.timestamp); node = &find_or_create_root(symbol, address, offset, sample.timestamp);
@ -89,12 +113,11 @@ void Profile::rebuild_tree()
node = &node->find_or_create_child(symbol, address, offset, sample.timestamp); node = &node->find_or_create_child(symbol, address, offset, sample.timestamp);
node->increment_sample_count(); node->increment_sample_count();
} return IterationDecision::Continue;
});
} }
for (auto& root : roots) { sort_profile_nodes(roots);
root.sort_children();
}
m_roots = move(roots); m_roots = move(roots);
m_model->update(); m_model->update();
@ -123,12 +146,7 @@ OwnPtr<Profile> Profile::load_from_file(const StringView& path)
void ProfileNode::sort_children() void ProfileNode::sort_children()
{ {
quick_sort(m_children.begin(), m_children.end(), [](auto& a, auto& b) { sort_profile_nodes(m_children);
return a->sample_count() >= b->sample_count();
});
for (auto& child : m_children)
child->sort_children();
} }
void Profile::set_timestamp_filter_range(u64 start, u64 end) void Profile::set_timestamp_filter_range(u64 start, u64 end)
@ -150,3 +168,11 @@ void Profile::clear_timestamp_filter_range()
m_has_timestamp_filter_range = false; m_has_timestamp_filter_range = false;
rebuild_tree(); rebuild_tree();
} }
void Profile::set_inverted(bool inverted)
{
if (m_inverted == inverted)
return;
m_inverted = inverted;
rebuild_tree();
}

View file

@ -105,6 +105,9 @@ public:
void clear_timestamp_filter_range(); void clear_timestamp_filter_range();
bool has_timestamp_filter_range() const { return m_has_timestamp_filter_range; } bool has_timestamp_filter_range() const { return m_has_timestamp_filter_range; }
bool is_inverted() const { return m_inverted; }
void set_inverted(bool);
private: private:
explicit Profile(const JsonArray&); explicit Profile(const JsonArray&);
@ -123,4 +126,5 @@ private:
u64 m_timestamp_filter_range_end { 0 }; u64 m_timestamp_filter_range_end { 0 };
u32 m_deepest_stack_depth { 0 }; u32 m_deepest_stack_depth { 0 };
bool m_inverted { false };
}; };

View file

@ -48,6 +48,17 @@ int main(int argc, char** argv)
menubar->add_menu(move(app_menu)); menubar->add_menu(move(app_menu));
auto view_menu = GMenu::construct("View");
auto invert_action = GAction::create("Invert tree", { Mod_Ctrl, Key_I }, [&](auto& action) {
action.set_checked(!action.is_checked());
profile->set_inverted(action.is_checked());
});
invert_action->set_checkable(true);
invert_action->set_checked(true);
view_menu->add_action(invert_action);
menubar->add_menu(move(view_menu));
app.set_menubar(move(menubar)); app.set_menubar(move(menubar));
window->show(); window->show();