diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-12-14 19:10:12 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-12-14 19:10:12 +0100 |
commit | 3fd2304dadd13334afcd43e2abe4e1ea0331d331 (patch) | |
tree | 8e96b5b531fde358dab5b893b18b62ffd6a6b50d /DevTools/ProfileViewer | |
parent | a3e7c99ffea8bd5f70edf9d6ef941c4e7303833f (diff) | |
download | serenity-3fd2304dadd13334afcd43e2abe4e1ea0331d331.zip |
ProfileViewer: Allow filtering samples in a specific time range
You can now select the time range you want on the profile timeline.
The tree view will update automatically as you alter the range.
Unfortunately this causes the treeview to collapse all of its nodes.
It would be nice to solve this somehow in the future so that nodes
can stay open.
Diffstat (limited to 'DevTools/ProfileViewer')
-rw-r--r-- | DevTools/ProfileViewer/Profile.cpp | 80 | ||||
-rw-r--r-- | DevTools/ProfileViewer/Profile.h | 12 | ||||
-rw-r--r-- | DevTools/ProfileViewer/ProfileTimelineWidget.cpp | 40 | ||||
-rw-r--r-- | DevTools/ProfileViewer/ProfileTimelineWidget.h | 6 |
4 files changed, 109 insertions, 29 deletions
diff --git a/DevTools/ProfileViewer/Profile.cpp b/DevTools/ProfileViewer/Profile.cpp index 4c24ae4394..cbd0caf683 100644 --- a/DevTools/ProfileViewer/Profile.cpp +++ b/DevTools/ProfileViewer/Profile.cpp @@ -4,13 +4,14 @@ #include <LibCore/CFile.h> #include <stdio.h> -Profile::Profile(const JsonArray& json, Vector<NonnullRefPtr<ProfileNode>>&& roots, u64 first_timestamp, u64 last_timestamp) +Profile::Profile(const JsonArray& json) : m_json(json) - , m_roots(move(roots)) - , m_first_timestamp(first_timestamp) - , m_last_timestamp(last_timestamp) { + m_first_timestamp = m_json.at(0).as_object().get("timestamp").to_number<u64>(); + m_last_timestamp = m_json.at(m_json.size() - 1).as_object().get("timestamp").to_number<u64>(); + m_model = ProfileModel::create(*this); + rebuild_tree(); } Profile::~Profile() @@ -22,24 +23,8 @@ GModel& Profile::model() return *m_model; } -OwnPtr<Profile> Profile::load_from_file(const StringView& path) +void Profile::rebuild_tree() { - auto file = CFile::construct(path); - if (!file->open(CIODevice::ReadOnly)) { - fprintf(stderr, "Unable to open %s, error: %s\n", String(path).characters(), file->error_string()); - return nullptr; - } - - auto json = JsonValue::from_string(file->read_all()); - if (!json.is_array()) { - fprintf(stderr, "Invalid format (not a JSON array)\n"); - return nullptr; - } - - auto& samples = json.as_array(); - if (samples.is_empty()) - return nullptr; - NonnullRefPtrVector<ProfileNode> roots; auto find_or_create_root = [&roots](const String& symbol, u32 address, u32 offset, u64 timestamp) -> ProfileNode& { @@ -54,10 +39,13 @@ OwnPtr<Profile> Profile::load_from_file(const StringView& path) return new_root; }; - u64 first_timestamp = samples.at(0).as_object().get("timestamp").to_number<u64>(); - u64 last_timestamp = samples.at(samples.size() - 1).as_object().get("timestamp").to_number<u64>(); + m_json.for_each([&](const JsonValue& sample) { + if (has_timestamp_filter_range()) { + auto timestamp = sample.as_object().get("timestamp").to_number<u64>(); + if (timestamp < m_timestamp_filter_range_start || timestamp > m_timestamp_filter_range_end) + return; + } - samples.for_each([&](const JsonValue& sample) { auto frames_value = sample.as_object().get("frames"); auto& frames = frames_value.as_array(); ProfileNode* node = nullptr; @@ -85,7 +73,29 @@ OwnPtr<Profile> Profile::load_from_file(const StringView& path) root.sort_children(); } - return NonnullOwnPtr<Profile>(NonnullOwnPtr<Profile>::Adopt, *new Profile(move(samples), move(roots), first_timestamp, last_timestamp)); + m_roots = move(roots); + m_model->update(); +} + +OwnPtr<Profile> Profile::load_from_file(const StringView& path) +{ + auto file = CFile::construct(path); + if (!file->open(CIODevice::ReadOnly)) { + fprintf(stderr, "Unable to open %s, error: %s\n", String(path).characters(), file->error_string()); + return nullptr; + } + + auto json = JsonValue::from_string(file->read_all()); + if (!json.is_array()) { + fprintf(stderr, "Invalid format (not a JSON array)\n"); + return nullptr; + } + + auto& samples = json.as_array(); + if (samples.is_empty()) + return nullptr; + + return NonnullOwnPtr<Profile>(NonnullOwnPtr<Profile>::Adopt, *new Profile(move(samples))); } void ProfileNode::sort_children() @@ -97,3 +107,23 @@ void ProfileNode::sort_children() for (auto& child : m_children) child->sort_children(); } + +void Profile::set_timestamp_filter_range(u64 start, u64 end) +{ + if (m_has_timestamp_filter_range && m_timestamp_filter_range_start == start && m_timestamp_filter_range_end == end) + return; + m_has_timestamp_filter_range = true; + + m_timestamp_filter_range_start = min(start, end); + m_timestamp_filter_range_end = max(start, end); + + rebuild_tree(); +} + +void Profile::clear_timestamp_filter_range() +{ + if (!m_has_timestamp_filter_range) + return; + m_has_timestamp_filter_range = false; + rebuild_tree(); +} diff --git a/DevTools/ProfileViewer/Profile.h b/DevTools/ProfileViewer/Profile.h index 13c83beb46..9305fc4d35 100644 --- a/DevTools/ProfileViewer/Profile.h +++ b/DevTools/ProfileViewer/Profile.h @@ -94,12 +94,22 @@ public: u64 first_timestamp() const { return m_first_timestamp; } u64 last_timestamp() const { return m_first_timestamp; } + void set_timestamp_filter_range(u64 start, u64 end); + void clear_timestamp_filter_range(); + bool has_timestamp_filter_range() const { return m_has_timestamp_filter_range; } + private: - explicit Profile(const JsonArray&, Vector<NonnullRefPtr<ProfileNode>>&&, u64 first_timestamp, u64 last_timestamp); + explicit Profile(const JsonArray&); + + void rebuild_tree(); JsonArray m_json; RefPtr<ProfileModel> m_model; Vector<NonnullRefPtr<ProfileNode>> m_roots; u64 m_first_timestamp { 0 }; u64 m_last_timestamp { 0 }; + + bool m_has_timestamp_filter_range { false }; + u64 m_timestamp_filter_range_start { 0 }; + u64 m_timestamp_filter_range_end { 0 }; }; diff --git a/DevTools/ProfileViewer/ProfileTimelineWidget.cpp b/DevTools/ProfileViewer/ProfileTimelineWidget.cpp index 6637d196c0..e841cb95cc 100644 --- a/DevTools/ProfileViewer/ProfileTimelineWidget.cpp +++ b/DevTools/ProfileViewer/ProfileTimelineWidget.cpp @@ -38,16 +38,50 @@ void ProfileTimelineWidget::paint_event(GPaintEvent& event) for (int i = 0; i < cw; ++i) painter.draw_line({ x + i, frame_thickness() }, { x + i, height() - frame_thickness() * 2 }, color); }); + + u64 normalized_start_time = min(m_select_start_time, m_select_end_time); + u64 normalized_end_time = max(m_select_start_time, m_select_end_time); + + int select_start_x = (int)((float)(normalized_start_time - m_profile.first_timestamp()) * column_width); + int select_end_x = (int)((float)(normalized_end_time - m_profile.first_timestamp()) * column_width); + painter.fill_rect({ select_start_x, frame_thickness(), select_end_x - select_start_x, height() - frame_thickness() * 2 }, Color(0, 0, 0, 60)); } -void ProfileTimelineWidget::mousedown_event(GMouseEvent&) +u64 ProfileTimelineWidget::timestamp_at_x(int x) const { + float column_width = (float)frame_inner_rect().width() / (float)m_profile.length_in_ms(); + float ms_into_profile = (float)x / column_width; + return m_profile.first_timestamp() + (u64)ms_into_profile; } -void ProfileTimelineWidget::mousemove_event(GMouseEvent&) +void ProfileTimelineWidget::mousedown_event(GMouseEvent& event) { + if (event.button() != GMouseButton::Left) + return; + + m_selecting = true; + m_select_start_time = timestamp_at_x(event.x()); + m_select_end_time = m_select_start_time; + m_profile.set_timestamp_filter_range(m_select_start_time, m_select_end_time); + update(); } -void ProfileTimelineWidget::mouseup_event(GMouseEvent&) +void ProfileTimelineWidget::mousemove_event(GMouseEvent& event) { + if (!m_selecting) + return; + + m_select_end_time = timestamp_at_x(event.x()); + m_profile.set_timestamp_filter_range(m_select_start_time, m_select_end_time); + update(); +} + +void ProfileTimelineWidget::mouseup_event(GMouseEvent& event) +{ + if (event.button() != GMouseButton::Left) + return; + + m_selecting = false; + if (m_select_start_time == m_select_end_time) + m_profile.clear_timestamp_filter_range(); } diff --git a/DevTools/ProfileViewer/ProfileTimelineWidget.h b/DevTools/ProfileViewer/ProfileTimelineWidget.h index eca99d3c58..6e438b2130 100644 --- a/DevTools/ProfileViewer/ProfileTimelineWidget.h +++ b/DevTools/ProfileViewer/ProfileTimelineWidget.h @@ -15,5 +15,11 @@ private: ProfileTimelineWidget(Profile&, GWidget* parent); + u64 timestamp_at_x(int x) const; + Profile& m_profile; + + bool m_selecting { false }; + u64 m_select_start_time { 0 }; + u64 m_select_end_time { 0 }; }; |