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