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();