Serenity Operating System
at master 187 lines 7.0 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "TimelineTrack.h" 9#include "Profile.h" 10#include "TimelineView.h" 11#include <LibGUI/Application.h> 12#include <LibGUI/Painter.h> 13#include <LibGfx/Palette.h> 14 15namespace Profiler { 16 17TimelineTrack::TimelineTrack(TimelineView const& view, Profile const& profile, Process const& process) 18 : m_view(view) 19 , m_profile(profile) 20 , m_process(process) 21{ 22 set_fill_with_background_color(true); 23 set_background_role(Gfx::ColorRole::Base); 24 set_fixed_height(40); 25 set_scale(view.scale()); 26 set_frame_thickness(1); 27} 28 29void TimelineTrack::set_scale(float scale) 30{ 31 set_fixed_width(m_profile.length_in_ms() / scale); 32} 33 34void TimelineTrack::event(Core::Event& event) 35{ 36 switch (event.type()) { 37 case GUI::Event::MouseUp: 38 case GUI::Event::MouseDown: 39 case GUI::Event::MouseMove: 40 event.ignore(); 41 break; 42 default: 43 break; 44 } 45 GUI::Frame::event(event); 46} 47 48void TimelineTrack::paint_event(GUI::PaintEvent& event) 49{ 50 GUI::Frame::paint_event(event); 51 52 GUI::Painter painter(*this); 53 painter.add_clip_rect(event.rect()); 54 55 u64 const start_of_trace = m_profile.first_timestamp(); 56 u64 const end_of_trace = start_of_trace + m_profile.length_in_ms(); 57 58 auto const clamp_timestamp = [start_of_trace, end_of_trace](u64 timestamp) -> u64 { 59 return min(end_of_trace, max(timestamp, start_of_trace)); 60 }; 61 62 recompute_histograms_if_needed({ start_of_trace, end_of_trace, (size_t)m_profile.length_in_ms() }); 63 64 float column_width = this->column_width(); 65 float frame_height = (float)frame_inner_rect().height() / (float)m_max_value; 66 67 for_each_signpost([&](auto& signpost) { 68 int x = (int)((float)(signpost.timestamp - start_of_trace) * column_width); 69 int y1 = frame_thickness(); 70 int y2 = height() - frame_thickness() * 2; 71 72 painter.draw_line({ x, y1 }, { x, y2 }, Color::Magenta); 73 74 return IterationDecision::Continue; 75 }); 76 77 for (size_t bucket = 0; bucket < m_kernel_histogram->size(); bucket++) { 78 auto kernel_value = m_kernel_histogram->at(bucket); 79 auto user_value = m_user_histogram->at(bucket); 80 if (kernel_value + user_value == 0) 81 continue; 82 83 auto t = bucket; 84 85 int x = (int)((float)t * column_width); 86 int cw = max(1, (int)column_width); 87 88 int kernel_column_height = frame_inner_rect().height() - (int)((float)kernel_value * frame_height); 89 int user_column_height = frame_inner_rect().height() - (int)((float)(kernel_value + user_value) * frame_height); 90 91 constexpr auto kernel_color = Color::from_rgb(0xc25e5a); 92 constexpr auto user_color = Color::from_rgb(0x5a65c2); 93 painter.fill_rect({ x, frame_thickness() + user_column_height, cw, height() - frame_thickness() * 2 }, user_color); 94 painter.fill_rect({ x, frame_thickness() + kernel_column_height, cw, height() - frame_thickness() * 2 }, kernel_color); 95 } 96 97 u64 normalized_start_time = clamp_timestamp(min(m_view.select_start_time(), m_view.select_end_time())); 98 u64 normalized_end_time = clamp_timestamp(max(m_view.select_start_time(), m_view.select_end_time())); 99 u64 normalized_hover_time = clamp_timestamp(m_view.hover_time()); 100 101 int select_start_x = (int)((float)(normalized_start_time - start_of_trace) * column_width); 102 int select_end_x = (int)((float)(normalized_end_time - start_of_trace) * column_width); 103 int select_hover_x = (int)((float)(normalized_hover_time - start_of_trace) * column_width); 104 painter.fill_rect({ select_start_x, frame_thickness(), select_end_x - select_start_x, height() - frame_thickness() * 2 }, Color(0, 0, 0, 60)); 105 painter.fill_rect({ select_hover_x, frame_thickness(), 1, height() - frame_thickness() * 2 }, Color::NamedColor::Black); 106} 107 108template<typename Callback> 109void TimelineTrack::for_each_signpost(Callback callback) 110{ 111 m_profile.for_each_signpost([&](auto& signpost) { 112 if (signpost.pid != m_process.pid) 113 return IterationDecision::Continue; 114 115 if (!m_process.valid_at(signpost.serial)) 116 return IterationDecision::Continue; 117 118 return callback(signpost); 119 }); 120} 121 122void TimelineTrack::mousemove_event(GUI::MouseEvent& event) 123{ 124 auto column_width = this->column_width(); 125 bool hovering_a_signpost = false; 126 127 for_each_signpost([&](auto& signpost) { 128 int x = (int)((float)(signpost.timestamp - m_profile.first_timestamp()) * column_width); 129 constexpr int hoverable_padding = 2; 130 Gfx::IntRect hoverable_rect { x - hoverable_padding, frame_thickness(), hoverable_padding * 2, height() - frame_thickness() * 2 }; 131 if (hoverable_rect.contains_horizontally(event.x())) { 132 auto const& data = signpost.data.template get<Profile::Event::SignpostData>(); 133 GUI::Application::the()->show_tooltip_immediately(DeprecatedString::formatted("{}, {}", data.string, data.arg), this); 134 hovering_a_signpost = true; 135 return IterationDecision::Break; 136 } 137 return IterationDecision::Continue; 138 }); 139 140 if (!hovering_a_signpost) 141 GUI::Application::the()->hide_tooltip(); 142} 143 144void TimelineTrack::recompute_histograms_if_needed(HistogramInputs const& inputs) 145{ 146 if (m_cached_histogram_inputs == inputs && m_kernel_histogram.has_value()) 147 return; 148 149 auto const clamp_timestamp = [&inputs](u64 timestamp) -> u64 { 150 return min(inputs.end, max(timestamp, inputs.start)); 151 }; 152 153 m_kernel_histogram = Histogram { inputs.start, inputs.end, inputs.columns }; 154 m_user_histogram = Histogram { inputs.start, inputs.end, inputs.columns }; 155 156 for (auto const& event : m_profile.events()) { 157 if (event.pid != m_process.pid) 158 continue; 159 160 if (!m_process.valid_at(event.serial)) 161 continue; 162 163 auto& histogram = event.in_kernel ? *m_kernel_histogram : *m_user_histogram; 164 histogram.insert(clamp_timestamp(event.timestamp), 1 + event.lost_samples); 165 } 166 167 auto shorter_histogram_size = min(m_kernel_histogram->size(), m_user_histogram->size()); 168 for (size_t bucket = 0; bucket < shorter_histogram_size; ++bucket) { 169 auto value = m_kernel_histogram->at(bucket) + m_user_histogram->at(bucket); 170 if (value > m_max_value) 171 m_max_value = value; 172 } 173 174 auto& longer_histogram = m_kernel_histogram->size() > m_user_histogram->size() ? *m_kernel_histogram : *m_user_histogram; 175 for (size_t bucket = shorter_histogram_size; bucket < longer_histogram.size(); ++bucket) { 176 auto value = longer_histogram.at(bucket); 177 if (value > m_max_value) 178 m_max_value = value; 179 } 180} 181 182float TimelineTrack::column_width() const 183{ 184 return (float)frame_inner_rect().width() / (float)m_profile.length_in_ms(); 185} 186 187}