Serenity Operating System
at master 172 lines 6.1 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl> 4 * Copyright (c) 2022, the SerenityOS developers. 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include "ProfileModel.h" 10#include "PercentageFormatting.h" 11#include "Profile.h" 12#include <LibGUI/FileIconProvider.h> 13#include <LibSymbolication/Symbolication.h> 14#include <stdio.h> 15 16namespace Profiler { 17 18ProfileModel::ProfileModel(Profile& profile) 19 : m_profile(profile) 20{ 21 m_user_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"sv).release_value_but_fixme_should_propagate_errors()); 22 m_kernel_frame_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object-red.png"sv).release_value_but_fixme_should_propagate_errors()); 23} 24 25GUI::ModelIndex ProfileModel::index(int row, int column, GUI::ModelIndex const& parent) const 26{ 27 if (!parent.is_valid()) { 28 if (m_profile.roots().is_empty()) 29 return {}; 30 return create_index(row, column, m_profile.roots().at(row).ptr()); 31 } 32 auto& remote_parent = *static_cast<ProfileNode*>(parent.internal_data()); 33 return create_index(row, column, remote_parent.children().at(row).ptr()); 34} 35 36GUI::ModelIndex ProfileModel::parent_index(GUI::ModelIndex const& index) const 37{ 38 if (!index.is_valid()) 39 return {}; 40 auto& node = *static_cast<ProfileNode*>(index.internal_data()); 41 if (!node.parent()) 42 return {}; 43 44 // NOTE: If the parent has no parent, it's a root, so we have to look among the roots. 45 if (!node.parent()->parent()) { 46 for (size_t row = 0; row < m_profile.roots().size(); ++row) { 47 if (m_profile.roots()[row].ptr() == node.parent()) { 48 return create_index(row, index.column(), node.parent()); 49 } 50 } 51 VERIFY_NOT_REACHED(); 52 return {}; 53 } 54 55 for (size_t row = 0; row < node.parent()->parent()->children().size(); ++row) { 56 if (node.parent()->parent()->children()[row].ptr() == node.parent()) 57 return create_index(row, index.column(), node.parent()); 58 } 59 60 VERIFY_NOT_REACHED(); 61 return {}; 62} 63 64int ProfileModel::row_count(GUI::ModelIndex const& index) const 65{ 66 if (!index.is_valid()) 67 return m_profile.roots().size(); 68 auto& node = *static_cast<ProfileNode*>(index.internal_data()); 69 return node.children().size(); 70} 71 72int ProfileModel::column_count(GUI::ModelIndex const&) const 73{ 74 return Column::__Count; 75} 76 77DeprecatedString ProfileModel::column_name(int column) const 78{ 79 switch (column) { 80 case Column::SampleCount: 81 return m_profile.show_percentages() ? "% Samples" : "# Samples"; 82 case Column::SelfCount: 83 return m_profile.show_percentages() ? "% Self" : "# Self"; 84 case Column::ObjectName: 85 return "Object"; 86 case Column::StackFrame: 87 return "Stack Frame"; 88 case Column::SymbolAddress: 89 return "Symbol Address"; 90 default: 91 VERIFY_NOT_REACHED(); 92 return {}; 93 } 94} 95 96GUI::Variant ProfileModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const 97{ 98 auto* node = static_cast<ProfileNode*>(index.internal_data()); 99 if (role == GUI::ModelRole::TextAlignment) { 100 if (index.column() == Column::SampleCount || index.column() == Column::SelfCount) 101 return Gfx::TextAlignment::CenterRight; 102 } 103 if (role == GUI::ModelRole::Icon) { 104 if (index.column() == Column::StackFrame) { 105 if (node->is_root()) { 106 return GUI::FileIconProvider::icon_for_executable(node->process().executable); 107 } 108 auto maybe_kernel_base = Symbolication::kernel_base(); 109 if (maybe_kernel_base.has_value() && node->address() >= maybe_kernel_base.value()) 110 return m_kernel_frame_icon; 111 return m_user_frame_icon; 112 } 113 return {}; 114 } 115 if (role == GUI::ModelRole::Display) { 116 if (index.column() == Column::SampleCount) { 117 if (m_profile.show_percentages()) 118 return format_percentage(node->event_count(), m_profile.filtered_event_indices().size()); 119 return node->event_count(); 120 } 121 if (index.column() == Column::SelfCount) { 122 if (m_profile.show_percentages()) 123 return format_percentage(node->self_count(), m_profile.filtered_event_indices().size()); 124 return node->self_count(); 125 } 126 if (index.column() == Column::ObjectName) 127 return node->object_name(); 128 if (index.column() == Column::StackFrame) { 129 if (node->is_root()) { 130 return DeprecatedString::formatted("{} ({})", node->process().basename, node->process().pid); 131 } 132 return node->symbol(); 133 } 134 if (index.column() == Column::SymbolAddress) { 135 if (node->is_root()) 136 return ""; 137 auto const* library = node->process().library_metadata.library_containing(node->address()); 138 if (!library) 139 return ""; 140 return DeprecatedString::formatted("{:p} (offset {:p})", node->address(), node->address() - library->base); 141 } 142 return {}; 143 } 144 return {}; 145} 146 147Vector<GUI::ModelIndex> ProfileModel::matches(StringView searching, unsigned flags, GUI::ModelIndex const& parent) 148{ 149 RemoveReference<decltype(m_profile.roots())>* nodes { nullptr }; 150 151 if (!parent.is_valid()) 152 nodes = &m_profile.roots(); 153 else 154 nodes = &static_cast<ProfileNode*>(parent.internal_data())->children(); 155 156 if (!nodes) 157 return {}; 158 159 Vector<GUI::ModelIndex> found_indices; 160 for (auto it = nodes->begin(); !it.is_end(); ++it) { 161 GUI::ModelIndex index = this->index(it.index(), StackFrame, parent); 162 if (!string_matches(data(index, GUI::ModelRole::Display).as_string(), searching, flags)) 163 continue; 164 165 found_indices.append(index); 166 if (flags & FirstMatchOnly) 167 break; 168 } 169 return found_indices; 170} 171 172}