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 <) { 284*8bcb0991SDimitry Andric for (const auto &LineEntry : LT) 285*8bcb0991SDimitry Andric OS << LineEntry << '\n'; 286*8bcb0991SDimitry Andric return OS; 287*8bcb0991SDimitry Andric } 288