InlineInfo.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. //===- InlineInfo.cpp -------------------------------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "llvm/DebugInfo/GSYM/FileEntry.h"
  9. #include "llvm/DebugInfo/GSYM/FileWriter.h"
  10. #include "llvm/DebugInfo/GSYM/GsymReader.h"
  11. #include "llvm/DebugInfo/GSYM/InlineInfo.h"
  12. #include "llvm/Support/DataExtractor.h"
  13. #include <algorithm>
  14. #include <inttypes.h>
  15. using namespace llvm;
  16. using namespace gsym;
  17. raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) {
  18. if (!II.isValid())
  19. return OS;
  20. bool First = true;
  21. for (auto Range : II.Ranges) {
  22. if (First)
  23. First = false;
  24. else
  25. OS << ' ';
  26. OS << Range;
  27. }
  28. OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile
  29. << ", CallLine = " << II.CallFile << '\n';
  30. for (const auto &Child : II.Children)
  31. OS << Child;
  32. return OS;
  33. }
  34. static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr,
  35. std::vector<const InlineInfo *> &InlineStack) {
  36. if (II.Ranges.contains(Addr)) {
  37. // If this is the top level that represents the concrete function,
  38. // there will be no name and we shoud clear the inline stack. Otherwise
  39. // we have found an inline call stack that we need to insert.
  40. if (II.Name != 0)
  41. InlineStack.insert(InlineStack.begin(), &II);
  42. for (const auto &Child : II.Children) {
  43. if (::getInlineStackHelper(Child, Addr, InlineStack))
  44. break;
  45. }
  46. return !InlineStack.empty();
  47. }
  48. return false;
  49. }
  50. std::optional<InlineInfo::InlineArray>
  51. InlineInfo::getInlineStack(uint64_t Addr) const {
  52. InlineArray Result;
  53. if (getInlineStackHelper(*this, Addr, Result))
  54. return Result;
  55. return std::nullopt;
  56. }
  57. /// Skip an InlineInfo object in the specified data at the specified offset.
  58. ///
  59. /// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo
  60. /// objects where the addres ranges isn't contained in the InlineInfo object
  61. /// or its children. This avoids allocations by not appending child InlineInfo
  62. /// objects to the InlineInfo::Children array.
  63. ///
  64. /// \param Data The binary stream to read the data from.
  65. ///
  66. /// \param Offset The byte offset within \a Data.
  67. ///
  68. /// \param SkippedRanges If true, address ranges have already been skipped.
  69. static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) {
  70. if (!SkippedRanges) {
  71. if (skipRanges(Data, Offset) == 0)
  72. return false;
  73. }
  74. bool HasChildren = Data.getU8(&Offset) != 0;
  75. Data.getU32(&Offset); // Skip Inline.Name.
  76. Data.getULEB128(&Offset); // Skip Inline.CallFile.
  77. Data.getULEB128(&Offset); // Skip Inline.CallLine.
  78. if (HasChildren) {
  79. while (skip(Data, Offset, false /* SkippedRanges */))
  80. /* Do nothing */;
  81. }
  82. // We skipped a valid InlineInfo.
  83. return true;
  84. }
  85. /// A Lookup helper functions.
  86. ///
  87. /// Used during the InlineInfo::lookup() call to quickly only parse an
  88. /// InlineInfo object if the address falls within this object. This avoids
  89. /// allocations by not appending child InlineInfo objects to the
  90. /// InlineInfo::Children array and also skips any InlineInfo objects that do
  91. /// not contain the address we are looking up.
  92. ///
  93. /// \param Data The binary stream to read the data from.
  94. ///
  95. /// \param Offset The byte offset within \a Data.
  96. ///
  97. /// \param BaseAddr The address that the relative address range offsets are
  98. /// relative to.
  99. static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,
  100. uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs,
  101. llvm::Error &Err) {
  102. InlineInfo Inline;
  103. decodeRanges(Inline.Ranges, Data, BaseAddr, Offset);
  104. if (Inline.Ranges.empty())
  105. return true;
  106. // Check if the address is contained within the inline information, and if
  107. // not, quickly skip this InlineInfo object and all its children.
  108. if (!Inline.Ranges.contains(Addr)) {
  109. skip(Data, Offset, true /* SkippedRanges */);
  110. return false;
  111. }
  112. // The address range is contained within this InlineInfo, add the source
  113. // location for this InlineInfo and any children that contain the address.
  114. bool HasChildren = Data.getU8(&Offset) != 0;
  115. Inline.Name = Data.getU32(&Offset);
  116. Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
  117. Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
  118. if (HasChildren) {
  119. // Child address ranges are encoded relative to the first address in the
  120. // parent InlineInfo object.
  121. const auto ChildBaseAddr = Inline.Ranges[0].start();
  122. bool Done = false;
  123. while (!Done)
  124. Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err);
  125. }
  126. std::optional<FileEntry> CallFile = GR.getFile(Inline.CallFile);
  127. if (!CallFile) {
  128. Err = createStringError(std::errc::invalid_argument,
  129. "failed to extract file[%" PRIu32 "]",
  130. Inline.CallFile);
  131. return false;
  132. }
  133. if (CallFile->Dir || CallFile->Base) {
  134. SourceLocation SrcLoc;
  135. SrcLoc.Name = SrcLocs.back().Name;
  136. SrcLoc.Offset = SrcLocs.back().Offset;
  137. SrcLoc.Dir = GR.getString(CallFile->Dir);
  138. SrcLoc.Base = GR.getString(CallFile->Base);
  139. SrcLoc.Line = Inline.CallLine;
  140. SrcLocs.back().Name = GR.getString(Inline.Name);
  141. SrcLocs.back().Offset = Addr - Inline.Ranges[0].start();
  142. SrcLocs.push_back(SrcLoc);
  143. }
  144. return true;
  145. }
  146. llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data,
  147. uint64_t BaseAddr, uint64_t Addr,
  148. SourceLocations &SrcLocs) {
  149. // Call our recursive helper function starting at offset zero.
  150. uint64_t Offset = 0;
  151. llvm::Error Err = Error::success();
  152. ::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err);
  153. return Err;
  154. }
  155. /// Decode an InlineInfo in Data at the specified offset.
  156. ///
  157. /// A local helper function to decode InlineInfo objects. This function is
  158. /// called recursively when parsing child InlineInfo objects.
  159. ///
  160. /// \param Data The data extractor to decode from.
  161. /// \param Offset The offset within \a Data to decode from.
  162. /// \param BaseAddr The base address to use when decoding address ranges.
  163. /// \returns An InlineInfo or an error describing the issue that was
  164. /// encountered during decoding.
  165. static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
  166. uint64_t BaseAddr) {
  167. InlineInfo Inline;
  168. if (!Data.isValidOffset(Offset))
  169. return createStringError(std::errc::io_error,
  170. "0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset);
  171. decodeRanges(Inline.Ranges, Data, BaseAddr, Offset);
  172. if (Inline.Ranges.empty())
  173. return Inline;
  174. if (!Data.isValidOffsetForDataOfSize(Offset, 1))
  175. return createStringError(std::errc::io_error,
  176. "0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children",
  177. Offset);
  178. bool HasChildren = Data.getU8(&Offset) != 0;
  179. if (!Data.isValidOffsetForDataOfSize(Offset, 4))
  180. return createStringError(std::errc::io_error,
  181. "0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset);
  182. Inline.Name = Data.getU32(&Offset);
  183. if (!Data.isValidOffset(Offset))
  184. return createStringError(std::errc::io_error,
  185. "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset);
  186. Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
  187. if (!Data.isValidOffset(Offset))
  188. return createStringError(std::errc::io_error,
  189. "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset);
  190. Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
  191. if (HasChildren) {
  192. // Child address ranges are encoded relative to the first address in the
  193. // parent InlineInfo object.
  194. const auto ChildBaseAddr = Inline.Ranges[0].start();
  195. while (true) {
  196. llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr);
  197. if (!Child)
  198. return Child.takeError();
  199. // InlineInfo with empty Ranges termintes a child sibling chain.
  200. if (Child.get().Ranges.empty())
  201. break;
  202. Inline.Children.emplace_back(std::move(*Child));
  203. }
  204. }
  205. return Inline;
  206. }
  207. llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data,
  208. uint64_t BaseAddr) {
  209. uint64_t Offset = 0;
  210. return ::decode(Data, Offset, BaseAddr);
  211. }
  212. llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const {
  213. // Users must verify the InlineInfo is valid prior to calling this funtion.
  214. // We don't want to emit any InlineInfo objects if they are not valid since
  215. // it will waste space in the GSYM file.
  216. if (!isValid())
  217. return createStringError(std::errc::invalid_argument,
  218. "attempted to encode invalid InlineInfo object");
  219. encodeRanges(Ranges, O, BaseAddr);
  220. bool HasChildren = !Children.empty();
  221. O.writeU8(HasChildren);
  222. O.writeU32(Name);
  223. O.writeULEB(CallFile);
  224. O.writeULEB(CallLine);
  225. if (HasChildren) {
  226. // Child address ranges are encoded as relative to the first
  227. // address in the Ranges for this object. This keeps the offsets
  228. // small and allows for efficient encoding using ULEB offsets.
  229. const uint64_t ChildBaseAddr = Ranges[0].start();
  230. for (const auto &Child : Children) {
  231. // Make sure all child address ranges are contained in the parent address
  232. // ranges.
  233. for (const auto &ChildRange: Child.Ranges) {
  234. if (!Ranges.contains(ChildRange))
  235. return createStringError(std::errc::invalid_argument,
  236. "child range not contained in parent");
  237. }
  238. llvm::Error Err = Child.encode(O, ChildBaseAddr);
  239. if (Err)
  240. return Err;
  241. }
  242. // Terminate child sibling chain by emitting a zero. This zero will cause
  243. // the decodeAll() function above to return false and stop the decoding
  244. // of child InlineInfo objects that are siblings.
  245. O.writeULEB(0);
  246. }
  247. return Error::success();
  248. }