xref: /llvm-project/llvm/lib/DebugInfo/GSYM/LineTable.cpp (revision 27033cc66500b7578b2cbf297b7881eef0cafdac)
17fcc2c2bSGreg Clayton //===- LineTable.cpp --------------------------------------------*- C++ -*-===//
27fcc2c2bSGreg Clayton //
37fcc2c2bSGreg Clayton // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47fcc2c2bSGreg Clayton // See https://llvm.org/LICENSE.txt for license information.
57fcc2c2bSGreg Clayton // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67fcc2c2bSGreg Clayton //
77fcc2c2bSGreg Clayton //===----------------------------------------------------------------------===//
87fcc2c2bSGreg Clayton 
97fcc2c2bSGreg Clayton #include "llvm/DebugInfo/GSYM/LineTable.h"
107fcc2c2bSGreg Clayton #include "llvm/DebugInfo/GSYM/FileWriter.h"
117fcc2c2bSGreg Clayton #include "llvm/Support/DataExtractor.h"
127fcc2c2bSGreg Clayton 
137fcc2c2bSGreg Clayton using namespace llvm;
147fcc2c2bSGreg Clayton using namespace gsym;
157fcc2c2bSGreg Clayton 
167fcc2c2bSGreg Clayton enum LineTableOpCode {
177fcc2c2bSGreg Clayton   EndSequence = 0x00,  ///< End of the line table.
187fcc2c2bSGreg Clayton   SetFile = 0x01,      ///< Set LineTableRow.file_idx, don't push a row.
197fcc2c2bSGreg Clayton   AdvancePC = 0x02,    ///< Increment LineTableRow.address, and push a row.
207fcc2c2bSGreg Clayton   AdvanceLine = 0x03,  ///< Set LineTableRow.file_line, don't push a row.
217fcc2c2bSGreg Clayton   FirstSpecial = 0x04, ///< All special opcodes push a row.
227fcc2c2bSGreg Clayton };
237fcc2c2bSGreg Clayton 
247fcc2c2bSGreg Clayton struct DeltaInfo {
257fcc2c2bSGreg Clayton   int64_t Delta;
267fcc2c2bSGreg Clayton   uint32_t Count;
DeltaInfoDeltaInfo277fcc2c2bSGreg Clayton   DeltaInfo(int64_t D, uint32_t C) : Delta(D), Count(C) {}
287fcc2c2bSGreg Clayton };
297fcc2c2bSGreg Clayton 
operator <(const DeltaInfo & LHS,int64_t Delta)307fcc2c2bSGreg Clayton inline bool operator<(const DeltaInfo &LHS, int64_t Delta) {
317fcc2c2bSGreg Clayton   return LHS.Delta < Delta;
327fcc2c2bSGreg Clayton }
337fcc2c2bSGreg Clayton 
encodeSpecial(int64_t MinLineDelta,int64_t MaxLineDelta,int64_t LineDelta,uint64_t AddrDelta,uint8_t & SpecialOp)347fcc2c2bSGreg Clayton static bool encodeSpecial(int64_t MinLineDelta, int64_t MaxLineDelta,
357fcc2c2bSGreg Clayton                           int64_t LineDelta, uint64_t AddrDelta,
367fcc2c2bSGreg Clayton                           uint8_t &SpecialOp) {
377fcc2c2bSGreg Clayton   if (LineDelta < MinLineDelta)
387fcc2c2bSGreg Clayton     return false;
397fcc2c2bSGreg Clayton   if (LineDelta > MaxLineDelta)
407fcc2c2bSGreg Clayton     return false;
417fcc2c2bSGreg Clayton   int64_t LineRange = MaxLineDelta - MinLineDelta + 1;
427fcc2c2bSGreg Clayton   int64_t AdjustedOp = ((LineDelta - MinLineDelta) + AddrDelta * LineRange);
437fcc2c2bSGreg Clayton   int64_t Op = AdjustedOp + FirstSpecial;
447fcc2c2bSGreg Clayton   if (Op < 0)
457fcc2c2bSGreg Clayton     return false;
467fcc2c2bSGreg Clayton   if (Op > 255)
477fcc2c2bSGreg Clayton     return false;
487fcc2c2bSGreg Clayton   SpecialOp = (uint8_t)Op;
497fcc2c2bSGreg Clayton   return true;
507fcc2c2bSGreg Clayton }
517fcc2c2bSGreg Clayton 
527fcc2c2bSGreg Clayton typedef std::function<bool(const LineEntry &Row)> LineEntryCallback;
537fcc2c2bSGreg Clayton 
parse(DataExtractor & Data,uint64_t BaseAddr,LineEntryCallback const & Callback)547fcc2c2bSGreg Clayton static llvm::Error parse(DataExtractor &Data, uint64_t BaseAddr,
557fcc2c2bSGreg Clayton                          LineEntryCallback const &Callback) {
567fcc2c2bSGreg Clayton   uint64_t Offset = 0;
577fcc2c2bSGreg Clayton   if (!Data.isValidOffset(Offset))
587fcc2c2bSGreg Clayton     return createStringError(std::errc::io_error,
597fcc2c2bSGreg Clayton         "0x%8.8" PRIx64 ": missing LineTable MinDelta", Offset);
607fcc2c2bSGreg Clayton   int64_t MinDelta = Data.getSLEB128(&Offset);
617fcc2c2bSGreg Clayton   if (!Data.isValidOffset(Offset))
627fcc2c2bSGreg Clayton     return createStringError(std::errc::io_error,
637fcc2c2bSGreg Clayton         "0x%8.8" PRIx64 ": missing LineTable MaxDelta", Offset);
647fcc2c2bSGreg Clayton   int64_t MaxDelta = Data.getSLEB128(&Offset);
657fcc2c2bSGreg Clayton   int64_t LineRange = MaxDelta - MinDelta + 1;
667fcc2c2bSGreg Clayton   if (!Data.isValidOffset(Offset))
677fcc2c2bSGreg Clayton     return createStringError(std::errc::io_error,
687fcc2c2bSGreg Clayton         "0x%8.8" PRIx64 ": missing LineTable FirstLine", Offset);
697fcc2c2bSGreg Clayton   const uint32_t FirstLine = (uint32_t)Data.getULEB128(&Offset);
707fcc2c2bSGreg Clayton   LineEntry Row(BaseAddr, 1, FirstLine);
717fcc2c2bSGreg Clayton   bool Done = false;
727fcc2c2bSGreg Clayton   while (!Done) {
737fcc2c2bSGreg Clayton     if (!Data.isValidOffset(Offset))
747fcc2c2bSGreg Clayton       return createStringError(std::errc::io_error,
757fcc2c2bSGreg Clayton           "0x%8.8" PRIx64 ": EOF found before EndSequence", Offset);
767fcc2c2bSGreg Clayton     uint8_t Op = Data.getU8(&Offset);
777fcc2c2bSGreg Clayton     switch (Op) {
787fcc2c2bSGreg Clayton     case EndSequence:
797fcc2c2bSGreg Clayton       Done = true;
807fcc2c2bSGreg Clayton       break;
817fcc2c2bSGreg Clayton     case SetFile:
827fcc2c2bSGreg Clayton       if (!Data.isValidOffset(Offset))
837fcc2c2bSGreg Clayton         return createStringError(std::errc::io_error,
847fcc2c2bSGreg Clayton             "0x%8.8" PRIx64 ": EOF found before SetFile value",
857fcc2c2bSGreg Clayton             Offset);
867fcc2c2bSGreg Clayton       Row.File = (uint32_t)Data.getULEB128(&Offset);
877fcc2c2bSGreg Clayton       break;
887fcc2c2bSGreg Clayton     case AdvancePC:
897fcc2c2bSGreg Clayton       if (!Data.isValidOffset(Offset))
907fcc2c2bSGreg Clayton         return createStringError(std::errc::io_error,
917fcc2c2bSGreg Clayton             "0x%8.8" PRIx64 ": EOF found before AdvancePC value",
927fcc2c2bSGreg Clayton             Offset);
937fcc2c2bSGreg Clayton       Row.Addr += Data.getULEB128(&Offset);
947fcc2c2bSGreg Clayton       // If the function callback returns false, we stop parsing.
957fcc2c2bSGreg Clayton       if (Callback(Row) == false)
967fcc2c2bSGreg Clayton         return Error::success();
977fcc2c2bSGreg Clayton       break;
987fcc2c2bSGreg Clayton     case AdvanceLine:
997fcc2c2bSGreg Clayton       if (!Data.isValidOffset(Offset))
1007fcc2c2bSGreg Clayton         return createStringError(std::errc::io_error,
1017fcc2c2bSGreg Clayton             "0x%8.8" PRIx64 ": EOF found before AdvanceLine value",
1027fcc2c2bSGreg Clayton             Offset);
1037fcc2c2bSGreg Clayton       Row.Line += Data.getSLEB128(&Offset);
1047fcc2c2bSGreg Clayton       break;
1057fcc2c2bSGreg Clayton     default: {
1067fcc2c2bSGreg Clayton         // A byte that contains both address and line increment.
1077fcc2c2bSGreg Clayton         uint8_t AdjustedOp = Op - FirstSpecial;
1087fcc2c2bSGreg Clayton         int64_t LineDelta = MinDelta + (AdjustedOp % LineRange);
1097fcc2c2bSGreg Clayton         uint64_t AddrDelta = (AdjustedOp / LineRange);
1107fcc2c2bSGreg Clayton         Row.Line += LineDelta;
1117fcc2c2bSGreg Clayton         Row.Addr += AddrDelta;
1127fcc2c2bSGreg Clayton         // If the function callback returns false, we stop parsing.
1137fcc2c2bSGreg Clayton         if (Callback(Row) == false)
1147fcc2c2bSGreg Clayton           return Error::success();
1157fcc2c2bSGreg Clayton         break;
1167fcc2c2bSGreg Clayton       }
1177fcc2c2bSGreg Clayton     }
1187fcc2c2bSGreg Clayton   }
1197fcc2c2bSGreg Clayton   return Error::success();
1207fcc2c2bSGreg Clayton }
1217fcc2c2bSGreg Clayton 
encode(FileWriter & Out,uint64_t BaseAddr) const1227fcc2c2bSGreg Clayton llvm::Error LineTable::encode(FileWriter &Out, uint64_t BaseAddr) const {
1237fcc2c2bSGreg Clayton   // Users must verify the LineTable is valid prior to calling this funtion.
1247fcc2c2bSGreg Clayton   // We don't want to emit any LineTable objects if they are not valid since
1257fcc2c2bSGreg Clayton   // it will waste space in the GSYM file.
1267fcc2c2bSGreg Clayton   if (!isValid())
1277fcc2c2bSGreg Clayton     return createStringError(std::errc::invalid_argument,
1287fcc2c2bSGreg Clayton                              "attempted to encode invalid LineTable object");
1297fcc2c2bSGreg Clayton 
1307fcc2c2bSGreg Clayton   int64_t MinLineDelta = INT64_MAX;
1317fcc2c2bSGreg Clayton   int64_t MaxLineDelta = INT64_MIN;
1327fcc2c2bSGreg Clayton   std::vector<DeltaInfo> DeltaInfos;
1337fcc2c2bSGreg Clayton   if (Lines.size() == 1) {
1347fcc2c2bSGreg Clayton     MinLineDelta = 0;
1357fcc2c2bSGreg Clayton     MaxLineDelta = 0;
1367fcc2c2bSGreg Clayton   } else {
1377fcc2c2bSGreg Clayton     int64_t PrevLine = 1;
1387fcc2c2bSGreg Clayton     bool First = true;
1397fcc2c2bSGreg Clayton     for (const auto &line_entry : Lines) {
1407fcc2c2bSGreg Clayton       if (First)
1417fcc2c2bSGreg Clayton         First = false;
1427fcc2c2bSGreg Clayton       else {
1437fcc2c2bSGreg Clayton         int64_t LineDelta = (int64_t)line_entry.Line - PrevLine;
1447fcc2c2bSGreg Clayton         auto End = DeltaInfos.end();
1457fcc2c2bSGreg Clayton         auto Pos = std::lower_bound(DeltaInfos.begin(), End, LineDelta);
1467fcc2c2bSGreg Clayton         if (Pos != End && Pos->Delta == LineDelta)
1477fcc2c2bSGreg Clayton           ++Pos->Count;
1487fcc2c2bSGreg Clayton         else
1497fcc2c2bSGreg Clayton           DeltaInfos.insert(Pos, DeltaInfo(LineDelta, 1));
1507fcc2c2bSGreg Clayton         if (LineDelta < MinLineDelta)
1517fcc2c2bSGreg Clayton           MinLineDelta = LineDelta;
1527fcc2c2bSGreg Clayton         if (LineDelta > MaxLineDelta)
1537fcc2c2bSGreg Clayton           MaxLineDelta = LineDelta;
1547fcc2c2bSGreg Clayton       }
1557fcc2c2bSGreg Clayton       PrevLine = (int64_t)line_entry.Line;
1567fcc2c2bSGreg Clayton     }
1577fcc2c2bSGreg Clayton     assert(MinLineDelta <= MaxLineDelta);
1587fcc2c2bSGreg Clayton   }
1597fcc2c2bSGreg Clayton   // Set the min and max line delta intelligently based on the counts of
1607fcc2c2bSGreg Clayton   // the line deltas. if our range is too large.
1617fcc2c2bSGreg Clayton   const int64_t MaxLineRange = 14;
1627fcc2c2bSGreg Clayton   if (MaxLineDelta - MinLineDelta > MaxLineRange) {
1637fcc2c2bSGreg Clayton     uint32_t BestIndex = 0;
1647fcc2c2bSGreg Clayton     uint32_t BestEndIndex = 0;
1657fcc2c2bSGreg Clayton     uint32_t BestCount = 0;
1667fcc2c2bSGreg Clayton     const size_t NumDeltaInfos = DeltaInfos.size();
1677fcc2c2bSGreg Clayton     for (uint32_t I = 0; I < NumDeltaInfos; ++I) {
1687fcc2c2bSGreg Clayton       const int64_t FirstDelta = DeltaInfos[I].Delta;
1697fcc2c2bSGreg Clayton       uint32_t CurrCount = 0;
1707fcc2c2bSGreg Clayton       uint32_t J;
1717fcc2c2bSGreg Clayton       for (J = I; J < NumDeltaInfos; ++J) {
1727fcc2c2bSGreg Clayton         auto LineRange = DeltaInfos[J].Delta - FirstDelta;
1737fcc2c2bSGreg Clayton         if (LineRange > MaxLineRange)
1747fcc2c2bSGreg Clayton           break;
1757fcc2c2bSGreg Clayton         CurrCount += DeltaInfos[J].Count;
1767fcc2c2bSGreg Clayton       }
1777fcc2c2bSGreg Clayton       if (CurrCount > BestCount) {
1787fcc2c2bSGreg Clayton         BestIndex = I;
1797fcc2c2bSGreg Clayton         BestEndIndex = J - 1;
1807fcc2c2bSGreg Clayton         BestCount = CurrCount;
1817fcc2c2bSGreg Clayton       }
1827fcc2c2bSGreg Clayton     }
1837fcc2c2bSGreg Clayton     MinLineDelta = DeltaInfos[BestIndex].Delta;
1847fcc2c2bSGreg Clayton     MaxLineDelta = DeltaInfos[BestEndIndex].Delta;
1857fcc2c2bSGreg Clayton   }
1867fcc2c2bSGreg Clayton   if (MinLineDelta == MaxLineDelta && MinLineDelta > 0 &&
1877fcc2c2bSGreg Clayton       MinLineDelta < MaxLineRange)
1887fcc2c2bSGreg Clayton     MinLineDelta = 0;
1897fcc2c2bSGreg Clayton   assert(MinLineDelta <= MaxLineDelta);
1907fcc2c2bSGreg Clayton 
1917fcc2c2bSGreg Clayton   // Initialize the line entry state as a starting point. All line entries
1927fcc2c2bSGreg Clayton   // will be deltas from this.
1937fcc2c2bSGreg Clayton   LineEntry Prev(BaseAddr, 1, Lines.front().Line);
1947fcc2c2bSGreg Clayton 
1957fcc2c2bSGreg Clayton   // Write out the min and max line delta as signed LEB128.
1967fcc2c2bSGreg Clayton   Out.writeSLEB(MinLineDelta);
1977fcc2c2bSGreg Clayton   Out.writeSLEB(MaxLineDelta);
1987fcc2c2bSGreg Clayton   // Write out the starting line number as a unsigned LEB128.
1997fcc2c2bSGreg Clayton   Out.writeULEB(Prev.Line);
2007fcc2c2bSGreg Clayton 
2017fcc2c2bSGreg Clayton   for (const auto &Curr : Lines) {
2027fcc2c2bSGreg Clayton     if (Curr.Addr < BaseAddr)
2037fcc2c2bSGreg Clayton       return createStringError(std::errc::invalid_argument,
2047fcc2c2bSGreg Clayton                                "LineEntry has address 0x%" PRIx64 " which is "
2057fcc2c2bSGreg Clayton                                "less than the function start address 0x%"
2067fcc2c2bSGreg Clayton                                PRIx64, Curr.Addr, BaseAddr);
2077fcc2c2bSGreg Clayton     if (Curr.Addr < Prev.Addr)
2087fcc2c2bSGreg Clayton       return createStringError(std::errc::invalid_argument,
2097fcc2c2bSGreg Clayton                                "LineEntry in LineTable not in ascending order");
2107fcc2c2bSGreg Clayton     const uint64_t AddrDelta = Curr.Addr - Prev.Addr;
2117fcc2c2bSGreg Clayton     int64_t LineDelta = 0;
2127fcc2c2bSGreg Clayton     if (Curr.Line > Prev.Line)
2137fcc2c2bSGreg Clayton       LineDelta = Curr.Line - Prev.Line;
2147fcc2c2bSGreg Clayton     else if (Prev.Line > Curr.Line)
2157fcc2c2bSGreg Clayton       LineDelta = -((int32_t)(Prev.Line - Curr.Line));
2167fcc2c2bSGreg Clayton 
2177fcc2c2bSGreg Clayton     // Set the file if it doesn't match the current one.
2187fcc2c2bSGreg Clayton     if (Curr.File != Prev.File) {
2197fcc2c2bSGreg Clayton       Out.writeU8(SetFile);
2207fcc2c2bSGreg Clayton       Out.writeULEB(Curr.File);
2217fcc2c2bSGreg Clayton     }
2227fcc2c2bSGreg Clayton 
2237fcc2c2bSGreg Clayton     uint8_t SpecialOp;
2247fcc2c2bSGreg Clayton     if (encodeSpecial(MinLineDelta, MaxLineDelta, LineDelta, AddrDelta,
2257fcc2c2bSGreg Clayton                       SpecialOp)) {
2267fcc2c2bSGreg Clayton       // Advance the PC and line and push a row.
2277fcc2c2bSGreg Clayton       Out.writeU8(SpecialOp);
2287fcc2c2bSGreg Clayton     } else {
2297fcc2c2bSGreg Clayton       // We can't encode the address delta and line delta into
2307fcc2c2bSGreg Clayton       // a single special opcode, we must do them separately.
2317fcc2c2bSGreg Clayton 
2327fcc2c2bSGreg Clayton       // Advance the line.
2337fcc2c2bSGreg Clayton       if (LineDelta != 0) {
2347fcc2c2bSGreg Clayton         Out.writeU8(AdvanceLine);
2357fcc2c2bSGreg Clayton         Out.writeSLEB(LineDelta);
2367fcc2c2bSGreg Clayton       }
2377fcc2c2bSGreg Clayton 
2387fcc2c2bSGreg Clayton       // Advance the PC and push a row.
2397fcc2c2bSGreg Clayton       Out.writeU8(AdvancePC);
2407fcc2c2bSGreg Clayton       Out.writeULEB(AddrDelta);
2417fcc2c2bSGreg Clayton     }
2427fcc2c2bSGreg Clayton     Prev = Curr;
2437fcc2c2bSGreg Clayton   }
2447fcc2c2bSGreg Clayton   Out.writeU8(EndSequence);
2457fcc2c2bSGreg Clayton   return Error::success();
2467fcc2c2bSGreg Clayton }
2477fcc2c2bSGreg Clayton 
2487fcc2c2bSGreg Clayton // Parse all line table entries into the "LineTable" vector. We can
2497fcc2c2bSGreg Clayton // cache the results of this if needed, or we can call LineTable::lookup()
2507fcc2c2bSGreg Clayton // below.
decode(DataExtractor & Data,uint64_t BaseAddr)2517fcc2c2bSGreg Clayton llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data,
2527fcc2c2bSGreg Clayton                                             uint64_t BaseAddr) {
2537fcc2c2bSGreg Clayton   LineTable LT;
2547fcc2c2bSGreg Clayton   llvm::Error Err = parse(Data, BaseAddr, [&](const LineEntry &Row) -> bool {
2557fcc2c2bSGreg Clayton     LT.Lines.push_back(Row);
2567fcc2c2bSGreg Clayton     return true; // Keep parsing by returning true.
2577fcc2c2bSGreg Clayton   });
2587fcc2c2bSGreg Clayton   if (Err)
259*c55cf4afSBill Wendling     return std::move(Err);
2607fcc2c2bSGreg Clayton   return LT;
2617fcc2c2bSGreg Clayton }
2627fcc2c2bSGreg Clayton // Parse the line table on the fly and find the row we are looking for.
2637fcc2c2bSGreg Clayton // We will need to determine if we need to cache the line table by calling
2647fcc2c2bSGreg Clayton // LineTable::parseAllEntries(...) or just call this function each time.
265aeda128aSGreg Clayton // There is a CPU vs memory tradeoff we will need to determined.
lookup(DataExtractor & Data,uint64_t BaseAddr,uint64_t Addr)266aeda128aSGreg Clayton Expected<LineEntry> LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) {
2677fcc2c2bSGreg Clayton   LineEntry Result;
2687fcc2c2bSGreg Clayton   llvm::Error Err = parse(Data, BaseAddr,
2697fcc2c2bSGreg Clayton                           [Addr, &Result](const LineEntry &Row) -> bool {
2707fcc2c2bSGreg Clayton     if (Addr < Row.Addr)
2717fcc2c2bSGreg Clayton       return false; // Stop parsing, result contains the line table row!
2727fcc2c2bSGreg Clayton     Result = Row;
2737fcc2c2bSGreg Clayton     return true; // Keep parsing till we find the right row.
2747fcc2c2bSGreg Clayton   });
275aeda128aSGreg Clayton   if (Err)
276*c55cf4afSBill Wendling     return std::move(Err);
277aeda128aSGreg Clayton   if (Result.isValid())
2787fcc2c2bSGreg Clayton     return Result;
279aeda128aSGreg Clayton   return createStringError(std::errc::invalid_argument,
280aeda128aSGreg Clayton                            "address 0x%" PRIx64 " is not in the line table",
281aeda128aSGreg Clayton                            Addr);
2827fcc2c2bSGreg Clayton }
2837fcc2c2bSGreg Clayton 
operator <<(raw_ostream & OS,const LineTable & LT)2847fcc2c2bSGreg Clayton raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable &LT) {
2857fcc2c2bSGreg Clayton   for (const auto &LineEntry : LT)
2867fcc2c2bSGreg Clayton     OS << LineEntry << '\n';
2877fcc2c2bSGreg Clayton   return OS;
2887fcc2c2bSGreg Clayton }
289