diff options
author | Andreas Kling <kling@serenityos.org> | 2021-08-11 20:05:41 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-08-12 00:03:38 +0200 |
commit | 00b11d757710ea3768373bb34745af3e31352efc (patch) | |
tree | 4b4971033bac248c404b2501f9a4084e72fc5a6b | |
parent | 9ae8cd823cf43106d37afd8d4cd4bcb515a56f91 (diff) | |
download | serenity-00b11d757710ea3768373bb34745af3e31352efc.zip |
Profiler: Parse and paint profile signpost events :^)
Signposts generated by perf_event(PERF_EVENT_SIGNPOST) now show up in
profile timelines, and if you hover them you get a tooltip with the two
arguments passed with the event.
-rw-r--r-- | Userland/DevTools/Profiler/Profile.cpp | 17 | ||||
-rw-r--r-- | Userland/DevTools/Profiler/Profile.h | 9 | ||||
-rw-r--r-- | Userland/DevTools/Profiler/TimelineTrack.cpp | 54 | ||||
-rw-r--r-- | Userland/DevTools/Profiler/TimelineTrack.h | 6 |
4 files changed, 81 insertions, 5 deletions
diff --git a/Userland/DevTools/Profiler/Profile.cpp b/Userland/DevTools/Profiler/Profile.cpp index c78ad405af..640636a5c4 100644 --- a/Userland/DevTools/Profiler/Profile.cpp +++ b/Userland/DevTools/Profiler/Profile.cpp @@ -31,9 +31,10 @@ static void sort_profile_nodes(Vector<NonnullRefPtr<ProfileNode>>& nodes) child->sort_children(); } -Profile::Profile(Vector<Process> processes, Vector<Event> events) +Profile::Profile(Vector<Process> processes, Vector<Event> events, Vector<Event> signposts) : m_processes(move(processes)) , m_events(move(events)) + , m_signposts(move(signposts)) { m_first_timestamp = m_events.first().timestamp; m_last_timestamp = m_events.last().timestamp; @@ -216,6 +217,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St NonnullOwnPtrVector<Process> all_processes; HashMap<pid_t, Process*> current_processes; Vector<Event> events; + Vector<Event> signposts; EventSerialNumber next_serial; for (auto& perf_event_value : perf_events.values()) { @@ -231,11 +233,17 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St event.pid = perf_event.get("pid").to_i32(); event.tid = perf_event.get("tid").to_i32(); + bool is_signpost = false; + if (event.type == "malloc"sv) { event.ptr = perf_event.get("ptr").to_number<FlatPtr>(); event.size = perf_event.get("size").to_number<size_t>(); } else if (event.type == "free"sv) { event.ptr = perf_event.get("ptr").to_number<FlatPtr>(); + } else if (event.type == "signpost"sv) { + is_signpost = true; + event.arg1 = perf_event.get("arg1").to_number<FlatPtr>(); + event.arg2 = perf_event.get("arg2").to_number<FlatPtr>(); } else if (event.type == "mmap"sv) { event.ptr = perf_event.get("ptr").to_number<FlatPtr>(); event.size = perf_event.get("size").to_number<size_t>(); @@ -343,7 +351,10 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St FlatPtr innermost_frame_address = event.frames.at(1).address; event.in_kernel = maybe_kernel_base.has_value() && innermost_frame_address >= maybe_kernel_base.value(); - events.append(move(event)); + if (is_signpost) + signposts.append(move(event)); + else + events.append(move(event)); } if (events.is_empty()) @@ -360,7 +371,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St for (auto& it : all_processes) processes.append(move(it)); - return adopt_own(*new Profile(move(processes), move(events))); + return adopt_own(*new Profile(move(processes), move(events), move(signposts))); } void ProfileNode::sort_children() diff --git a/Userland/DevTools/Profiler/Profile.h b/Userland/DevTools/Profiler/Profile.h index 0007449852..e1c75a281e 100644 --- a/Userland/DevTools/Profiler/Profile.h +++ b/Userland/DevTools/Profiler/Profile.h @@ -176,10 +176,16 @@ public: int tid { 0 }; u32 lost_samples { 0 }; bool in_kernel { false }; + + // FIXME: Put event type-specific arguments in a union to save memory. + FlatPtr arg1 {}; + FlatPtr arg2 {}; + Vector<Frame> frames; }; const Vector<Event>& events() const { return m_events; } + Vector<Event> const& signposts() const { return m_signposts; } const Vector<size_t>& filtered_event_indices() const { return m_filtered_event_indices; } u64 length_in_ms() const { return m_last_timestamp - m_first_timestamp; } @@ -220,7 +226,7 @@ public: } private: - Profile(Vector<Process>, Vector<Event>); + Profile(Vector<Process>, Vector<Event> events, Vector<Event> signposts); void rebuild_tree(); @@ -237,6 +243,7 @@ private: Vector<Process> m_processes; Vector<Event> m_events; + Vector<Event> m_signposts; bool m_has_timestamp_filter_range { false }; u64 m_timestamp_filter_range_start { 0 }; diff --git a/Userland/DevTools/Profiler/TimelineTrack.cpp b/Userland/DevTools/Profiler/TimelineTrack.cpp index a9682ccec7..3d75d8ce7b 100644 --- a/Userland/DevTools/Profiler/TimelineTrack.cpp +++ b/Userland/DevTools/Profiler/TimelineTrack.cpp @@ -7,6 +7,7 @@ #include "TimelineTrack.h" #include "Profile.h" #include "TimelineView.h" +#include <LibGUI/Application.h> #include <LibGUI/Painter.h> #include <LibGfx/Palette.h> @@ -61,7 +62,7 @@ void TimelineTrack::paint_event(GUI::PaintEvent& event) return min(end_of_trace, max(timestamp, start_of_trace)); }; - float column_width = (float)frame_inner_rect().width() / (float)m_profile.length_in_ms(); + float column_width = this->column_width(); size_t columns = frame_inner_rect().width() / column_width; recompute_histograms_if_needed({ start_of_trace, end_of_trace, columns }); @@ -97,6 +98,52 @@ void TimelineTrack::paint_event(GUI::PaintEvent& event) int select_hover_x = (int)((float)(normalized_hover_time - start_of_trace) * 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)); painter.fill_rect({ select_hover_x, frame_thickness(), 1, height() - frame_thickness() * 2 }, Color::NamedColor::Black); + + for_each_signpost([&](auto& signpost) { + int x = (int)((float)(signpost.timestamp - start_of_trace) * column_width); + int y1 = frame_thickness(); + int y2 = height() - frame_thickness() * 2; + + painter.draw_line({ x, y1 }, { x, y2 }, Color::Magenta); + + return IterationDecision::Continue; + }); +} + +template<typename Callback> +void TimelineTrack::for_each_signpost(Callback callback) +{ + for (auto& signpost : m_profile.signposts()) { + if (signpost.pid != m_process.pid) + continue; + + if (!m_process.valid_at(signpost.serial)) + continue; + + if (callback(signpost) == IterationDecision::Break) + break; + } +} + +void TimelineTrack::mousemove_event(GUI::MouseEvent& event) +{ + auto column_width = this->column_width(); + bool hovering_a_signpost = false; + + for_each_signpost([&](auto& signpost) { + int x = (int)((float)(signpost.timestamp - m_profile.first_timestamp()) * column_width); + constexpr int hoverable_padding = 2; + Gfx::IntRect hoverable_rect { x - hoverable_padding, frame_thickness(), hoverable_padding * 2, height() - frame_thickness() * 2 }; + if (hoverable_rect.contains_horizontally(event.x())) { + GUI::Application::the()->show_tooltip_immediately(String::formatted("Signpost {}, {}", signpost.arg1, signpost.arg2), this); + hovering_a_signpost = true; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + + if (!hovering_a_signpost) + GUI::Application::the()->hide_tooltip(); } void TimelineTrack::recompute_histograms_if_needed(HistogramInputs const& inputs) @@ -129,4 +176,9 @@ void TimelineTrack::recompute_histograms_if_needed(HistogramInputs const& inputs } } +float TimelineTrack::column_width() const +{ + return (float)frame_inner_rect().width() / (float)m_profile.length_in_ms(); +} + } diff --git a/Userland/DevTools/Profiler/TimelineTrack.h b/Userland/DevTools/Profiler/TimelineTrack.h index 5a9ded5cc7..fb74dde0b6 100644 --- a/Userland/DevTools/Profiler/TimelineTrack.h +++ b/Userland/DevTools/Profiler/TimelineTrack.h @@ -24,8 +24,14 @@ public: void set_scale(float); private: + float column_width() const; + + template<typename Callback> + void for_each_signpost(Callback); + virtual void event(Core::Event&) override; virtual void paint_event(GUI::PaintEvent&) override; + virtual void mousemove_event(GUI::MouseEvent&) override; struct HistogramInputs { bool operator==(HistogramInputs const&) const = default; |