Skip to content

Commit

Permalink
Add a LineTable class to GSYM and test it.
Browse files Browse the repository at this point in the history
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



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@371657 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
clayborg committed Sep 11, 2019
1 parent e57ff88 commit 5447f44
Show file tree
Hide file tree
Showing 6 changed files with 660 additions and 24 deletions.
25 changes: 11 additions & 14 deletions include/llvm/DebugInfo/GSYM/FunctionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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) {}
Expand All @@ -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 {
Expand All @@ -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);
Expand All @@ -88,14 +89,10 @@ inline bool operator<(const FunctionInfo &LHS, const FunctionInfo &RHS) {
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);
Expand Down
198 changes: 198 additions & 0 deletions include/llvm/DebugInfo/GSYM/LineTable.h
Original file line number Diff line number Diff line change
@@ -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 &LT);

} // namespace gsym
} // namespace llvm

#endif // #ifndef LLVM_DEBUGINFO_GSYM_LINETABLE_H
1 change: 1 addition & 0 deletions lib/DebugInfo/GSYM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ add_llvm_library(LLVMDebugInfoGSYM
FileWriter.cpp
FunctionInfo.cpp
InlineInfo.cpp
LineTable.cpp
Range.cpp

ADDITIONAL_HEADER_DIRS
Expand Down
5 changes: 1 addition & 4 deletions lib/DebugInfo/GSYM/FunctionInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Loading

0 comments on commit 5447f44

Please sign in to comment.