SourceModel.cpp 5.9 KB

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