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