mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 23:37:35 +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:
parent
5ddb747038
commit
fe421bd7b4
3 changed files with 57 additions and 16 deletions
|
@ -4,6 +4,16 @@
|
|||
#include <LibCore/CFile.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)
|
||||
: m_json(json)
|
||||
{
|
||||
|
@ -22,7 +32,7 @@ Profile::Profile(const JsonArray& json)
|
|||
|
||||
auto frames_value = sample_object.get("frames");
|
||||
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_object = frame_value.as_object();
|
||||
Frame frame;
|
||||
|
@ -51,12 +61,12 @@ GModel& Profile::model()
|
|||
|
||||
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& {
|
||||
for (int i = 0; i < roots.size(); ++i) {
|
||||
auto& root = roots[i];
|
||||
if (root.symbol() == symbol) {
|
||||
if (root->symbol() == symbol) {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
@ -73,15 +83,29 @@ void Profile::rebuild_tree()
|
|||
}
|
||||
|
||||
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& address = frame.address;
|
||||
auto& offset = frame.offset;
|
||||
|
||||
if (symbol.is_empty())
|
||||
break;
|
||||
return IterationDecision::Break;
|
||||
|
||||
if (!node)
|
||||
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->increment_sample_count();
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& root : roots) {
|
||||
root.sort_children();
|
||||
}
|
||||
sort_profile_nodes(roots);
|
||||
|
||||
m_roots = move(roots);
|
||||
m_model->update();
|
||||
|
@ -123,12 +146,7 @@ OwnPtr<Profile> Profile::load_from_file(const StringView& path)
|
|||
|
||||
void ProfileNode::sort_children()
|
||||
{
|
||||
quick_sort(m_children.begin(), m_children.end(), [](auto& a, auto& b) {
|
||||
return a->sample_count() >= b->sample_count();
|
||||
});
|
||||
|
||||
for (auto& child : m_children)
|
||||
child->sort_children();
|
||||
sort_profile_nodes(m_children);
|
||||
}
|
||||
|
||||
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;
|
||||
rebuild_tree();
|
||||
}
|
||||
|
||||
void Profile::set_inverted(bool inverted)
|
||||
{
|
||||
if (m_inverted == inverted)
|
||||
return;
|
||||
m_inverted = inverted;
|
||||
rebuild_tree();
|
||||
}
|
||||
|
|
|
@ -105,6 +105,9 @@ public:
|
|||
void clear_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:
|
||||
explicit Profile(const JsonArray&);
|
||||
|
||||
|
@ -123,4 +126,5 @@ private:
|
|||
u64 m_timestamp_filter_range_end { 0 };
|
||||
|
||||
u32 m_deepest_stack_depth { 0 };
|
||||
bool m_inverted { false };
|
||||
};
|
||||
|
|
|
@ -48,6 +48,17 @@ int main(int argc, char** argv)
|
|||
|
||||
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));
|
||||
|
||||
window->show();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue