[llvm] r371657 - Add a LineTable class to GSYM and test it.
Greg Clayton via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 11 13:51:03 PDT 2019
Author: gclayton
Date: Wed Sep 11 13:51:03 2019
New Revision: 371657
URL: http://llvm.org/viewvc/llvm-project?rev=371657&view=rev
Log:
Add a LineTable class to GSYM and test it.
This patch adds the ability to create a gsym::LineTable object, populate it, encode and decode it and test all functionality.
The full format of the LineTable encoding is specified in the header file LineTable.h.
Differential Revision: https://reviews.llvm.org/D66602
Added:
llvm/trunk/include/llvm/DebugInfo/GSYM/LineTable.h
llvm/trunk/lib/DebugInfo/GSYM/LineTable.cpp
Modified:
llvm/trunk/include/llvm/DebugInfo/GSYM/FunctionInfo.h
llvm/trunk/lib/DebugInfo/GSYM/CMakeLists.txt
llvm/trunk/lib/DebugInfo/GSYM/FunctionInfo.cpp
llvm/trunk/unittests/DebugInfo/GSYM/GSYMTest.cpp
Modified: llvm/trunk/include/llvm/DebugInfo/GSYM/FunctionInfo.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/GSYM/FunctionInfo.h?rev=371657&r1=371656&r2=371657&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/GSYM/FunctionInfo.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/GSYM/FunctionInfo.h Wed Sep 11 13:51:03 2019
@@ -9,8 +9,9 @@
#ifndef LLVM_DEBUGINFO_GSYM_FUNCTIONINFO_H
#define LLVM_DEBUGINFO_GSYM_FUNCTIONINFO_H
+#include "llvm/ADT/Optional.h"
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
-#include "llvm/DebugInfo/GSYM/LineEntry.h"
+#include "llvm/DebugInfo/GSYM/LineTable.h"
#include "llvm/DebugInfo/GSYM/Range.h"
#include "llvm/DebugInfo/GSYM/StringTable.h"
#include <tuple>
@@ -32,8 +33,8 @@ namespace gsym {
struct FunctionInfo {
AddressRange Range;
uint32_t Name; ///< String table offset in the string table.
- std::vector<gsym::LineEntry> Lines;
- InlineInfo Inline;
+ llvm::Optional<LineTable> LineTable;
+ llvm::Optional<InlineInfo> Inline;
FunctionInfo(uint64_t Addr = 0, uint64_t Size = 0, uint32_t N = 0)
: Range(Addr, Addr + Size), Name(N) {}
@@ -43,7 +44,7 @@ struct FunctionInfo {
/// converting information from a symbol table and from debug info, we
/// might end up with multiple FunctionInfo objects for the same range
/// and we need to be able to tell which one is the better object to use.
- return !Lines.empty() || Inline.isValid();
+ return LineTable.hasValue() || Inline.hasValue();
}
bool isValid() const {
@@ -65,14 +66,14 @@ struct FunctionInfo {
void clear() {
Range = {0, 0};
Name = 0;
- Lines.clear();
- Inline.clear();
+ LineTable = llvm::None;
+ Inline = llvm::None;
}
};
inline bool operator==(const FunctionInfo &LHS, const FunctionInfo &RHS) {
return LHS.Range == RHS.Range && LHS.Name == RHS.Name &&
- LHS.Lines == RHS.Lines && LHS.Inline == RHS.Inline;
+ LHS.LineTable == RHS.LineTable && LHS.Inline == RHS.Inline;
}
inline bool operator!=(const FunctionInfo &LHS, const FunctionInfo &RHS) {
return !(LHS == RHS);
@@ -88,14 +89,10 @@ inline bool operator<(const FunctionInfo
return LHS.Range < RHS.Range;
// Then sort by inline
- if (LHS.Inline.isValid() != RHS.Inline.isValid())
- return RHS.Inline.isValid();
+ if (LHS.Inline.hasValue() != RHS.Inline.hasValue())
+ return RHS.Inline.hasValue();
- // If the number of lines is the same, then compare line table entries
- if (LHS.Lines.size() == RHS.Lines.size())
- return LHS.Lines < RHS.Lines;
- // Then sort by number of line table entries (more is better)
- return LHS.Lines.size() < RHS.Lines.size();
+ return LHS.LineTable < RHS.LineTable;
}
raw_ostream &operator<<(raw_ostream &OS, const FunctionInfo &R);
Added: llvm/trunk/include/llvm/DebugInfo/GSYM/LineTable.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/GSYM/LineTable.h?rev=371657&view=auto
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/GSYM/LineTable.h (added)
+++ llvm/trunk/include/llvm/DebugInfo/GSYM/LineTable.h Wed Sep 11 13:51:03 2019
@@ -0,0 +1,198 @@
+//===- LineTable.h ----------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_GSYM_LINETABLE_H
+#define LLVM_DEBUGINFO_GSYM_LINETABLE_H
+
+#include "llvm/DebugInfo/GSYM/LineEntry.h"
+#include "llvm/Support/Error.h"
+#include <cstdint>
+#include <vector>
+
+namespace llvm {
+namespace gsym {
+
+struct FunctionInfo;
+class FileWriter;
+
+/// LineTable class contains deserialized versions of line tables for each
+/// function's address ranges.
+///
+/// When saved to disk, the line table is encoded using a modified version of
+/// the DWARF line tables that only tracks address to source file and line.
+///
+/// ENCODING
+///
+/// The line table starts with a small prolog that contains the following
+/// values:
+///
+/// ENCODING NAME DESCRIPTION
+/// ======== =========== ====================================================
+/// SLEB MinDelta The min line delta for special opcodes that advance
+/// the address and line number.
+/// SLEB MaxDelta The max line delta for single byte opcodes that
+/// advance the address and line number.
+/// ULEB FirstLine The value of the first source line number to
+/// initialize the LineEntry with.
+///
+/// Once these prolog items are read, we initialize a LineEntry struct with
+/// the start address of the function from the FunctionInfo's address range,
+/// a default file index of 1, and the line number set to "FirstLine" from
+/// the prolog above:
+///
+/// LineEntry Row(BaseAddr, 1, FirstLine);
+///
+/// The line table state machine is now initialized and ready to be parsed.
+/// The stream that follows this encodes the line entries in a compact
+/// form. Some opcodes cause "Row" to be modified and some opcodes may also
+/// push "Row" onto the end of the "LineTable.Lines" vector. The end result
+/// is a vector of LineEntry structs that is sorted in ascending address
+/// order.
+///
+/// NORMAL OPCODES
+///
+/// The opcodes 0 through 3 are normal in opcodes. Their encoding and
+/// descriptions are listed below:
+///
+/// ENCODING ENUMERATION VALUE DESCRIPTION
+/// ======== ================ ===== ========================================
+/// LTOC_EndSequence 0x00 Parsing is done.
+/// ULEB LTOC_SetFile 0x01 Row.File = ULEB
+/// ULEB LTOC_AdvancePC 0x02 Row.Addr += ULEB, push "Row".
+/// SLEB LTOC_AdvanceLine 0x03 Row.Line += SLEB
+/// LTOC_FirstSpecial 0x04 First special opcode (see SPECIAL
+/// OPCODES below).
+///
+/// SPECIAL OPCODES
+///
+/// Opcodes LTOC_FirstSpecial through 255 are special opcodes that always
+/// increment both the Row.Addr and Row.Line and push "Row" onto the
+/// LineEntry.Lines array. They do this by using some of the bits to
+/// increment/decrement the source line number, and some of the bits to
+/// increment the address. Line numbers can go up or down when making line
+/// tables, where addresses always only increase since line tables are sorted
+/// by address.
+///
+/// In order to calculate the amount to increment the line and address for
+/// these special opcodes, we calculate the number of values reserved for the
+/// line increment/decrement using the "MinDelta" and "MaxDelta" from the
+/// prolog:
+///
+/// const int64_t LineRange = MaxDelta - MinDelta + 1;
+///
+/// Then we can adjust the opcode to not include any of the normal opcodes:
+///
+/// const uint8_t AdjustedOp = Opcode - LTOC_FirstSpecial;
+///
+/// And we can calculate the line offset, and address offset:
+///
+/// const int64_t LineDelta = MinDelta + (AdjustedOp % LineRange);
+/// const uint64_t AddrDelta = (AdjustedOp / LineRange);
+///
+/// And use these to modify our "Row":
+///
+/// Row.Line += LineDelta;
+/// Row.Addr += AddrDelta;
+///
+/// And push a row onto the line table:
+///
+/// Lines.push_back(Row);
+///
+/// This is verify similar to the way that DWARF encodes its line tables. The
+/// only difference is the DWARF line tables have more normal opcodes and the
+/// "Row" contains more members, like source column number, bools for end of
+/// prologue, beginnging of epilogue, is statement and many others. There are
+/// also more complex rules that happen for the extra normal opcodes. By
+/// leaving these extra opcodes out, we leave more bits for the special
+/// opcodes that allows us to encode line tables in fewer bytes than standard
+/// DWARF encodings.
+///
+/// Opcodes that will push "Row" onto the LineEntry.Lines include the
+/// LTOC_AdvancePC opcode and all special opcodes. All other opcodes
+/// only modify the current "Row", or cause the line table to end.
+class LineTable {
+ typedef std::vector<gsym::LineEntry> Collection;
+ Collection Lines; ///< All line entries in the line table.
+public:
+ static LineEntry lookup(DataExtractor &Data, uint64_t BaseAddr,
+ uint64_t Addr);
+
+ /// Decode an LineTable object from a binary data stream.
+ ///
+ /// \param Data The binary stream to read the data from. This object must
+ /// have the data for the LineTable object starting at offset zero. The data
+ /// can contain more data than needed.
+ ///
+ /// \param BaseAddr The base address to use when decoding the line table.
+ /// This will be the FunctionInfo's start address and will be used to
+ /// initialize the line table row prior to parsing any opcodes.
+ ///
+ /// \returns An LineTable or an error describing the issue that was
+ /// encountered during decoding.
+ static llvm::Expected<LineTable> decode(DataExtractor &Data,
+ uint64_t BaseAddr);
+ /// Encode this LineTable object into FileWriter stream.
+ ///
+ /// \param O The binary stream to write the data to at the current file
+ /// position.
+ ///
+ /// \param BaseAddr The base address to use when decoding the line table.
+ /// This will be the FunctionInfo's start address.
+ ///
+ /// \returns An error object that indicates success or failure or the
+ /// encoding process.
+ llvm::Error encode(FileWriter &O, uint64_t BaseAddr) const;
+ bool empty() const { return Lines.empty(); }
+ void clear() { Lines.clear(); }
+ void push(const LineEntry &LE) {
+ Lines.push_back(LE);
+ }
+ size_t isValid() const {
+ return !Lines.empty();
+ }
+ size_t size() const {
+ return Lines.size();
+ }
+ LineEntry &get(size_t i) {
+ assert(i < Lines.size());
+ return Lines[i];
+ }
+ const LineEntry &get(size_t i) const {
+ assert(i < Lines.size());
+ return Lines[i];
+ }
+ LineEntry &operator[](size_t i) {
+ return get(i);
+ }
+ const LineEntry &operator[](size_t i) const {
+ return get(i);
+ }
+ bool operator==(const LineTable &RHS) const {
+ return Lines == RHS.Lines;
+ }
+ bool operator!=(const LineTable &RHS) const {
+ return Lines != RHS.Lines;
+ }
+ bool operator<(const LineTable &RHS) const {
+ const auto LHSSize = Lines.size();
+ const auto RHSSize = RHS.Lines.size();
+ if (LHSSize == RHSSize)
+ return Lines < RHS.Lines;
+ return LHSSize < RHSSize;
+ }
+ Collection::const_iterator begin() const { return Lines.begin(); }
+ Collection::const_iterator end() const { return Lines.end(); }
+
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const gsym::LineTable <);
+
+} // namespace gsym
+} // namespace llvm
+
+#endif // #ifndef LLVM_DEBUGINFO_GSYM_LINETABLE_H
Modified: llvm/trunk/lib/DebugInfo/GSYM/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/GSYM/CMakeLists.txt?rev=371657&r1=371656&r2=371657&view=diff
==============================================================================
--- llvm/trunk/lib/DebugInfo/GSYM/CMakeLists.txt (original)
+++ llvm/trunk/lib/DebugInfo/GSYM/CMakeLists.txt Wed Sep 11 13:51:03 2019
@@ -2,6 +2,7 @@ add_llvm_library(LLVMDebugInfoGSYM
FileWriter.cpp
FunctionInfo.cpp
InlineInfo.cpp
+ LineTable.cpp
Range.cpp
ADDITIONAL_HEADER_DIRS
Modified: llvm/trunk/lib/DebugInfo/GSYM/FunctionInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/GSYM/FunctionInfo.cpp?rev=371657&r1=371656&r2=371657&view=diff
==============================================================================
--- llvm/trunk/lib/DebugInfo/GSYM/FunctionInfo.cpp (original)
+++ llvm/trunk/lib/DebugInfo/GSYM/FunctionInfo.cpp Wed Sep 11 13:51:03 2019
@@ -14,9 +14,6 @@ using namespace gsym;
raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) {
OS << '[' << HEX64(FI.Range.Start) << '-' << HEX64(FI.Range.End) << "): "
- << "Name=" << HEX32(FI.Name) << '\n';
- for (const auto &Line : FI.Lines)
- OS << Line << '\n';
- OS << FI.Inline;
+ << "Name=" << HEX32(FI.Name) << '\n' << FI.LineTable << FI.Inline;
return OS;
}
Added: llvm/trunk/lib/DebugInfo/GSYM/LineTable.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/GSYM/LineTable.cpp?rev=371657&view=auto
==============================================================================
--- llvm/trunk/lib/DebugInfo/GSYM/LineTable.cpp (added)
+++ llvm/trunk/lib/DebugInfo/GSYM/LineTable.cpp Wed Sep 11 13:51:03 2019
@@ -0,0 +1,287 @@
+//===- 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 determine.
+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.
+ });
+ return Result;
+}
+
+raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable <) {
+ for (const auto &LineEntry : LT)
+ OS << LineEntry << '\n';
+ return OS;
+}
Modified: llvm/trunk/unittests/DebugInfo/GSYM/GSYMTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/DebugInfo/GSYM/GSYMTest.cpp?rev=371657&r1=371656&r2=371657&view=diff
==============================================================================
--- llvm/trunk/unittests/DebugInfo/GSYM/GSYMTest.cpp (original)
+++ llvm/trunk/unittests/DebugInfo/GSYM/GSYMTest.cpp Wed Sep 11 13:51:03 2019
@@ -74,7 +74,8 @@ TEST(GSYMTest, TestFunctionInfo) {
EXPECT_EQ(FI.size(), Size);
const uint32_t FileIdx = 1;
const uint32_t Line = 12;
- FI.Lines.push_back(LineEntry(StartAddr, FileIdx, Line));
+ FI.LineTable = LineTable();
+ FI.LineTable->push(LineEntry(StartAddr,FileIdx,Line));
EXPECT_TRUE(FI.hasRichInfo());
FI.clear();
EXPECT_FALSE(FI.isValid());
@@ -109,13 +110,15 @@ TEST(GSYMTest, TestFunctionInfo) {
// best version of a function info.
FunctionInfo FISymtab(StartAddr, Size, NameOffset);
FunctionInfo FIWithLines(StartAddr, Size, NameOffset);
- FIWithLines.Lines.push_back(LineEntry(StartAddr, FileIdx, Line));
+ FIWithLines.LineTable = LineTable();
+ FIWithLines.LineTable->push(LineEntry(StartAddr,FileIdx,Line));
// Test that a FunctionInfo with just a name and size is less than one
// that has name, size and any number of line table entries
EXPECT_LT(FISymtab, FIWithLines);
FunctionInfo FIWithLinesAndInline = FIWithLines;
- FIWithLinesAndInline.Inline.Ranges.insert(
+ FIWithLinesAndInline.Inline = InlineInfo();
+ FIWithLinesAndInline.Inline->Ranges.insert(
AddressRange(StartAddr, StartAddr + 0x10));
// Test that a FunctionInfo with name, size, and line entries is less than
// the same one with valid inline info
@@ -124,13 +127,13 @@ TEST(GSYMTest, TestFunctionInfo) {
// Test if we have an entry with lines and one with more lines for the same
// range, the ones with more lines is greater than the one with less.
FunctionInfo FIWithMoreLines = FIWithLines;
- FIWithMoreLines.Lines.push_back(LineEntry(StartAddr, FileIdx, Line + 5));
+ FIWithMoreLines.LineTable->push(LineEntry(StartAddr,FileIdx,Line+5));
EXPECT_LT(FIWithLines, FIWithMoreLines);
// Test that if we have the same number of lines we compare the line entries
- // in the FunctionInfo.Lines vector.
+ // in the FunctionInfo.LineTable.Lines vector.
FunctionInfo FIWithLinesWithHigherAddress = FIWithLines;
- FIWithLinesWithHigherAddress.Lines[0].Addr += 0x10;
+ FIWithLinesWithHigherAddress.LineTable->get(0).Addr += 0x10;
EXPECT_LT(FIWithLines, FIWithLinesWithHigherAddress);
}
@@ -633,3 +636,156 @@ TEST(GSYMTest, TestAddressRangesEncodeDe
Ranges.insert(AddressRange(0x1050, 0x1070));
TestAddressRangeEncodeDecodeHelper(Ranges, BaseAddr);
}
+
+static void TestLineTableHelper(llvm::support::endianness ByteOrder,
+ const LineTable <) {
+ SmallString<512> Str;
+ raw_svector_ostream OutStrm(Str);
+ FileWriter FW(OutStrm, ByteOrder);
+ const uint64_t BaseAddr = LT[0].Addr;
+ llvm::Error Err = LT.encode(FW, BaseAddr);
+ ASSERT_FALSE(Err);
+ std::string Bytes(OutStrm.str());
+ uint8_t AddressSize = 4;
+ DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
+ llvm::Expected<LineTable> Decoded = LineTable::decode(Data, BaseAddr);
+ // Make sure decoding succeeded.
+ ASSERT_TRUE((bool)Decoded);
+ // Make sure decoded object is the same as the one we encoded.
+ EXPECT_EQ(LT, Decoded.get());
+}
+
+TEST(GSYMTest, TestLineTable) {
+ const uint64_t StartAddr = 0x1000;
+ const uint32_t FileIdx = 1;
+ LineTable LT;
+ LineEntry Line0(StartAddr+0x000, FileIdx, 10);
+ LineEntry Line1(StartAddr+0x010, FileIdx, 11);
+ LineEntry Line2(StartAddr+0x100, FileIdx, 1000);
+ ASSERT_TRUE(LT.empty());
+ ASSERT_EQ(LT.size(), (size_t)0);
+ LT.push(Line0);
+ ASSERT_EQ(LT.size(), (size_t)1);
+ LT.push(Line1);
+ LT.push(Line2);
+ LT.push(LineEntry(StartAddr+0x120, FileIdx, 900));
+ LT.push(LineEntry(StartAddr+0x120, FileIdx, 2000));
+ LT.push(LineEntry(StartAddr+0x121, FileIdx, 2001));
+ LT.push(LineEntry(StartAddr+0x122, FileIdx, 2002));
+ LT.push(LineEntry(StartAddr+0x123, FileIdx, 2003));
+ ASSERT_FALSE(LT.empty());
+ ASSERT_EQ(LT.size(), (size_t)8);
+ // Test operator[].
+ ASSERT_EQ(LT[0], Line0);
+ ASSERT_EQ(LT[1], Line1);
+ ASSERT_EQ(LT[2], Line2);
+
+ // Test encoding and decoding line tables.
+ TestLineTableHelper(llvm::support::little, LT);
+ TestLineTableHelper(llvm::support::big, LT);
+
+ // Verify the clear method works as expected.
+ LT.clear();
+ ASSERT_TRUE(LT.empty());
+ ASSERT_EQ(LT.size(), (size_t)0);
+
+ LineTable LT1;
+ LineTable LT2;
+
+ // Test that two empty line tables are equal and neither are less than
+ // each other.
+ ASSERT_EQ(LT1, LT2);
+ ASSERT_FALSE(LT1 < LT2);
+ ASSERT_FALSE(LT2 < LT2);
+
+ // Test that a line table with less number of line entries is less than a
+ // line table with more line entries and that they are not equal.
+ LT2.push(Line0);
+ ASSERT_LT(LT1, LT2);
+ ASSERT_NE(LT1, LT2);
+
+ // Test that two line tables with the same entries are equal.
+ LT1.push(Line0);
+ ASSERT_EQ(LT1, LT2);
+ ASSERT_FALSE(LT1 < LT2);
+ ASSERT_FALSE(LT2 < LT2);
+}
+
+static void TestLineTableDecodeError(llvm::support::endianness ByteOrder,
+ std::string Bytes,
+ const uint64_t BaseAddr,
+ std::string ExpectedErrorMsg) {
+ uint8_t AddressSize = 4;
+ DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
+ llvm::Expected<LineTable> Decoded = LineTable::decode(Data, BaseAddr);
+ // Make sure decoding fails.
+ ASSERT_FALSE((bool)Decoded);
+ // Make sure decoded object is the same as the one we encoded.
+ checkError(ExpectedErrorMsg, Decoded.takeError());
+}
+
+TEST(GSYMTest, TestLineTableDecodeErrors) {
+ // Test decoding InlineInfo objects that ensure we report an appropriate
+ // error message.
+ const llvm::support::endianness ByteOrder = llvm::support::little;
+ SmallString<512> Str;
+ raw_svector_ostream OutStrm(Str);
+ FileWriter FW(OutStrm, ByteOrder);
+ const uint64_t BaseAddr = 0x100;
+ TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
+ "0x00000000: missing LineTable MinDelta");
+ FW.writeU8(1); // MinDelta (ULEB)
+ TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
+ "0x00000001: missing LineTable MaxDelta");
+ FW.writeU8(10); // MaxDelta (ULEB)
+ TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
+ "0x00000002: missing LineTable FirstLine");
+ FW.writeU8(20); // FirstLine (ULEB)
+ TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
+ "0x00000003: EOF found before EndSequence");
+ // Test a SetFile with the argument missing from the stream
+ FW.writeU8(1); // SetFile opcode (uint8_t)
+ TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
+ "0x00000004: EOF found before SetFile value");
+ FW.writeU8(5); // SetFile value as index (ULEB)
+ // Test a AdvancePC with the argument missing from the stream
+ FW.writeU8(2); // AdvancePC opcode (uint8_t)
+ TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
+ "0x00000006: EOF found before AdvancePC value");
+ FW.writeU8(20); // AdvancePC value as offset (ULEB)
+ // Test a AdvancePC with the argument missing from the stream
+ FW.writeU8(3); // AdvanceLine opcode (uint8_t)
+ TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
+ "0x00000008: EOF found before AdvanceLine value");
+ FW.writeU8(20); // AdvanceLine value as offset (LLEB)
+}
+
+TEST(GSYMTest, TestLineTableEncodeErrors) {
+ const uint64_t BaseAddr = 0x1000;
+ const uint32_t FileIdx = 1;
+ const llvm::support::endianness ByteOrder = llvm::support::little;
+ SmallString<512> Str;
+ raw_svector_ostream OutStrm(Str);
+ FileWriter FW(OutStrm, ByteOrder);
+ LineTable LT;
+ checkError("attempted to encode invalid LineTable object",
+ LT.encode(FW, BaseAddr));
+
+ // Try to encode a line table where a line entry has an address that is less
+ // than BaseAddr and verify we get an appropriate error.
+ LineEntry Line0(BaseAddr+0x000, FileIdx, 10);
+ LineEntry Line1(BaseAddr+0x010, FileIdx, 11);
+ LT.push(Line0);
+ LT.push(Line1);
+ checkError("LineEntry has address 0x1000 which is less than the function "
+ "start address 0x1010", LT.encode(FW, BaseAddr+0x10));
+ LT.clear();
+
+ // Try to encode a line table where a line entries has an address that is less
+ // than BaseAddr and verify we get an appropriate error.
+ LT.push(Line1);
+ LT.push(Line0);
+ checkError("LineEntry in LineTable not in ascending order",
+ LT.encode(FW, BaseAddr));
+ LT.clear();
+}
More information about the llvm-commits
mailing list