Symbolication.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Checked.h>
  7. #include <AK/JsonArray.h>
  8. #include <AK/JsonObject.h>
  9. #include <AK/JsonValue.h>
  10. #include <AK/MappedFile.h>
  11. #include <LibCore/File.h>
  12. #include <LibDebug/DebugInfo.h>
  13. #include <LibSymbolication/Symbolication.h>
  14. namespace Symbolication {
  15. struct CachedELF {
  16. NonnullRefPtr<MappedFile> mapped_file;
  17. NonnullOwnPtr<Debug::DebugInfo> debug_info;
  18. };
  19. static HashMap<String, OwnPtr<CachedELF>> s_cache;
  20. enum class KernelBaseState {
  21. Uninitialized,
  22. Valid,
  23. Invalid,
  24. };
  25. static FlatPtr s_kernel_base;
  26. static KernelBaseState s_kernel_base_state = KernelBaseState::Uninitialized;
  27. Optional<FlatPtr> kernel_base()
  28. {
  29. if (s_kernel_base_state == KernelBaseState::Uninitialized) {
  30. auto file = Core::File::open("/proc/kernel_base", Core::OpenMode::ReadOnly);
  31. if (file.is_error()) {
  32. s_kernel_base_state = KernelBaseState::Invalid;
  33. return {};
  34. }
  35. auto kernel_base_str = String { file.value()->read_all(), NoChomp };
  36. #if ARCH(I386)
  37. using AddressType = u32;
  38. #else
  39. using AddressType = u64;
  40. #endif
  41. auto maybe_kernel_base = kernel_base_str.to_uint<AddressType>();
  42. if (!maybe_kernel_base.has_value()) {
  43. s_kernel_base_state = KernelBaseState::Invalid;
  44. return {};
  45. }
  46. s_kernel_base = maybe_kernel_base.value();
  47. s_kernel_base_state = KernelBaseState::Valid;
  48. }
  49. if (s_kernel_base_state == KernelBaseState::Invalid)
  50. return {};
  51. return s_kernel_base;
  52. }
  53. Optional<Symbol> symbolicate(String const& path, FlatPtr address)
  54. {
  55. if (!s_cache.contains(path)) {
  56. auto mapped_file = MappedFile::map(path);
  57. if (mapped_file.is_error()) {
  58. dbgln("Failed to map {}: {}", path, mapped_file.error().string());
  59. s_cache.set(path, {});
  60. return {};
  61. }
  62. auto elf = make<ELF::Image>(mapped_file.value()->bytes());
  63. if (!elf->is_valid()) {
  64. dbgln("ELF not valid: {}", path);
  65. s_cache.set(path, {});
  66. {};
  67. }
  68. auto cached_elf = make<CachedELF>(mapped_file.release_value(), make<Debug::DebugInfo>(move(elf)));
  69. s_cache.set(path, move(cached_elf));
  70. }
  71. auto it = s_cache.find(path);
  72. VERIFY(it != s_cache.end());
  73. auto& cached_elf = it->value;
  74. if (!cached_elf)
  75. return {};
  76. u32 offset = 0;
  77. auto symbol = cached_elf->debug_info->elf().symbolicate(address, &offset);
  78. auto source_position_with_inlines = cached_elf->debug_info->get_source_position_with_inlines(address);
  79. Vector<Debug::DebugInfo::SourcePosition> positions;
  80. for (auto& position : source_position_with_inlines.inline_chain) {
  81. if (!positions.contains_slow(position))
  82. positions.append(position);
  83. }
  84. if (source_position_with_inlines.source_position.has_value() && !positions.contains_slow(source_position_with_inlines.source_position.value())) {
  85. positions.insert(0, source_position_with_inlines.source_position.value());
  86. }
  87. return Symbol {
  88. .address = address,
  89. .name = move(symbol),
  90. .offset = offset,
  91. .source_positions = move(positions),
  92. };
  93. }
  94. Vector<Symbol> symbolicate_thread(pid_t pid, pid_t tid)
  95. {
  96. struct RegionWithSymbols {
  97. FlatPtr base { 0 };
  98. size_t size { 0 };
  99. String path;
  100. bool is_relative { true };
  101. };
  102. Vector<FlatPtr> stack;
  103. Vector<RegionWithSymbols> regions;
  104. if (auto maybe_kernel_base = kernel_base(); maybe_kernel_base.has_value()) {
  105. regions.append(RegionWithSymbols {
  106. .base = maybe_kernel_base.value(),
  107. .size = 0x3fffffff,
  108. .path = "/boot/Kernel.debug",
  109. .is_relative = false,
  110. });
  111. }
  112. {
  113. auto stack_path = String::formatted("/proc/{}/stacks/{}", pid, tid);
  114. auto file_or_error = Core::File::open(stack_path, Core::OpenMode::ReadOnly);
  115. if (file_or_error.is_error()) {
  116. warnln("Could not open {}: {}", stack_path, file_or_error.error());
  117. return {};
  118. }
  119. auto json = JsonValue::from_string(file_or_error.value()->read_all());
  120. if (!json.has_value() || !json.value().is_array()) {
  121. warnln("Invalid contents in {}", stack_path);
  122. return {};
  123. }
  124. stack.ensure_capacity(json.value().as_array().size());
  125. for (auto& value : json.value().as_array().values()) {
  126. stack.append(value.to_addr());
  127. }
  128. }
  129. {
  130. auto vm_path = String::formatted("/proc/{}/vm", pid);
  131. auto file_or_error = Core::File::open(vm_path, Core::OpenMode::ReadOnly);
  132. if (file_or_error.is_error()) {
  133. warnln("Could not open {}: {}", vm_path, file_or_error.error());
  134. return {};
  135. }
  136. auto json = JsonValue::from_string(file_or_error.value()->read_all());
  137. if (!json.has_value() || !json.value().is_array()) {
  138. warnln("Invalid contents in {}", vm_path);
  139. return {};
  140. }
  141. for (auto& region_value : json.value().as_array().values()) {
  142. auto& region = region_value.as_object();
  143. auto name = region.get("name").to_string();
  144. auto address = region.get("address").to_addr();
  145. auto size = region.get("size").to_addr();
  146. String path;
  147. if (name == "/usr/lib/Loader.so") {
  148. path = name;
  149. } else if (name.ends_with(": .text")) {
  150. auto parts = name.split_view(':');
  151. path = parts[0];
  152. if (!path.starts_with('/'))
  153. path = String::formatted("/usr/lib/{}", path);
  154. } else {
  155. continue;
  156. }
  157. RegionWithSymbols r;
  158. r.base = address;
  159. r.size = size;
  160. r.path = path;
  161. regions.append(move(r));
  162. }
  163. }
  164. Vector<Symbol> symbols;
  165. bool first_frame = true;
  166. for (auto address : stack) {
  167. const RegionWithSymbols* found_region = nullptr;
  168. for (auto& region : regions) {
  169. FlatPtr region_end;
  170. if (Checked<FlatPtr>::addition_would_overflow(region.base, region.size))
  171. region_end = NumericLimits<FlatPtr>::max();
  172. else
  173. region_end = region.base + region.size;
  174. if (address >= region.base && address < region_end) {
  175. found_region = &region;
  176. break;
  177. }
  178. }
  179. if (!found_region) {
  180. outln("{:p} ??", address);
  181. continue;
  182. }
  183. FlatPtr adjusted_address;
  184. if (found_region->is_relative)
  185. adjusted_address = address - found_region->base;
  186. else
  187. adjusted_address = address;
  188. // We're subtracting 1 from the address because this is the return address,
  189. // i.e. it is one instruction past the call instruction.
  190. // However, because the first frame represents the current
  191. // instruction pointer rather than the return address we don't
  192. // subtract 1 for that.
  193. auto result = symbolicate(found_region->path, adjusted_address - (first_frame ? 0 : 1));
  194. first_frame = false;
  195. if (!result.has_value()) {
  196. symbols.append(Symbol {
  197. .address = address,
  198. .source_positions = {},
  199. });
  200. continue;
  201. }
  202. symbols.append(result.value());
  203. }
  204. return symbols;
  205. }
  206. }