From fe421bd7b4c335c5e40f6a827b7138ae8ac5e0e1 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 16 Dec 2019 18:21:05 +0100 Subject: [PATCH] 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. :^) --- DevTools/ProfileViewer/Profile.cpp | 58 +++++++++++++++++++++--------- DevTools/ProfileViewer/Profile.h | 4 +++ DevTools/ProfileViewer/main.cpp | 11 ++++++ 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/DevTools/ProfileViewer/Profile.cpp b/DevTools/ProfileViewer/Profile.cpp index 11b9b9e567..c9bcfe48c8 100644 --- a/DevTools/ProfileViewer/Profile.cpp +++ b/DevTools/ProfileViewer/Profile.cpp @@ -4,6 +4,16 @@ #include #include +static void sort_profile_nodes(Vector>& 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 roots; + Vector> 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 = [&](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::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(); +} diff --git a/DevTools/ProfileViewer/Profile.h b/DevTools/ProfileViewer/Profile.h index 6ffb8514e9..a7c1773877 100644 --- a/DevTools/ProfileViewer/Profile.h +++ b/DevTools/ProfileViewer/Profile.h @@ -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 }; }; diff --git a/DevTools/ProfileViewer/main.cpp b/DevTools/ProfileViewer/main.cpp index 2a6c0ef627..8d8c1d08a3 100644 --- a/DevTools/ProfileViewer/main.cpp +++ b/DevTools/ProfileViewer/main.cpp @@ -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();