mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:52:45 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									9ae8cd823c
								
							
						
					
					
						commit
						00b11d7577
					
				
					 4 changed files with 81 additions and 5 deletions
				
			
		|  | @ -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() | ||||
|  |  | |||
|  | @ -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 }; | ||||
|  |  | |||
|  | @ -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(); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling