mirror of
https://github.com/RGBCube/serenity
synced 2025-06-02 10:28:14 +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();
|
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_processes(move(processes))
|
||||||
, m_events(move(events))
|
, m_events(move(events))
|
||||||
|
, m_signposts(move(signposts))
|
||||||
{
|
{
|
||||||
m_first_timestamp = m_events.first().timestamp;
|
m_first_timestamp = m_events.first().timestamp;
|
||||||
m_last_timestamp = m_events.last().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;
|
NonnullOwnPtrVector<Process> all_processes;
|
||||||
HashMap<pid_t, Process*> current_processes;
|
HashMap<pid_t, Process*> current_processes;
|
||||||
Vector<Event> events;
|
Vector<Event> events;
|
||||||
|
Vector<Event> signposts;
|
||||||
EventSerialNumber next_serial;
|
EventSerialNumber next_serial;
|
||||||
|
|
||||||
for (auto& perf_event_value : perf_events.values()) {
|
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.pid = perf_event.get("pid").to_i32();
|
||||||
event.tid = perf_event.get("tid").to_i32();
|
event.tid = perf_event.get("tid").to_i32();
|
||||||
|
|
||||||
|
bool is_signpost = false;
|
||||||
|
|
||||||
if (event.type == "malloc"sv) {
|
if (event.type == "malloc"sv) {
|
||||||
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
||||||
event.size = perf_event.get("size").to_number<size_t>();
|
event.size = perf_event.get("size").to_number<size_t>();
|
||||||
} else if (event.type == "free"sv) {
|
} else if (event.type == "free"sv) {
|
||||||
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
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) {
|
} else if (event.type == "mmap"sv) {
|
||||||
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
||||||
event.size = perf_event.get("size").to_number<size_t>();
|
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;
|
FlatPtr innermost_frame_address = event.frames.at(1).address;
|
||||||
event.in_kernel = maybe_kernel_base.has_value() && innermost_frame_address >= maybe_kernel_base.value();
|
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())
|
if (events.is_empty())
|
||||||
|
@ -360,7 +371,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
|
||||||
for (auto& it : all_processes)
|
for (auto& it : all_processes)
|
||||||
processes.append(move(it));
|
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()
|
void ProfileNode::sort_children()
|
||||||
|
|
|
@ -176,10 +176,16 @@ public:
|
||||||
int tid { 0 };
|
int tid { 0 };
|
||||||
u32 lost_samples { 0 };
|
u32 lost_samples { 0 };
|
||||||
bool in_kernel { false };
|
bool in_kernel { false };
|
||||||
|
|
||||||
|
// FIXME: Put event type-specific arguments in a union to save memory.
|
||||||
|
FlatPtr arg1 {};
|
||||||
|
FlatPtr arg2 {};
|
||||||
|
|
||||||
Vector<Frame> frames;
|
Vector<Frame> frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Vector<Event>& events() const { return m_events; }
|
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; }
|
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; }
|
u64 length_in_ms() const { return m_last_timestamp - m_first_timestamp; }
|
||||||
|
@ -220,7 +226,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Profile(Vector<Process>, Vector<Event>);
|
Profile(Vector<Process>, Vector<Event> events, Vector<Event> signposts);
|
||||||
|
|
||||||
void rebuild_tree();
|
void rebuild_tree();
|
||||||
|
|
||||||
|
@ -237,6 +243,7 @@ private:
|
||||||
|
|
||||||
Vector<Process> m_processes;
|
Vector<Process> m_processes;
|
||||||
Vector<Event> m_events;
|
Vector<Event> m_events;
|
||||||
|
Vector<Event> m_signposts;
|
||||||
|
|
||||||
bool m_has_timestamp_filter_range { false };
|
bool m_has_timestamp_filter_range { false };
|
||||||
u64 m_timestamp_filter_range_start { 0 };
|
u64 m_timestamp_filter_range_start { 0 };
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "TimelineTrack.h"
|
#include "TimelineTrack.h"
|
||||||
#include "Profile.h"
|
#include "Profile.h"
|
||||||
#include "TimelineView.h"
|
#include "TimelineView.h"
|
||||||
|
#include <LibGUI/Application.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
#include <LibGfx/Palette.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));
|
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;
|
size_t columns = frame_inner_rect().width() / column_width;
|
||||||
|
|
||||||
recompute_histograms_if_needed({ start_of_trace, end_of_trace, columns });
|
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);
|
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_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);
|
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)
|
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);
|
void set_scale(float);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
float column_width() const;
|
||||||
|
|
||||||
|
template<typename Callback>
|
||||||
|
void for_each_signpost(Callback);
|
||||||
|
|
||||||
virtual void event(Core::Event&) override;
|
virtual void event(Core::Event&) override;
|
||||||
virtual void paint_event(GUI::PaintEvent&) override;
|
virtual void paint_event(GUI::PaintEvent&) override;
|
||||||
|
virtual void mousemove_event(GUI::MouseEvent&) override;
|
||||||
|
|
||||||
struct HistogramInputs {
|
struct HistogramInputs {
|
||||||
bool operator==(HistogramInputs const&) const = default;
|
bool operator==(HistogramInputs const&) const = default;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue