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