314 lines
12 KiB
C
314 lines
12 KiB
C
|
//===- GsymReader.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_GSYMREADER_H
|
||
|
#define LLVM_DEBUGINFO_GSYM_GSYMREADER_H
|
||
|
|
||
|
|
||
|
#include "llvm/ADT/ArrayRef.h"
|
||
|
#include "llvm/DebugInfo/GSYM/FileEntry.h"
|
||
|
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
|
||
|
#include "llvm/DebugInfo/GSYM/Header.h"
|
||
|
#include "llvm/DebugInfo/GSYM/LineEntry.h"
|
||
|
#include "llvm/DebugInfo/GSYM/StringTable.h"
|
||
|
#include "llvm/Support/DataExtractor.h"
|
||
|
#include "llvm/Support/Endian.h"
|
||
|
#include "llvm/Support/ErrorOr.h"
|
||
|
|
||
|
#include <inttypes.h>
|
||
|
#include <memory>
|
||
|
#include <stdint.h>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
|
||
|
namespace llvm {
|
||
|
class MemoryBuffer;
|
||
|
class raw_ostream;
|
||
|
|
||
|
namespace gsym {
|
||
|
|
||
|
/// GsymReader is used to read GSYM data from a file or buffer.
|
||
|
///
|
||
|
/// This class is optimized for very quick lookups when the endianness matches
|
||
|
/// the host system. The Header, address table, address info offsets, and file
|
||
|
/// table is designed to be mmap'ed as read only into memory and used without
|
||
|
/// any parsing needed. If the endianness doesn't match, we swap these objects
|
||
|
/// and tables into GsymReader::SwappedData and then point our header and
|
||
|
/// ArrayRefs to this swapped internal data.
|
||
|
///
|
||
|
/// GsymReader objects must use one of the static functions to create an
|
||
|
/// instance: GsymReader::openFile(...) and GsymReader::copyBuffer(...).
|
||
|
|
||
|
class GsymReader {
|
||
|
GsymReader(std::unique_ptr<MemoryBuffer> Buffer);
|
||
|
llvm::Error parse();
|
||
|
|
||
|
std::unique_ptr<MemoryBuffer> MemBuffer;
|
||
|
StringRef GsymBytes;
|
||
|
llvm::support::endianness Endian;
|
||
|
const Header *Hdr = nullptr;
|
||
|
ArrayRef<uint8_t> AddrOffsets;
|
||
|
ArrayRef<uint32_t> AddrInfoOffsets;
|
||
|
ArrayRef<FileEntry> Files;
|
||
|
StringTable StrTab;
|
||
|
/// When the GSYM file's endianness doesn't match the host system then
|
||
|
/// we must decode all data structures that need to be swapped into
|
||
|
/// local storage and set point the ArrayRef objects above to these swapped
|
||
|
/// copies.
|
||
|
struct SwappedData {
|
||
|
Header Hdr;
|
||
|
std::vector<uint8_t> AddrOffsets;
|
||
|
std::vector<uint32_t> AddrInfoOffsets;
|
||
|
std::vector<FileEntry> Files;
|
||
|
};
|
||
|
std::unique_ptr<SwappedData> Swap;
|
||
|
|
||
|
public:
|
||
|
GsymReader(GsymReader &&RHS);
|
||
|
~GsymReader();
|
||
|
|
||
|
/// Construct a GsymReader from a file on disk.
|
||
|
///
|
||
|
/// \param Path The file path the GSYM file to read.
|
||
|
/// \returns An expected GsymReader that contains the object or an error
|
||
|
/// object that indicates reason for failing to read the GSYM.
|
||
|
static llvm::Expected<GsymReader> openFile(StringRef Path);
|
||
|
|
||
|
/// Construct a GsymReader from a buffer.
|
||
|
///
|
||
|
/// \param Bytes A set of bytes that will be copied and owned by the
|
||
|
/// returned object on success.
|
||
|
/// \returns An expected GsymReader that contains the object or an error
|
||
|
/// object that indicates reason for failing to read the GSYM.
|
||
|
static llvm::Expected<GsymReader> copyBuffer(StringRef Bytes);
|
||
|
|
||
|
/// Access the GSYM header.
|
||
|
/// \returns A native endian version of the GSYM header.
|
||
|
const Header &getHeader() const;
|
||
|
|
||
|
/// Get the full function info for an address.
|
||
|
///
|
||
|
/// This should be called when a client will store a copy of the complete
|
||
|
/// FunctionInfo for a given address. For one off lookups, use the lookup()
|
||
|
/// function below.
|
||
|
///
|
||
|
/// Symbolication server processes might want to parse the entire function
|
||
|
/// info for a given address and cache it if the process stays around to
|
||
|
/// service many symbolication addresses, like for parsing profiling
|
||
|
/// information.
|
||
|
///
|
||
|
/// \param Addr A virtual address from the orignal object file to lookup.
|
||
|
///
|
||
|
/// \returns An expected FunctionInfo that contains the function info object
|
||
|
/// or an error object that indicates reason for failing to lookup the
|
||
|
/// address.
|
||
|
llvm::Expected<FunctionInfo> getFunctionInfo(uint64_t Addr) const;
|
||
|
|
||
|
/// Lookup an address in the a GSYM.
|
||
|
///
|
||
|
/// Lookup just the information needed for a specific address \a Addr. This
|
||
|
/// function is faster that calling getFunctionInfo() as it will only return
|
||
|
/// information that pertains to \a Addr and allows the parsing to skip any
|
||
|
/// extra information encoded for other addresses. For example the line table
|
||
|
/// parsing can stop when a matching LineEntry has been fouhnd, and the
|
||
|
/// InlineInfo can stop parsing early once a match has been found and also
|
||
|
/// skip information that doesn't match. This avoids memory allocations and
|
||
|
/// is much faster for lookups.
|
||
|
///
|
||
|
/// \param Addr A virtual address from the orignal object file to lookup.
|
||
|
/// \returns An expected LookupResult that contains only the information
|
||
|
/// needed for the current address, or an error object that indicates reason
|
||
|
/// for failing to lookup the address.
|
||
|
llvm::Expected<LookupResult> lookup(uint64_t Addr) const;
|
||
|
|
||
|
/// Get a string from the string table.
|
||
|
///
|
||
|
/// \param Offset The string table offset for the string to retrieve.
|
||
|
/// \returns The string from the strin table.
|
||
|
StringRef getString(uint32_t Offset) const { return StrTab[Offset]; }
|
||
|
|
||
|
/// Get the a file entry for the suppplied file index.
|
||
|
///
|
||
|
/// Used to convert any file indexes in the FunctionInfo data back into
|
||
|
/// files. This function can be used for iteration, but is more commonly used
|
||
|
/// for random access when doing lookups.
|
||
|
///
|
||
|
/// \param Index An index into the file table.
|
||
|
/// \returns An optional FileInfo that will be valid if the file index is
|
||
|
/// valid, or llvm::None if the file index is out of bounds,
|
||
|
Optional<FileEntry> getFile(uint32_t Index) const {
|
||
|
if (Index < Files.size())
|
||
|
return Files[Index];
|
||
|
return llvm::None;
|
||
|
}
|
||
|
|
||
|
/// Dump the entire Gsym data contained in this object.
|
||
|
///
|
||
|
/// \param OS The output stream to dump to.
|
||
|
void dump(raw_ostream &OS);
|
||
|
|
||
|
/// Dump a FunctionInfo object.
|
||
|
///
|
||
|
/// This function will convert any string table indexes and file indexes
|
||
|
/// into human readable format.
|
||
|
///
|
||
|
/// \param OS The output stream to dump to.
|
||
|
///
|
||
|
/// \param FI The object to dump.
|
||
|
void dump(raw_ostream &OS, const FunctionInfo &FI);
|
||
|
|
||
|
/// Dump a LineTable object.
|
||
|
///
|
||
|
/// This function will convert any string table indexes and file indexes
|
||
|
/// into human readable format.
|
||
|
///
|
||
|
///
|
||
|
/// \param OS The output stream to dump to.
|
||
|
///
|
||
|
/// \param LT The object to dump.
|
||
|
void dump(raw_ostream &OS, const LineTable <);
|
||
|
|
||
|
/// Dump a InlineInfo object.
|
||
|
///
|
||
|
/// This function will convert any string table indexes and file indexes
|
||
|
/// into human readable format.
|
||
|
///
|
||
|
/// \param OS The output stream to dump to.
|
||
|
///
|
||
|
/// \param II The object to dump.
|
||
|
///
|
||
|
/// \param Indent The indentation as number of spaces. Used for recurive
|
||
|
/// dumping.
|
||
|
void dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent = 0);
|
||
|
|
||
|
/// Dump a FileEntry object.
|
||
|
///
|
||
|
/// This function will convert any string table indexes into human readable
|
||
|
/// format.
|
||
|
///
|
||
|
/// \param OS The output stream to dump to.
|
||
|
///
|
||
|
/// \param FE The object to dump.
|
||
|
void dump(raw_ostream &OS, Optional<FileEntry> FE);
|
||
|
|
||
|
/// Get the number of addresses in this Gsym file.
|
||
|
uint32_t getNumAddresses() const {
|
||
|
return Hdr->NumAddresses;
|
||
|
}
|
||
|
|
||
|
/// Gets an address from the address table.
|
||
|
///
|
||
|
/// Addresses are stored as offsets frrom the gsym::Header::BaseAddress.
|
||
|
///
|
||
|
/// \param Index A index into the address table.
|
||
|
/// \returns A resolved virtual address for adddress in the address table
|
||
|
/// or llvm::None if Index is out of bounds.
|
||
|
Optional<uint64_t> getAddress(size_t Index) const;
|
||
|
|
||
|
protected:
|
||
|
|
||
|
/// Get an appropriate address info offsets array.
|
||
|
///
|
||
|
/// The address table in the GSYM file is stored as array of 1, 2, 4 or 8
|
||
|
/// byte offsets from the The gsym::Header::BaseAddress. The table is stored
|
||
|
/// internally as a array of bytes that are in the correct endianness. When
|
||
|
/// we access this table we must get an array that matches those sizes. This
|
||
|
/// templatized helper function is used when accessing address offsets in the
|
||
|
/// AddrOffsets member variable.
|
||
|
///
|
||
|
/// \returns An ArrayRef of an appropriate address offset size.
|
||
|
template <class T> ArrayRef<T>
|
||
|
getAddrOffsets() const {
|
||
|
return ArrayRef<T>(reinterpret_cast<const T *>(AddrOffsets.data()),
|
||
|
AddrOffsets.size()/sizeof(T));
|
||
|
}
|
||
|
|
||
|
/// Get an appropriate address from the address table.
|
||
|
///
|
||
|
/// The address table in the GSYM file is stored as array of 1, 2, 4 or 8
|
||
|
/// byte address offsets from the The gsym::Header::BaseAddress. The table is
|
||
|
/// stored internally as a array of bytes that are in the correct endianness.
|
||
|
/// In order to extract an address from the address table we must access the
|
||
|
/// address offset using the correct size and then add it to the BaseAddress
|
||
|
/// in the header.
|
||
|
///
|
||
|
/// \param Index An index into the AddrOffsets array.
|
||
|
/// \returns An virtual address that matches the original object file for the
|
||
|
/// address as the specified index, or llvm::None if Index is out of bounds.
|
||
|
template <class T> Optional<uint64_t>
|
||
|
addressForIndex(size_t Index) const {
|
||
|
ArrayRef<T> AIO = getAddrOffsets<T>();
|
||
|
if (Index < AIO.size())
|
||
|
return AIO[Index] + Hdr->BaseAddress;
|
||
|
return llvm::None;
|
||
|
}
|
||
|
/// Lookup an address offset in the AddrOffsets table.
|
||
|
///
|
||
|
/// Given an address offset, look it up using a binary search of the
|
||
|
/// AddrOffsets table.
|
||
|
///
|
||
|
/// \param AddrOffset An address offset, that has already been computed by
|
||
|
/// subtracting the gsym::Header::BaseAddress.
|
||
|
/// \returns The matching address offset index. This index will be used to
|
||
|
/// extract the FunctionInfo data's offset from the AddrInfoOffsets array.
|
||
|
template <class T>
|
||
|
llvm::Optional<uint64_t> getAddressOffsetIndex(const uint64_t AddrOffset) const {
|
||
|
ArrayRef<T> AIO = getAddrOffsets<T>();
|
||
|
const auto Begin = AIO.begin();
|
||
|
const auto End = AIO.end();
|
||
|
auto Iter = std::lower_bound(Begin, End, AddrOffset);
|
||
|
// Watch for addresses that fall between the gsym::Header::BaseAddress and
|
||
|
// the first address offset.
|
||
|
if (Iter == Begin && AddrOffset < *Begin)
|
||
|
return llvm::None;
|
||
|
if (Iter == End || AddrOffset < *Iter)
|
||
|
--Iter;
|
||
|
return std::distance(Begin, Iter);
|
||
|
}
|
||
|
|
||
|
/// Create a GSYM from a memory buffer.
|
||
|
///
|
||
|
/// Called by both openFile() and copyBuffer(), this function does all of the
|
||
|
/// work of parsing the GSYM file and returning an error.
|
||
|
///
|
||
|
/// \param MemBuffer A memory buffer that will transfer ownership into the
|
||
|
/// GsymReader.
|
||
|
/// \returns An expected GsymReader that contains the object or an error
|
||
|
/// object that indicates reason for failing to read the GSYM.
|
||
|
static llvm::Expected<llvm::gsym::GsymReader>
|
||
|
create(std::unique_ptr<MemoryBuffer> &MemBuffer);
|
||
|
|
||
|
|
||
|
/// Given an address, find the address index.
|
||
|
///
|
||
|
/// Binary search the address table and find the matching address index.
|
||
|
///
|
||
|
/// \param Addr A virtual address that matches the original object file
|
||
|
/// to lookup.
|
||
|
/// \returns An index into the address table. This index can be used to
|
||
|
/// extract the FunctionInfo data's offset from the AddrInfoOffsets array.
|
||
|
/// Returns an error if the address isn't in the GSYM with details of why.
|
||
|
Expected<uint64_t> getAddressIndex(const uint64_t Addr) const;
|
||
|
|
||
|
/// Given an address index, get the offset for the FunctionInfo.
|
||
|
///
|
||
|
/// Looking up an address is done by finding the corresponding address
|
||
|
/// index for the address. This index is then used to get the offset of the
|
||
|
/// FunctionInfo data that we will decode using this function.
|
||
|
///
|
||
|
/// \param Index An index into the address table.
|
||
|
/// \returns An optional GSYM data offset for the offset of the FunctionInfo
|
||
|
/// that needs to be decoded.
|
||
|
Optional<uint64_t> getAddressInfoOffset(size_t Index) const;
|
||
|
};
|
||
|
|
||
|
} // namespace gsym
|
||
|
} // namespace llvm
|
||
|
|
||
|
#endif // #ifndef LLVM_DEBUGINFO_GSYM_GSYMREADER_H
|