LineTable.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. //===- LineTable.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/LineTable.h"
  9. #include "llvm/DebugInfo/GSYM/FileWriter.h"
  10. #include "llvm/Support/DataExtractor.h"
  11. using namespace llvm;
  12. using namespace gsym;
  13. enum LineTableOpCode {
  14. EndSequence = 0x00, ///< End of the line table.
  15. SetFile = 0x01, ///< Set LineTableRow.file_idx, don't push a row.
  16. AdvancePC = 0x02, ///< Increment LineTableRow.address, and push a row.
  17. AdvanceLine = 0x03, ///< Set LineTableRow.file_line, don't push a row.
  18. FirstSpecial = 0x04, ///< All special opcodes push a row.
  19. };
  20. struct DeltaInfo {
  21. int64_t Delta;
  22. uint32_t Count;
  23. DeltaInfo(int64_t D, uint32_t C) : Delta(D), Count(C) {}
  24. };
  25. inline bool operator<(const DeltaInfo &LHS, int64_t Delta) {
  26. return LHS.Delta < Delta;
  27. }
  28. static bool encodeSpecial(int64_t MinLineDelta, int64_t MaxLineDelta,
  29. int64_t LineDelta, uint64_t AddrDelta,
  30. uint8_t &SpecialOp) {
  31. if (LineDelta < MinLineDelta)
  32. return false;
  33. if (LineDelta > MaxLineDelta)
  34. return false;
  35. int64_t LineRange = MaxLineDelta - MinLineDelta + 1;
  36. int64_t AdjustedOp = ((LineDelta - MinLineDelta) + AddrDelta * LineRange);
  37. int64_t Op = AdjustedOp + FirstSpecial;
  38. if (Op < 0)
  39. return false;
  40. if (Op > 255)
  41. return false;
  42. SpecialOp = (uint8_t)Op;
  43. return true;
  44. }
  45. typedef std::function<bool(const LineEntry &Row)> LineEntryCallback;
  46. static llvm::Error parse(DataExtractor &Data, uint64_t BaseAddr,
  47. LineEntryCallback const &Callback) {
  48. uint64_t Offset = 0;
  49. if (!Data.isValidOffset(Offset))
  50. return createStringError(std::errc::io_error,
  51. "0x%8.8" PRIx64 ": missing LineTable MinDelta", Offset);
  52. int64_t MinDelta = Data.getSLEB128(&Offset);
  53. if (!Data.isValidOffset(Offset))
  54. return createStringError(std::errc::io_error,
  55. "0x%8.8" PRIx64 ": missing LineTable MaxDelta", Offset);
  56. int64_t MaxDelta = Data.getSLEB128(&Offset);
  57. int64_t LineRange = MaxDelta - MinDelta + 1;
  58. if (!Data.isValidOffset(Offset))
  59. return createStringError(std::errc::io_error,
  60. "0x%8.8" PRIx64 ": missing LineTable FirstLine", Offset);
  61. const uint32_t FirstLine = (uint32_t)Data.getULEB128(&Offset);
  62. LineEntry Row(BaseAddr, 1, FirstLine);
  63. bool Done = false;
  64. while (!Done) {
  65. if (!Data.isValidOffset(Offset))
  66. return createStringError(std::errc::io_error,
  67. "0x%8.8" PRIx64 ": EOF found before EndSequence", Offset);
  68. uint8_t Op = Data.getU8(&Offset);
  69. switch (Op) {
  70. case EndSequence:
  71. Done = true;
  72. break;
  73. case SetFile:
  74. if (!Data.isValidOffset(Offset))
  75. return createStringError(std::errc::io_error,
  76. "0x%8.8" PRIx64 ": EOF found before SetFile value",
  77. Offset);
  78. Row.File = (uint32_t)Data.getULEB128(&Offset);
  79. break;
  80. case AdvancePC:
  81. if (!Data.isValidOffset(Offset))
  82. return createStringError(std::errc::io_error,
  83. "0x%8.8" PRIx64 ": EOF found before AdvancePC value",
  84. Offset);
  85. Row.Addr += Data.getULEB128(&Offset);
  86. // If the function callback returns false, we stop parsing.
  87. if (Callback(Row) == false)
  88. return Error::success();
  89. break;
  90. case AdvanceLine:
  91. if (!Data.isValidOffset(Offset))
  92. return createStringError(std::errc::io_error,
  93. "0x%8.8" PRIx64 ": EOF found before AdvanceLine value",
  94. Offset);
  95. Row.Line += Data.getSLEB128(&Offset);
  96. break;
  97. default: {
  98. // A byte that contains both address and line increment.
  99. uint8_t AdjustedOp = Op - FirstSpecial;
  100. int64_t LineDelta = MinDelta + (AdjustedOp % LineRange);
  101. uint64_t AddrDelta = (AdjustedOp / LineRange);
  102. Row.Line += LineDelta;
  103. Row.Addr += AddrDelta;
  104. // If the function callback returns false, we stop parsing.
  105. if (Callback(Row) == false)
  106. return Error::success();
  107. break;
  108. }
  109. }
  110. }
  111. return Error::success();
  112. }
  113. llvm::Error LineTable::encode(FileWriter &Out, uint64_t BaseAddr) const {
  114. // Users must verify the LineTable is valid prior to calling this funtion.
  115. // We don't want to emit any LineTable objects if they are not valid since
  116. // it will waste space in the GSYM file.
  117. if (!isValid())
  118. return createStringError(std::errc::invalid_argument,
  119. "attempted to encode invalid LineTable object");
  120. int64_t MinLineDelta = INT64_MAX;
  121. int64_t MaxLineDelta = INT64_MIN;
  122. std::vector<DeltaInfo> DeltaInfos;
  123. if (Lines.size() == 1) {
  124. MinLineDelta = 0;
  125. MaxLineDelta = 0;
  126. } else {
  127. int64_t PrevLine = 1;
  128. bool First = true;
  129. for (const auto &line_entry : Lines) {
  130. if (First)
  131. First = false;
  132. else {
  133. int64_t LineDelta = (int64_t)line_entry.Line - PrevLine;
  134. auto End = DeltaInfos.end();
  135. auto Pos = std::lower_bound(DeltaInfos.begin(), End, LineDelta);
  136. if (Pos != End && Pos->Delta == LineDelta)
  137. ++Pos->Count;
  138. else
  139. DeltaInfos.insert(Pos, DeltaInfo(LineDelta, 1));
  140. if (LineDelta < MinLineDelta)
  141. MinLineDelta = LineDelta;
  142. if (LineDelta > MaxLineDelta)
  143. MaxLineDelta = LineDelta;
  144. }
  145. PrevLine = (int64_t)line_entry.Line;
  146. }
  147. assert(MinLineDelta <= MaxLineDelta);
  148. }
  149. // Set the min and max line delta intelligently based on the counts of
  150. // the line deltas. if our range is too large.
  151. const int64_t MaxLineRange = 14;
  152. if (MaxLineDelta - MinLineDelta > MaxLineRange) {
  153. uint32_t BestIndex = 0;
  154. uint32_t BestEndIndex = 0;
  155. uint32_t BestCount = 0;
  156. const size_t NumDeltaInfos = DeltaInfos.size();
  157. for (uint32_t I = 0; I < NumDeltaInfos; ++I) {
  158. const int64_t FirstDelta = DeltaInfos[I].Delta;
  159. uint32_t CurrCount = 0;
  160. uint32_t J;
  161. for (J = I; J < NumDeltaInfos; ++J) {
  162. auto LineRange = DeltaInfos[J].Delta - FirstDelta;
  163. if (LineRange > MaxLineRange)
  164. break;
  165. CurrCount += DeltaInfos[J].Count;
  166. }
  167. if (CurrCount > BestCount) {
  168. BestIndex = I;
  169. BestEndIndex = J - 1;
  170. BestCount = CurrCount;
  171. }
  172. }
  173. MinLineDelta = DeltaInfos[BestIndex].Delta;
  174. MaxLineDelta = DeltaInfos[BestEndIndex].Delta;
  175. }
  176. if (MinLineDelta == MaxLineDelta && MinLineDelta > 0 &&
  177. MinLineDelta < MaxLineRange)
  178. MinLineDelta = 0;
  179. assert(MinLineDelta <= MaxLineDelta);
  180. // Initialize the line entry state as a starting point. All line entries
  181. // will be deltas from this.
  182. LineEntry Prev(BaseAddr, 1, Lines.front().Line);
  183. // Write out the min and max line delta as signed LEB128.
  184. Out.writeSLEB(MinLineDelta);
  185. Out.writeSLEB(MaxLineDelta);
  186. // Write out the starting line number as a unsigned LEB128.
  187. Out.writeULEB(Prev.Line);
  188. for (const auto &Curr : Lines) {
  189. if (Curr.Addr < BaseAddr)
  190. return createStringError(std::errc::invalid_argument,
  191. "LineEntry has address 0x%" PRIx64 " which is "
  192. "less than the function start address 0x%"
  193. PRIx64, Curr.Addr, BaseAddr);
  194. if (Curr.Addr < Prev.Addr)
  195. return createStringError(std::errc::invalid_argument,
  196. "LineEntry in LineTable not in ascending order");
  197. const uint64_t AddrDelta = Curr.Addr - Prev.Addr;
  198. int64_t LineDelta = 0;
  199. if (Curr.Line > Prev.Line)
  200. LineDelta = Curr.Line - Prev.Line;
  201. else if (Prev.Line > Curr.Line)
  202. LineDelta = -((int32_t)(Prev.Line - Curr.Line));
  203. // Set the file if it doesn't match the current one.
  204. if (Curr.File != Prev.File) {
  205. Out.writeU8(SetFile);
  206. Out.writeULEB(Curr.File);
  207. }
  208. uint8_t SpecialOp;
  209. if (encodeSpecial(MinLineDelta, MaxLineDelta, LineDelta, AddrDelta,
  210. SpecialOp)) {
  211. // Advance the PC and line and push a row.
  212. Out.writeU8(SpecialOp);
  213. } else {
  214. // We can't encode the address delta and line delta into
  215. // a single special opcode, we must do them separately.
  216. // Advance the line.
  217. if (LineDelta != 0) {
  218. Out.writeU8(AdvanceLine);
  219. Out.writeSLEB(LineDelta);
  220. }
  221. // Advance the PC and push a row.
  222. Out.writeU8(AdvancePC);
  223. Out.writeULEB(AddrDelta);
  224. }
  225. Prev = Curr;
  226. }
  227. Out.writeU8(EndSequence);
  228. return Error::success();
  229. }
  230. // Parse all line table entries into the "LineTable" vector. We can
  231. // cache the results of this if needed, or we can call LineTable::lookup()
  232. // below.
  233. llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data,
  234. uint64_t BaseAddr) {
  235. LineTable LT;
  236. llvm::Error Err = parse(Data, BaseAddr, [&](const LineEntry &Row) -> bool {
  237. LT.Lines.push_back(Row);
  238. return true; // Keep parsing by returning true.
  239. });
  240. if (Err)
  241. return std::move(Err);
  242. return LT;
  243. }
  244. // Parse the line table on the fly and find the row we are looking for.
  245. // We will need to determine if we need to cache the line table by calling
  246. // LineTable::parseAllEntries(...) or just call this function each time.
  247. // There is a CPU vs memory tradeoff we will need to determined.
  248. Expected<LineEntry> LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) {
  249. LineEntry Result;
  250. llvm::Error Err = parse(Data, BaseAddr,
  251. [Addr, &Result](const LineEntry &Row) -> bool {
  252. if (Addr < Row.Addr)
  253. return false; // Stop parsing, result contains the line table row!
  254. Result = Row;
  255. if (Addr == Row.Addr) {
  256. // Stop parsing, this is the row we are looking for since the address
  257. // matches.
  258. return false;
  259. }
  260. return true; // Keep parsing till we find the right row.
  261. });
  262. if (Err)
  263. return std::move(Err);
  264. if (Result.isValid())
  265. return Result;
  266. return createStringError(std::errc::invalid_argument,
  267. "address 0x%" PRIx64 " is not in the line table",
  268. Addr);
  269. }
  270. raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable &LT) {
  271. for (const auto &LineEntry : LT)
  272. OS << LineEntry << '\n';
  273. return OS;
  274. }