SourceModel.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "SourceModel.h"
  7. #include "Gradient.h"
  8. #include "Profile.h"
  9. #include <LibCore/File.h>
  10. #include <LibDebug/DebugInfo.h>
  11. #include <LibGfx/Font/FontDatabase.h>
  12. #include <LibSymbolication/Symbolication.h>
  13. #include <stdio.h>
  14. namespace Profiler {
  15. class SourceFile final {
  16. public:
  17. struct Line {
  18. String content;
  19. size_t num_samples { 0 };
  20. };
  21. static constexpr StringView source_root_path = "/usr/src/serenity/"sv;
  22. public:
  23. SourceFile(StringView filename)
  24. {
  25. String source_file_name = filename.replace("../../", source_root_path, ReplaceMode::FirstOnly);
  26. auto maybe_file = Core::File::open(source_file_name, Core::OpenMode::ReadOnly);
  27. if (maybe_file.is_error()) {
  28. dbgln("Could not map source file \"{}\". Tried {}. {} (errno={})", filename, source_file_name, maybe_file.error().string_literal(), maybe_file.error().code());
  29. return;
  30. }
  31. auto file = maybe_file.value();
  32. while (!file->eof())
  33. m_lines.append({ file->read_line(1024), 0 });
  34. }
  35. void try_add_samples(size_t line, size_t samples)
  36. {
  37. if (line < 1 || line - 1 >= m_lines.size())
  38. return;
  39. m_lines[line - 1].num_samples += samples;
  40. }
  41. Vector<Line> const& lines() const { return m_lines; }
  42. private:
  43. Vector<Line> m_lines;
  44. };
  45. SourceModel::SourceModel(Profile& profile, ProfileNode& node)
  46. : m_profile(profile)
  47. , m_node(node)
  48. {
  49. FlatPtr base_address = 0;
  50. Debug::DebugInfo const* debug_info;
  51. if (auto maybe_kernel_base = Symbolication::kernel_base(); maybe_kernel_base.has_value() && m_node.address() >= *maybe_kernel_base) {
  52. if (!g_kernel_debuginfo_object.has_value())
  53. return;
  54. base_address = maybe_kernel_base.release_value();
  55. if (g_kernel_debug_info == nullptr)
  56. g_kernel_debug_info = make<Debug::DebugInfo>(g_kernel_debuginfo_object->elf, String::empty(), base_address);
  57. debug_info = g_kernel_debug_info.ptr();
  58. } else {
  59. auto const& process = node.process();
  60. auto const* library_data = process.library_metadata.library_containing(node.address());
  61. if (!library_data) {
  62. dbgln("no library data for address {:p}", node.address());
  63. return;
  64. }
  65. base_address = library_data->base;
  66. debug_info = &library_data->load_debug_info(base_address);
  67. }
  68. VERIFY(debug_info != nullptr);
  69. // Try to read all source files contributing to the selected function and aggregate the samples by line.
  70. HashMap<String, SourceFile> source_files;
  71. for (auto const& pair : node.events_per_address()) {
  72. auto position = debug_info->get_source_position(pair.key - base_address);
  73. if (position.has_value()) {
  74. auto it = source_files.find(position.value().file_path);
  75. if (it == source_files.end()) {
  76. source_files.set(position.value().file_path, SourceFile(position.value().file_path));
  77. it = source_files.find(position.value().file_path);
  78. }
  79. it->value.try_add_samples(position.value().line_number, pair.value);
  80. }
  81. }
  82. // Process source file map and turn content into view model
  83. for (auto const& file_iterator : source_files) {
  84. u32 line_number = 0;
  85. for (auto const& line_iterator : file_iterator.value.lines()) {
  86. line_number++;
  87. m_source_lines.append({
  88. (u32)line_iterator.num_samples,
  89. line_iterator.num_samples * 100.0f / node.event_count(),
  90. file_iterator.key,
  91. line_number,
  92. line_iterator.content,
  93. });
  94. }
  95. }
  96. }
  97. int SourceModel::row_count(GUI::ModelIndex const&) const
  98. {
  99. return m_source_lines.size();
  100. }
  101. String SourceModel::column_name(int column) const
  102. {
  103. switch (column) {
  104. case Column::SampleCount:
  105. return m_profile.show_percentages() ? "% Samples" : "# Samples";
  106. case Column::SourceCode:
  107. return "Source Code";
  108. case Column::Location:
  109. return "Location";
  110. case Column::LineNumber:
  111. return "Line";
  112. default:
  113. VERIFY_NOT_REACHED();
  114. return {};
  115. }
  116. }
  117. struct ColorPair {
  118. Color background;
  119. Color foreground;
  120. };
  121. static Optional<ColorPair> color_pair_for(SourceLineData const& line)
  122. {
  123. if (line.percent == 0)
  124. return {};
  125. Color background = color_for_percent(line.percent);
  126. Color foreground;
  127. if (line.percent > 50)
  128. foreground = Color::White;
  129. else
  130. foreground = Color::Black;
  131. return ColorPair { background, foreground };
  132. }
  133. GUI::Variant SourceModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const
  134. {
  135. auto const& line = m_source_lines[index.row()];
  136. if (role == GUI::ModelRole::BackgroundColor) {
  137. auto colors = color_pair_for(line);
  138. if (!colors.has_value())
  139. return {};
  140. return colors.value().background;
  141. }
  142. if (role == GUI::ModelRole::ForegroundColor) {
  143. auto colors = color_pair_for(line);
  144. if (!colors.has_value())
  145. return {};
  146. return colors.value().foreground;
  147. }
  148. if (role == GUI::ModelRole::Font) {
  149. if (index.column() == Column::SourceCode)
  150. return Gfx::FontDatabase::default_fixed_width_font();
  151. return {};
  152. }
  153. if (role == GUI::ModelRole::Display) {
  154. if (index.column() == Column::SampleCount) {
  155. if (m_profile.show_percentages())
  156. return ((float)line.event_count / (float)m_node.event_count()) * 100.0f;
  157. return line.event_count;
  158. }
  159. if (index.column() == Column::Location)
  160. return line.location;
  161. if (index.column() == Column::LineNumber)
  162. return line.line_number;
  163. if (index.column() == Column::SourceCode)
  164. return line.source_code;
  165. return {};
  166. }
  167. return {};
  168. }
  169. }