123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- //===- LineTable.cpp --------------------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "llvm/DebugInfo/GSYM/LineTable.h"
- #include "llvm/DebugInfo/GSYM/FileWriter.h"
- #include "llvm/Support/DataExtractor.h"
- using namespace llvm;
- using namespace gsym;
- enum LineTableOpCode {
- EndSequence = 0x00, ///< End of the line table.
- SetFile = 0x01, ///< Set LineTableRow.file_idx, don't push a row.
- AdvancePC = 0x02, ///< Increment LineTableRow.address, and push a row.
- AdvanceLine = 0x03, ///< Set LineTableRow.file_line, don't push a row.
- FirstSpecial = 0x04, ///< All special opcodes push a row.
- };
- struct DeltaInfo {
- int64_t Delta;
- uint32_t Count;
- DeltaInfo(int64_t D, uint32_t C) : Delta(D), Count(C) {}
- };
- inline bool operator<(const DeltaInfo &LHS, int64_t Delta) {
- return LHS.Delta < Delta;
- }
- static bool encodeSpecial(int64_t MinLineDelta, int64_t MaxLineDelta,
- int64_t LineDelta, uint64_t AddrDelta,
- uint8_t &SpecialOp) {
- if (LineDelta < MinLineDelta)
- return false;
- if (LineDelta > MaxLineDelta)
- return false;
- int64_t LineRange = MaxLineDelta - MinLineDelta + 1;
- int64_t AdjustedOp = ((LineDelta - MinLineDelta) + AddrDelta * LineRange);
- int64_t Op = AdjustedOp + FirstSpecial;
- if (Op < 0)
- return false;
- if (Op > 255)
- return false;
- SpecialOp = (uint8_t)Op;
- return true;
- }
- typedef std::function<bool(const LineEntry &Row)> LineEntryCallback;
- static llvm::Error parse(DataExtractor &Data, uint64_t BaseAddr,
- LineEntryCallback const &Callback) {
- uint64_t Offset = 0;
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing LineTable MinDelta", Offset);
- int64_t MinDelta = Data.getSLEB128(&Offset);
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing LineTable MaxDelta", Offset);
- int64_t MaxDelta = Data.getSLEB128(&Offset);
- int64_t LineRange = MaxDelta - MinDelta + 1;
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing LineTable FirstLine", Offset);
- const uint32_t FirstLine = (uint32_t)Data.getULEB128(&Offset);
- LineEntry Row(BaseAddr, 1, FirstLine);
- bool Done = false;
- while (!Done) {
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": EOF found before EndSequence", Offset);
- uint8_t Op = Data.getU8(&Offset);
- switch (Op) {
- case EndSequence:
- Done = true;
- break;
- case SetFile:
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": EOF found before SetFile value",
- Offset);
- Row.File = (uint32_t)Data.getULEB128(&Offset);
- break;
- case AdvancePC:
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": EOF found before AdvancePC value",
- Offset);
- Row.Addr += Data.getULEB128(&Offset);
- // If the function callback returns false, we stop parsing.
- if (Callback(Row) == false)
- return Error::success();
- break;
- case AdvanceLine:
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": EOF found before AdvanceLine value",
- Offset);
- Row.Line += Data.getSLEB128(&Offset);
- break;
- default: {
- // A byte that contains both address and line increment.
- uint8_t AdjustedOp = Op - FirstSpecial;
- int64_t LineDelta = MinDelta + (AdjustedOp % LineRange);
- uint64_t AddrDelta = (AdjustedOp / LineRange);
- Row.Line += LineDelta;
- Row.Addr += AddrDelta;
- // If the function callback returns false, we stop parsing.
- if (Callback(Row) == false)
- return Error::success();
- break;
- }
- }
- }
- return Error::success();
- }
- llvm::Error LineTable::encode(FileWriter &Out, uint64_t BaseAddr) const {
- // Users must verify the LineTable is valid prior to calling this funtion.
- // We don't want to emit any LineTable objects if they are not valid since
- // it will waste space in the GSYM file.
- if (!isValid())
- return createStringError(std::errc::invalid_argument,
- "attempted to encode invalid LineTable object");
- int64_t MinLineDelta = INT64_MAX;
- int64_t MaxLineDelta = INT64_MIN;
- std::vector<DeltaInfo> DeltaInfos;
- if (Lines.size() == 1) {
- MinLineDelta = 0;
- MaxLineDelta = 0;
- } else {
- int64_t PrevLine = 1;
- bool First = true;
- for (const auto &line_entry : Lines) {
- if (First)
- First = false;
- else {
- int64_t LineDelta = (int64_t)line_entry.Line - PrevLine;
- auto End = DeltaInfos.end();
- auto Pos = std::lower_bound(DeltaInfos.begin(), End, LineDelta);
- if (Pos != End && Pos->Delta == LineDelta)
- ++Pos->Count;
- else
- DeltaInfos.insert(Pos, DeltaInfo(LineDelta, 1));
- if (LineDelta < MinLineDelta)
- MinLineDelta = LineDelta;
- if (LineDelta > MaxLineDelta)
- MaxLineDelta = LineDelta;
- }
- PrevLine = (int64_t)line_entry.Line;
- }
- assert(MinLineDelta <= MaxLineDelta);
- }
- // Set the min and max line delta intelligently based on the counts of
- // the line deltas. if our range is too large.
- const int64_t MaxLineRange = 14;
- if (MaxLineDelta - MinLineDelta > MaxLineRange) {
- uint32_t BestIndex = 0;
- uint32_t BestEndIndex = 0;
- uint32_t BestCount = 0;
- const size_t NumDeltaInfos = DeltaInfos.size();
- for (uint32_t I = 0; I < NumDeltaInfos; ++I) {
- const int64_t FirstDelta = DeltaInfos[I].Delta;
- uint32_t CurrCount = 0;
- uint32_t J;
- for (J = I; J < NumDeltaInfos; ++J) {
- auto LineRange = DeltaInfos[J].Delta - FirstDelta;
- if (LineRange > MaxLineRange)
- break;
- CurrCount += DeltaInfos[J].Count;
- }
- if (CurrCount > BestCount) {
- BestIndex = I;
- BestEndIndex = J - 1;
- BestCount = CurrCount;
- }
- }
- MinLineDelta = DeltaInfos[BestIndex].Delta;
- MaxLineDelta = DeltaInfos[BestEndIndex].Delta;
- }
- if (MinLineDelta == MaxLineDelta && MinLineDelta > 0 &&
- MinLineDelta < MaxLineRange)
- MinLineDelta = 0;
- assert(MinLineDelta <= MaxLineDelta);
- // Initialize the line entry state as a starting point. All line entries
- // will be deltas from this.
- LineEntry Prev(BaseAddr, 1, Lines.front().Line);
- // Write out the min and max line delta as signed LEB128.
- Out.writeSLEB(MinLineDelta);
- Out.writeSLEB(MaxLineDelta);
- // Write out the starting line number as a unsigned LEB128.
- Out.writeULEB(Prev.Line);
- for (const auto &Curr : Lines) {
- if (Curr.Addr < BaseAddr)
- return createStringError(std::errc::invalid_argument,
- "LineEntry has address 0x%" PRIx64 " which is "
- "less than the function start address 0x%"
- PRIx64, Curr.Addr, BaseAddr);
- if (Curr.Addr < Prev.Addr)
- return createStringError(std::errc::invalid_argument,
- "LineEntry in LineTable not in ascending order");
- const uint64_t AddrDelta = Curr.Addr - Prev.Addr;
- int64_t LineDelta = 0;
- if (Curr.Line > Prev.Line)
- LineDelta = Curr.Line - Prev.Line;
- else if (Prev.Line > Curr.Line)
- LineDelta = -((int32_t)(Prev.Line - Curr.Line));
- // Set the file if it doesn't match the current one.
- if (Curr.File != Prev.File) {
- Out.writeU8(SetFile);
- Out.writeULEB(Curr.File);
- }
- uint8_t SpecialOp;
- if (encodeSpecial(MinLineDelta, MaxLineDelta, LineDelta, AddrDelta,
- SpecialOp)) {
- // Advance the PC and line and push a row.
- Out.writeU8(SpecialOp);
- } else {
- // We can't encode the address delta and line delta into
- // a single special opcode, we must do them separately.
- // Advance the line.
- if (LineDelta != 0) {
- Out.writeU8(AdvanceLine);
- Out.writeSLEB(LineDelta);
- }
- // Advance the PC and push a row.
- Out.writeU8(AdvancePC);
- Out.writeULEB(AddrDelta);
- }
- Prev = Curr;
- }
- Out.writeU8(EndSequence);
- return Error::success();
- }
- // Parse all line table entries into the "LineTable" vector. We can
- // cache the results of this if needed, or we can call LineTable::lookup()
- // below.
- llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data,
- uint64_t BaseAddr) {
- LineTable LT;
- llvm::Error Err = parse(Data, BaseAddr, [&](const LineEntry &Row) -> bool {
- LT.Lines.push_back(Row);
- return true; // Keep parsing by returning true.
- });
- if (Err)
- return std::move(Err);
- return LT;
- }
- // Parse the line table on the fly and find the row we are looking for.
- // We will need to determine if we need to cache the line table by calling
- // LineTable::parseAllEntries(...) or just call this function each time.
- // There is a CPU vs memory tradeoff we will need to determined.
- Expected<LineEntry> LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) {
- LineEntry Result;
- llvm::Error Err = parse(Data, BaseAddr,
- [Addr, &Result](const LineEntry &Row) -> bool {
- if (Addr < Row.Addr)
- return false; // Stop parsing, result contains the line table row!
- Result = Row;
- if (Addr == Row.Addr) {
- // Stop parsing, this is the row we are looking for since the address
- // matches.
- return false;
- }
- return true; // Keep parsing till we find the right row.
- });
- if (Err)
- return std::move(Err);
- if (Result.isValid())
- return Result;
- return createStringError(std::errc::invalid_argument,
- "address 0x%" PRIx64 " is not in the line table",
- Addr);
- }
- raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable <) {
- for (const auto &LineEntry : LT)
- OS << LineEntry << '\n';
- return OS;
- }
|