xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/LineTable.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
18bcb0991SDimitry Andric //===- LineTable.cpp --------------------------------------------*- C++ -*-===//
28bcb0991SDimitry Andric //
38bcb0991SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48bcb0991SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
58bcb0991SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68bcb0991SDimitry Andric //
78bcb0991SDimitry Andric //===----------------------------------------------------------------------===//
88bcb0991SDimitry Andric 
98bcb0991SDimitry Andric #include "llvm/DebugInfo/GSYM/LineTable.h"
108bcb0991SDimitry Andric #include "llvm/DebugInfo/GSYM/FileWriter.h"
118bcb0991SDimitry Andric #include "llvm/Support/DataExtractor.h"
128bcb0991SDimitry Andric 
138bcb0991SDimitry Andric using namespace llvm;
148bcb0991SDimitry Andric using namespace gsym;
158bcb0991SDimitry Andric 
168bcb0991SDimitry Andric enum LineTableOpCode {
178bcb0991SDimitry Andric   EndSequence = 0x00,  ///< End of the line table.
188bcb0991SDimitry Andric   SetFile = 0x01,      ///< Set LineTableRow.file_idx, don't push a row.
198bcb0991SDimitry Andric   AdvancePC = 0x02,    ///< Increment LineTableRow.address, and push a row.
208bcb0991SDimitry Andric   AdvanceLine = 0x03,  ///< Set LineTableRow.file_line, don't push a row.
218bcb0991SDimitry Andric   FirstSpecial = 0x04, ///< All special opcodes push a row.
228bcb0991SDimitry Andric };
238bcb0991SDimitry Andric 
248bcb0991SDimitry Andric struct DeltaInfo {
258bcb0991SDimitry Andric   int64_t Delta;
268bcb0991SDimitry Andric   uint32_t Count;
278bcb0991SDimitry Andric   DeltaInfo(int64_t D, uint32_t C) : Delta(D), Count(C) {}
288bcb0991SDimitry Andric };
298bcb0991SDimitry Andric 
308bcb0991SDimitry Andric inline bool operator<(const DeltaInfo &LHS, int64_t Delta) {
318bcb0991SDimitry Andric   return LHS.Delta < Delta;
328bcb0991SDimitry Andric }
338bcb0991SDimitry Andric 
348bcb0991SDimitry Andric static bool encodeSpecial(int64_t MinLineDelta, int64_t MaxLineDelta,
358bcb0991SDimitry Andric                           int64_t LineDelta, uint64_t AddrDelta,
368bcb0991SDimitry Andric                           uint8_t &SpecialOp) {
378bcb0991SDimitry Andric   if (LineDelta < MinLineDelta)
388bcb0991SDimitry Andric     return false;
398bcb0991SDimitry Andric   if (LineDelta > MaxLineDelta)
408bcb0991SDimitry Andric     return false;
418bcb0991SDimitry Andric   int64_t LineRange = MaxLineDelta - MinLineDelta + 1;
428bcb0991SDimitry Andric   int64_t AdjustedOp = ((LineDelta - MinLineDelta) + AddrDelta * LineRange);
438bcb0991SDimitry Andric   int64_t Op = AdjustedOp + FirstSpecial;
448bcb0991SDimitry Andric   if (Op < 0)
458bcb0991SDimitry Andric     return false;
468bcb0991SDimitry Andric   if (Op > 255)
478bcb0991SDimitry Andric     return false;
488bcb0991SDimitry Andric   SpecialOp = (uint8_t)Op;
498bcb0991SDimitry Andric   return true;
508bcb0991SDimitry Andric }
518bcb0991SDimitry Andric 
528bcb0991SDimitry Andric typedef std::function<bool(const LineEntry &Row)> LineEntryCallback;
538bcb0991SDimitry Andric 
548bcb0991SDimitry Andric static llvm::Error parse(DataExtractor &Data, uint64_t BaseAddr,
558bcb0991SDimitry Andric                          LineEntryCallback const &Callback) {
568bcb0991SDimitry Andric   uint64_t Offset = 0;
578bcb0991SDimitry Andric   if (!Data.isValidOffset(Offset))
588bcb0991SDimitry Andric     return createStringError(std::errc::io_error,
598bcb0991SDimitry Andric         "0x%8.8" PRIx64 ": missing LineTable MinDelta", Offset);
608bcb0991SDimitry Andric   int64_t MinDelta = Data.getSLEB128(&Offset);
618bcb0991SDimitry Andric   if (!Data.isValidOffset(Offset))
628bcb0991SDimitry Andric     return createStringError(std::errc::io_error,
638bcb0991SDimitry Andric         "0x%8.8" PRIx64 ": missing LineTable MaxDelta", Offset);
648bcb0991SDimitry Andric   int64_t MaxDelta = Data.getSLEB128(&Offset);
658bcb0991SDimitry Andric   int64_t LineRange = MaxDelta - MinDelta + 1;
668bcb0991SDimitry Andric   if (!Data.isValidOffset(Offset))
678bcb0991SDimitry Andric     return createStringError(std::errc::io_error,
688bcb0991SDimitry Andric         "0x%8.8" PRIx64 ": missing LineTable FirstLine", Offset);
698bcb0991SDimitry Andric   const uint32_t FirstLine = (uint32_t)Data.getULEB128(&Offset);
708bcb0991SDimitry Andric   LineEntry Row(BaseAddr, 1, FirstLine);
718bcb0991SDimitry Andric   bool Done = false;
728bcb0991SDimitry Andric   while (!Done) {
738bcb0991SDimitry Andric     if (!Data.isValidOffset(Offset))
748bcb0991SDimitry Andric       return createStringError(std::errc::io_error,
758bcb0991SDimitry Andric           "0x%8.8" PRIx64 ": EOF found before EndSequence", Offset);
768bcb0991SDimitry Andric     uint8_t Op = Data.getU8(&Offset);
778bcb0991SDimitry Andric     switch (Op) {
788bcb0991SDimitry Andric     case EndSequence:
798bcb0991SDimitry Andric       Done = true;
808bcb0991SDimitry Andric       break;
818bcb0991SDimitry Andric     case SetFile:
828bcb0991SDimitry Andric       if (!Data.isValidOffset(Offset))
838bcb0991SDimitry Andric         return createStringError(std::errc::io_error,
848bcb0991SDimitry Andric             "0x%8.8" PRIx64 ": EOF found before SetFile value",
858bcb0991SDimitry Andric             Offset);
868bcb0991SDimitry Andric       Row.File = (uint32_t)Data.getULEB128(&Offset);
878bcb0991SDimitry Andric       break;
888bcb0991SDimitry Andric     case AdvancePC:
898bcb0991SDimitry Andric       if (!Data.isValidOffset(Offset))
908bcb0991SDimitry Andric         return createStringError(std::errc::io_error,
918bcb0991SDimitry Andric             "0x%8.8" PRIx64 ": EOF found before AdvancePC value",
928bcb0991SDimitry Andric             Offset);
938bcb0991SDimitry Andric       Row.Addr += Data.getULEB128(&Offset);
948bcb0991SDimitry Andric       // If the function callback returns false, we stop parsing.
958bcb0991SDimitry Andric       if (Callback(Row) == false)
968bcb0991SDimitry Andric         return Error::success();
978bcb0991SDimitry Andric       break;
988bcb0991SDimitry Andric     case AdvanceLine:
998bcb0991SDimitry Andric       if (!Data.isValidOffset(Offset))
1008bcb0991SDimitry Andric         return createStringError(std::errc::io_error,
1018bcb0991SDimitry Andric             "0x%8.8" PRIx64 ": EOF found before AdvanceLine value",
1028bcb0991SDimitry Andric             Offset);
1038bcb0991SDimitry Andric       Row.Line += Data.getSLEB128(&Offset);
1048bcb0991SDimitry Andric       break;
1058bcb0991SDimitry Andric     default: {
1068bcb0991SDimitry Andric         // A byte that contains both address and line increment.
1078bcb0991SDimitry Andric         uint8_t AdjustedOp = Op - FirstSpecial;
1088bcb0991SDimitry Andric         int64_t LineDelta = MinDelta + (AdjustedOp % LineRange);
1098bcb0991SDimitry Andric         uint64_t AddrDelta = (AdjustedOp / LineRange);
1108bcb0991SDimitry Andric         Row.Line += LineDelta;
1118bcb0991SDimitry Andric         Row.Addr += AddrDelta;
1128bcb0991SDimitry Andric         // If the function callback returns false, we stop parsing.
1138bcb0991SDimitry Andric         if (Callback(Row) == false)
1148bcb0991SDimitry Andric           return Error::success();
1158bcb0991SDimitry Andric         break;
1168bcb0991SDimitry Andric       }
1178bcb0991SDimitry Andric     }
1188bcb0991SDimitry Andric   }
1198bcb0991SDimitry Andric   return Error::success();
1208bcb0991SDimitry Andric }
1218bcb0991SDimitry Andric 
1228bcb0991SDimitry Andric llvm::Error LineTable::encode(FileWriter &Out, uint64_t BaseAddr) const {
1238bcb0991SDimitry Andric   // Users must verify the LineTable is valid prior to calling this funtion.
1248bcb0991SDimitry Andric   // We don't want to emit any LineTable objects if they are not valid since
1258bcb0991SDimitry Andric   // it will waste space in the GSYM file.
1268bcb0991SDimitry Andric   if (!isValid())
1278bcb0991SDimitry Andric     return createStringError(std::errc::invalid_argument,
1288bcb0991SDimitry Andric                              "attempted to encode invalid LineTable object");
1298bcb0991SDimitry Andric 
1308bcb0991SDimitry Andric   int64_t MinLineDelta = INT64_MAX;
1318bcb0991SDimitry Andric   int64_t MaxLineDelta = INT64_MIN;
1328bcb0991SDimitry Andric   std::vector<DeltaInfo> DeltaInfos;
1338bcb0991SDimitry Andric   if (Lines.size() == 1) {
1348bcb0991SDimitry Andric     MinLineDelta = 0;
1358bcb0991SDimitry Andric     MaxLineDelta = 0;
1368bcb0991SDimitry Andric   } else {
1378bcb0991SDimitry Andric     int64_t PrevLine = 1;
1388bcb0991SDimitry Andric     bool First = true;
1398bcb0991SDimitry Andric     for (const auto &line_entry : Lines) {
1408bcb0991SDimitry Andric       if (First)
1418bcb0991SDimitry Andric         First = false;
1428bcb0991SDimitry Andric       else {
1438bcb0991SDimitry Andric         int64_t LineDelta = (int64_t)line_entry.Line - PrevLine;
1448bcb0991SDimitry Andric         auto End = DeltaInfos.end();
1458bcb0991SDimitry Andric         auto Pos = std::lower_bound(DeltaInfos.begin(), End, LineDelta);
1468bcb0991SDimitry Andric         if (Pos != End && Pos->Delta == LineDelta)
1478bcb0991SDimitry Andric           ++Pos->Count;
1488bcb0991SDimitry Andric         else
1498bcb0991SDimitry Andric           DeltaInfos.insert(Pos, DeltaInfo(LineDelta, 1));
1508bcb0991SDimitry Andric         if (LineDelta < MinLineDelta)
1518bcb0991SDimitry Andric           MinLineDelta = LineDelta;
1528bcb0991SDimitry Andric         if (LineDelta > MaxLineDelta)
1538bcb0991SDimitry Andric           MaxLineDelta = LineDelta;
1548bcb0991SDimitry Andric       }
1558bcb0991SDimitry Andric       PrevLine = (int64_t)line_entry.Line;
1568bcb0991SDimitry Andric     }
1578bcb0991SDimitry Andric     assert(MinLineDelta <= MaxLineDelta);
1588bcb0991SDimitry Andric   }
1598bcb0991SDimitry Andric   // Set the min and max line delta intelligently based on the counts of
1608bcb0991SDimitry Andric   // the line deltas. if our range is too large.
1618bcb0991SDimitry Andric   const int64_t MaxLineRange = 14;
1628bcb0991SDimitry Andric   if (MaxLineDelta - MinLineDelta > MaxLineRange) {
1638bcb0991SDimitry Andric     uint32_t BestIndex = 0;
1648bcb0991SDimitry Andric     uint32_t BestEndIndex = 0;
1658bcb0991SDimitry Andric     uint32_t BestCount = 0;
1668bcb0991SDimitry Andric     const size_t NumDeltaInfos = DeltaInfos.size();
1678bcb0991SDimitry Andric     for (uint32_t I = 0; I < NumDeltaInfos; ++I) {
1688bcb0991SDimitry Andric       const int64_t FirstDelta = DeltaInfos[I].Delta;
1698bcb0991SDimitry Andric       uint32_t CurrCount = 0;
1708bcb0991SDimitry Andric       uint32_t J;
1718bcb0991SDimitry Andric       for (J = I; J < NumDeltaInfos; ++J) {
1728bcb0991SDimitry Andric         auto LineRange = DeltaInfos[J].Delta - FirstDelta;
1738bcb0991SDimitry Andric         if (LineRange > MaxLineRange)
1748bcb0991SDimitry Andric           break;
1758bcb0991SDimitry Andric         CurrCount += DeltaInfos[J].Count;
1768bcb0991SDimitry Andric       }
1778bcb0991SDimitry Andric       if (CurrCount > BestCount) {
1788bcb0991SDimitry Andric         BestIndex = I;
1798bcb0991SDimitry Andric         BestEndIndex = J - 1;
1808bcb0991SDimitry Andric         BestCount = CurrCount;
1818bcb0991SDimitry Andric       }
1828bcb0991SDimitry Andric     }
1838bcb0991SDimitry Andric     MinLineDelta = DeltaInfos[BestIndex].Delta;
1848bcb0991SDimitry Andric     MaxLineDelta = DeltaInfos[BestEndIndex].Delta;
1858bcb0991SDimitry Andric   }
1868bcb0991SDimitry Andric   if (MinLineDelta == MaxLineDelta && MinLineDelta > 0 &&
1878bcb0991SDimitry Andric       MinLineDelta < MaxLineRange)
1888bcb0991SDimitry Andric     MinLineDelta = 0;
1898bcb0991SDimitry Andric   assert(MinLineDelta <= MaxLineDelta);
1908bcb0991SDimitry Andric 
1918bcb0991SDimitry Andric   // Initialize the line entry state as a starting point. All line entries
1928bcb0991SDimitry Andric   // will be deltas from this.
1938bcb0991SDimitry Andric   LineEntry Prev(BaseAddr, 1, Lines.front().Line);
1948bcb0991SDimitry Andric 
1958bcb0991SDimitry Andric   // Write out the min and max line delta as signed LEB128.
1968bcb0991SDimitry Andric   Out.writeSLEB(MinLineDelta);
1978bcb0991SDimitry Andric   Out.writeSLEB(MaxLineDelta);
1988bcb0991SDimitry Andric   // Write out the starting line number as a unsigned LEB128.
1998bcb0991SDimitry Andric   Out.writeULEB(Prev.Line);
2008bcb0991SDimitry Andric 
2018bcb0991SDimitry Andric   for (const auto &Curr : Lines) {
2028bcb0991SDimitry Andric     if (Curr.Addr < BaseAddr)
2038bcb0991SDimitry Andric       return createStringError(std::errc::invalid_argument,
2048bcb0991SDimitry Andric                                "LineEntry has address 0x%" PRIx64 " which is "
2058bcb0991SDimitry Andric                                "less than the function start address 0x%"
2068bcb0991SDimitry Andric                                PRIx64, Curr.Addr, BaseAddr);
2078bcb0991SDimitry Andric     if (Curr.Addr < Prev.Addr)
2088bcb0991SDimitry Andric       return createStringError(std::errc::invalid_argument,
2098bcb0991SDimitry Andric                                "LineEntry in LineTable not in ascending order");
2108bcb0991SDimitry Andric     const uint64_t AddrDelta = Curr.Addr - Prev.Addr;
2118bcb0991SDimitry Andric     int64_t LineDelta = 0;
2128bcb0991SDimitry Andric     if (Curr.Line > Prev.Line)
2138bcb0991SDimitry Andric       LineDelta = Curr.Line - Prev.Line;
2148bcb0991SDimitry Andric     else if (Prev.Line > Curr.Line)
2158bcb0991SDimitry Andric       LineDelta = -((int32_t)(Prev.Line - Curr.Line));
2168bcb0991SDimitry Andric 
2178bcb0991SDimitry Andric     // Set the file if it doesn't match the current one.
2188bcb0991SDimitry Andric     if (Curr.File != Prev.File) {
2198bcb0991SDimitry Andric       Out.writeU8(SetFile);
2208bcb0991SDimitry Andric       Out.writeULEB(Curr.File);
2218bcb0991SDimitry Andric     }
2228bcb0991SDimitry Andric 
2238bcb0991SDimitry Andric     uint8_t SpecialOp;
2248bcb0991SDimitry Andric     if (encodeSpecial(MinLineDelta, MaxLineDelta, LineDelta, AddrDelta,
2258bcb0991SDimitry Andric                       SpecialOp)) {
2268bcb0991SDimitry Andric       // Advance the PC and line and push a row.
2278bcb0991SDimitry Andric       Out.writeU8(SpecialOp);
2288bcb0991SDimitry Andric     } else {
2298bcb0991SDimitry Andric       // We can't encode the address delta and line delta into
2308bcb0991SDimitry Andric       // a single special opcode, we must do them separately.
2318bcb0991SDimitry Andric 
2328bcb0991SDimitry Andric       // Advance the line.
2338bcb0991SDimitry Andric       if (LineDelta != 0) {
2348bcb0991SDimitry Andric         Out.writeU8(AdvanceLine);
2358bcb0991SDimitry Andric         Out.writeSLEB(LineDelta);
2368bcb0991SDimitry Andric       }
2378bcb0991SDimitry Andric 
2388bcb0991SDimitry Andric       // Advance the PC and push a row.
2398bcb0991SDimitry Andric       Out.writeU8(AdvancePC);
2408bcb0991SDimitry Andric       Out.writeULEB(AddrDelta);
2418bcb0991SDimitry Andric     }
2428bcb0991SDimitry Andric     Prev = Curr;
2438bcb0991SDimitry Andric   }
2448bcb0991SDimitry Andric   Out.writeU8(EndSequence);
2458bcb0991SDimitry Andric   return Error::success();
2468bcb0991SDimitry Andric }
2478bcb0991SDimitry Andric 
2488bcb0991SDimitry Andric // Parse all line table entries into the "LineTable" vector. We can
2498bcb0991SDimitry Andric // cache the results of this if needed, or we can call LineTable::lookup()
2508bcb0991SDimitry Andric // below.
2518bcb0991SDimitry Andric llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data,
2528bcb0991SDimitry Andric                                             uint64_t BaseAddr) {
2538bcb0991SDimitry Andric   LineTable LT;
2548bcb0991SDimitry Andric   llvm::Error Err = parse(Data, BaseAddr, [&](const LineEntry &Row) -> bool {
2558bcb0991SDimitry Andric     LT.Lines.push_back(Row);
2568bcb0991SDimitry Andric     return true; // Keep parsing by returning true.
2578bcb0991SDimitry Andric   });
2588bcb0991SDimitry Andric   if (Err)
2598bcb0991SDimitry Andric     return std::move(Err);
2608bcb0991SDimitry Andric   return LT;
2618bcb0991SDimitry Andric }
2628bcb0991SDimitry Andric // Parse the line table on the fly and find the row we are looking for.
2638bcb0991SDimitry Andric // We will need to determine if we need to cache the line table by calling
2648bcb0991SDimitry Andric // LineTable::parseAllEntries(...) or just call this function each time.
265*480093f4SDimitry Andric // There is a CPU vs memory tradeoff we will need to determined.
266*480093f4SDimitry Andric Expected<LineEntry> LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) {
2678bcb0991SDimitry Andric   LineEntry Result;
2688bcb0991SDimitry Andric   llvm::Error Err = parse(Data, BaseAddr,
2698bcb0991SDimitry Andric                           [Addr, &Result](const LineEntry &Row) -> bool {
2708bcb0991SDimitry Andric     if (Addr < Row.Addr)
2718bcb0991SDimitry Andric       return false; // Stop parsing, result contains the line table row!
2728bcb0991SDimitry Andric     Result = Row;
2738bcb0991SDimitry Andric     return true; // Keep parsing till we find the right row.
2748bcb0991SDimitry Andric   });
275*480093f4SDimitry Andric   if (Err)
276*480093f4SDimitry Andric     return std::move(Err);
277*480093f4SDimitry Andric   if (Result.isValid())
2788bcb0991SDimitry Andric     return Result;
279*480093f4SDimitry Andric   return createStringError(std::errc::invalid_argument,
280*480093f4SDimitry Andric                            "address 0x%" PRIx64 " is not in the line table",
281*480093f4SDimitry Andric                            Addr);
2828bcb0991SDimitry Andric }
2838bcb0991SDimitry Andric 
2848bcb0991SDimitry Andric raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable &LT) {
2858bcb0991SDimitry Andric   for (const auto &LineEntry : LT)
2868bcb0991SDimitry Andric     OS << LineEntry << '\n';
2878bcb0991SDimitry Andric   return OS;
2888bcb0991SDimitry Andric }
289