Serenity Operating System
at master 240 lines 8.5 kB view raw
1/* 2 * Copyright (c) 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 "DisassemblyModel.h" 10#include "Gradient.h" 11#include "PercentageFormatting.h" 12#include "Profile.h" 13#include <LibCore/MappedFile.h> 14#include <LibDebug/DebugInfo.h> 15#include <LibELF/Image.h> 16#include <LibSymbolication/Symbolication.h> 17#include <LibX86/Disassembler.h> 18#include <LibX86/ELFSymbolProvider.h> 19#include <stdio.h> 20 21namespace Profiler { 22 23static Optional<MappedObject> s_kernel_binary; 24 25static ELF::Image* try_load_kernel_binary() 26{ 27 if (s_kernel_binary.has_value()) 28 return &s_kernel_binary->elf; 29 auto kernel_binary_or_error = Core::MappedFile::map("/boot/Kernel"sv); 30 if (!kernel_binary_or_error.is_error()) { 31 auto kernel_binary = kernel_binary_or_error.release_value(); 32 s_kernel_binary = { { kernel_binary, ELF::Image(kernel_binary->bytes()) } }; 33 return &s_kernel_binary->elf; 34 } 35 return nullptr; 36} 37 38DisassemblyModel::DisassemblyModel(Profile& profile, ProfileNode& node) 39 : m_profile(profile) 40 , m_node(node) 41{ 42 FlatPtr base_address = 0; 43 Debug::DebugInfo const* debug_info; 44 ELF::Image const* elf; 45 if (auto maybe_kernel_base = Symbolication::kernel_base(); maybe_kernel_base.has_value() && m_node.address() >= *maybe_kernel_base) { 46 if (!g_kernel_debuginfo_object.has_value()) 47 return; 48 base_address = maybe_kernel_base.release_value(); 49 elf = try_load_kernel_binary(); 50 if (elf == nullptr) 51 return; 52 if (g_kernel_debug_info == nullptr) 53 g_kernel_debug_info = make<Debug::DebugInfo>(g_kernel_debuginfo_object->elf, DeprecatedString::empty(), base_address); 54 debug_info = g_kernel_debug_info.ptr(); 55 } else { 56 auto const& process = node.process(); 57 auto const* library_data = process.library_metadata.library_containing(node.address()); 58 if (!library_data) { 59 dbgln("no library data for address {:p}", node.address()); 60 return; 61 } 62 base_address = library_data->base; 63 elf = &library_data->object->elf; 64 debug_info = &library_data->load_debug_info(base_address); 65 } 66 67 VERIFY(elf != nullptr); 68 VERIFY(debug_info != nullptr); 69 70 FlatPtr function_address = node.address() - base_address; 71 auto is_function_address = false; 72 auto function = debug_info->get_containing_function(function_address); 73 if (function.has_value()) { 74 if (function_address == function->address_low) 75 is_function_address = true; 76 function_address = function->address_low; 77 } else { 78 dbgln("DisassemblyModel: Function containing {:p} ({}) not found", node.address() - base_address, node.symbol()); 79 } 80 81 auto symbol = elf->find_symbol(function_address); 82 if (!symbol.has_value()) { 83 dbgln("DisassemblyModel: symbol not found"); 84 return; 85 } 86 if (!symbol.value().raw_data().length()) { 87 dbgln("DisassemblyModel: Found symbol without code"); 88 return; 89 } 90 VERIFY(symbol.has_value()); 91 92 auto symbol_offset_from_function_start = node.address() - base_address - symbol->value(); 93 auto view = symbol.value().raw_data().substring_view(symbol_offset_from_function_start); 94 95 X86::ELFSymbolProvider symbol_provider(*elf, base_address); 96 X86::SimpleInstructionStream stream((u8 const*)view.characters_without_null_termination(), view.length()); 97 X86::Disassembler disassembler(stream); 98 99 size_t offset_into_symbol = 0; 100 FlatPtr last_instruction_offset = 0; 101 if (!is_function_address) { 102 FlatPtr last_instruction_address = 0; 103 for (auto const& event : node.events_per_address()) 104 last_instruction_address = max(event.key, last_instruction_address); 105 last_instruction_offset = last_instruction_address - node.address(); 106 } 107 for (;;) { 108 if (!is_function_address && offset_into_symbol > last_instruction_offset) 109 break; 110 111 auto insn = disassembler.next(); 112 if (!insn.has_value()) 113 break; 114 FlatPtr address_in_profiled_program = node.address() + offset_into_symbol; 115 116 auto disassembly = insn.value().to_deprecated_string(address_in_profiled_program, &symbol_provider); 117 118 StringView instruction_bytes = view.substring_view(offset_into_symbol, insn.value().length()); 119 u32 samples_at_this_instruction = m_node.events_per_address().get(address_in_profiled_program).value_or(0); 120 float percent = ((float)samples_at_this_instruction / (float)m_node.event_count()) * 100.0f; 121 auto source_position = debug_info->get_source_position_with_inlines(address_in_profiled_program - base_address).release_value_but_fixme_should_propagate_errors(); 122 123 m_instructions.append({ insn.value(), disassembly, instruction_bytes, address_in_profiled_program, samples_at_this_instruction, percent, source_position }); 124 125 offset_into_symbol += insn.value().length(); 126 } 127} 128 129int DisassemblyModel::row_count(GUI::ModelIndex const&) const 130{ 131 return m_instructions.size(); 132} 133 134DeprecatedString DisassemblyModel::column_name(int column) const 135{ 136 switch (column) { 137 case Column::SampleCount: 138 return m_profile.show_percentages() ? "% Samples" : "# Samples"; 139 case Column::Address: 140 return "Address"; 141 case Column::InstructionBytes: 142 return "Insn Bytes"; 143 case Column::Disassembly: 144 return "Disassembly"; 145 case Column::SourceLocation: 146 return "Source Location"; 147 default: 148 VERIFY_NOT_REACHED(); 149 return {}; 150 } 151} 152 153struct ColorPair { 154 Color background; 155 Color foreground; 156}; 157 158static Optional<ColorPair> color_pair_for(InstructionData const& insn) 159{ 160 if (insn.percent == 0) 161 return {}; 162 163 Color background = color_for_percent(insn.percent); 164 Color foreground; 165 if (insn.percent > 50) 166 foreground = Color::White; 167 else 168 foreground = Color::Black; 169 return ColorPair { background, foreground }; 170} 171 172GUI::Variant DisassemblyModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const 173{ 174 auto const& insn = m_instructions[index.row()]; 175 176 if (role == GUI::ModelRole::BackgroundColor) { 177 auto colors = color_pair_for(insn); 178 if (!colors.has_value()) 179 return {}; 180 return colors.value().background; 181 } 182 183 if (role == GUI::ModelRole::ForegroundColor) { 184 auto colors = color_pair_for(insn); 185 if (!colors.has_value()) 186 return {}; 187 return colors.value().foreground; 188 } 189 190 if (role == GUI::ModelRole::TextAlignment) { 191 if (index.column() == Column::SampleCount) 192 return Gfx::TextAlignment::CenterRight; 193 } 194 195 if (role == GUI::ModelRole::Display) { 196 if (index.column() == Column::SampleCount) { 197 if (m_profile.show_percentages()) 198 return format_percentage(insn.event_count, m_node.event_count()); 199 return insn.event_count; 200 } 201 202 if (index.column() == Column::Address) 203 return DeprecatedString::formatted("{:p}", insn.address); 204 205 if (index.column() == Column::InstructionBytes) { 206 StringBuilder builder; 207 for (auto ch : insn.bytes) { 208 builder.appendff("{:02x} ", (u8)ch); 209 } 210 return builder.to_deprecated_string(); 211 } 212 213 if (index.column() == Column::Disassembly) 214 return insn.disassembly; 215 216 if (index.column() == Column::SourceLocation) { 217 StringBuilder builder; 218 auto first = true; 219 for (auto const& entry : insn.source_position_with_inlines.inline_chain) { 220 if (first) 221 first = false; 222 else 223 builder.append(" => "sv); 224 builder.appendff("{}:{}", entry.file_path, entry.line_number); 225 } 226 if (insn.source_position_with_inlines.source_position.has_value()) { 227 if (!first) 228 builder.append(" => "sv); 229 auto const& entry = insn.source_position_with_inlines.source_position.value(); 230 builder.appendff("{}:{}", entry.file_path, entry.line_number); 231 } 232 return builder.to_deprecated_string(); 233 } 234 235 return {}; 236 } 237 return {}; 238} 239 240}