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