Make GSYM 64 bit safe and add a new version 2 of the GSYM files (#190353)
# Motivation GSYM files are approaching the need for 64 bit offsets in the GSYM files. We also want to add more global data to GSYM files. Right now the GSYM file format is: ``` Header AddressOffsets AddressInfoOffsets FileTable StringTable FunctionInfos ``` The location of the `AddressOffsets`, `AddressInfoOffsets` and `FileTable` are always immediately following the Header. The `StringTable` is pointed to by the header and the header uses 32 bit integers for the string table file offset and file size. The `AddressInfoOffsets` are fixed at 32 bits as well. So with the current format, we can't have any string or function info with an offset >= 4G. # GSYM V2 design (64 bit safe and extensible) This new design increments the GSYM version to 2 and we are adding a new `GlobalInfoType` enum which allows us to specify the file offset and file size of all of the things in the global data table to be 64 bit safe. Everything is now in the global info data (listed below). The new design is extensible: new global info types can be added in the future, and the order that they appear in the file can be changed/optimized. * UUID (optional) * AddressOffsets table * AddressInfoOffsets table * File table * String table * FunctionInfo data We are also adding a new `StringTableEncoding` enum so that new string table encodings can be added in the future. GSYM V2 files can be produced by using the new `--oputput-version=2` option. For example: ``` llvm-gsymutil --convert my.dSYM -o my.gSYM --output-version=2 ``` # Tests **Unit tests**: Extended existing tests (`GSYMTests.cpp`) to cover both v1 and v2. Added new V2 tests (`GSYMV2Tests.cpp`). ``` ninja DebugInfoGSYMTests SupportTests unittests/DebugInfo/GSYM/DebugInfoGSYMTests unittests/Support/SupportTests --gtest_filter='*DataExtractor*' bin/llvm-lit \ ../llvm-project/llvm/test/tools/llvm-gsymutil/X86/elf-dwarf.yaml \ ../llvm-project/llvm/test/tools/llvm-gsymutil/X86/mach-dwarf.yaml ``` **Parity tests to V1 (manual)**: * All tests were conducted on a very large DSYM (9.24 GB) and the GSYMs generated from it (3.42~3.57 GB). * All correctness tests were conducted on both little-endian and big-endian machines. * **Data parity (on-par)**: The new gsymutil [generates the exact same GSYM v1 file as the baseline gsymutil](https://gist.github.com/royitaqi/746c15ec22725cf89a1f5c6d9fb396aa), both using a single thread. * **Performance parity/improvement (on-par; v2 convert is 2.8x faster)**: * **Parse + lookup**: The new gsymutil is [2% (GSYM v1 file) and 3% (GSYM v2 file) slower than the baseline gsymutil](https://gist.github.com/royitaqi/9789b92d63ae74a806c776f32a33e4fb) (1.56s vs. 1.52s). * **Convert**: The new gsymutil is [on par (GSYM v1 file) and 2.8x faster (GSYM v2 file) than the baseline gsymutil](https://gist.github.com/royitaqi/17aa69408bc5a3416fae9e192b1dc1ce) (102s vs. 288s). * **Memory footprint parity (on-par)**: * **Convert**: The new gsymutil uses the same amount of memory as the base line gsymutil (13 GB when converting a 2.52 GB DSYM; 42 GB when converting a 9.24 GB DSYM) since the memory peak is in `finalize()` which is before `encode()`. * **Segment correctness (on-par)**: The new gsymutil [generates segmented GSYM v2 files](https://gist.github.com/royitaqi/9bea5b3d50f13247d577ea91fbc6368a), whose content ([seg1](https://gist.github.com/royitaqi/5b95a793b548f2f44edca250d64366b4), [seg2](https://gist.github.com/royitaqi/f64730cbd1885ac9c0b9136e03e7742e), [seg3](https://gist.github.com/royitaqi/2ce585fae910c57784cafafe4fb4cdcb)) match that of a [single GSYM v2 file](https://gist.github.com/royitaqi/475499477b9dbb1dea81f5d653ec39bf).
This commit is contained in:
@@ -12,12 +12,12 @@
|
||||
#include "llvm/ADT/BitmaskEnum.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymTypes.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
class DataExtractor;
|
||||
class raw_ostream;
|
||||
|
||||
namespace yaml {
|
||||
@@ -27,6 +27,7 @@ struct FunctionsYAML;
|
||||
namespace gsym {
|
||||
class FileWriter;
|
||||
class GsymCreator;
|
||||
class GsymDataExtractor;
|
||||
struct FunctionInfo;
|
||||
struct CallSiteInfo {
|
||||
enum Flags : uint8_t {
|
||||
@@ -45,7 +46,7 @@ struct CallSiteInfo {
|
||||
uint64_t ReturnOffset = 0;
|
||||
|
||||
/// Offsets into the string table for function names regex patterns.
|
||||
std::vector<uint32_t> MatchRegex;
|
||||
std::vector<gsym_strp_t> MatchRegex;
|
||||
|
||||
/// Bitwise OR of CallSiteInfo::Flags values
|
||||
uint8_t Flags = CallSiteInfo::Flags::None;
|
||||
@@ -64,7 +65,7 @@ struct CallSiteInfo {
|
||||
/// \param Data The binary stream to read the data from.
|
||||
/// \param Offset The current offset within the data stream.
|
||||
/// \returns A CallSiteInfo or an error describing the issue.
|
||||
LLVM_ABI static llvm::Expected<CallSiteInfo> decode(DataExtractor &Data,
|
||||
LLVM_ABI static llvm::Expected<CallSiteInfo> decode(GsymDataExtractor &Data,
|
||||
uint64_t &Offset);
|
||||
|
||||
/// Encode this CallSiteInfo object into a FileWriter stream.
|
||||
@@ -82,7 +83,7 @@ struct CallSiteInfoCollection {
|
||||
/// \param Data The binary stream to read the data from.
|
||||
/// \returns A CallSiteInfoCollection or an error describing the issue.
|
||||
LLVM_ABI static llvm::Expected<CallSiteInfoCollection>
|
||||
decode(DataExtractor &Data);
|
||||
decode(GsymDataExtractor &Data);
|
||||
|
||||
/// Encode this CallSiteInfoCollection object into a FileWriter stream.
|
||||
///
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
#define HEX64(v) llvm::format_hex(v, 18)
|
||||
|
||||
namespace llvm {
|
||||
class DataExtractor;
|
||||
class raw_ostream;
|
||||
|
||||
namespace gsym {
|
||||
|
||||
class FileWriter;
|
||||
class GsymDataExtractor;
|
||||
|
||||
/// AddressRange objects are encoded and decoded to be relative to a base
|
||||
/// address. This will be the FunctionInfo's start address if the AddressRange
|
||||
@@ -37,7 +37,7 @@ class FileWriter;
|
||||
/// encoded addresses easy to relocate as we just need to relocate one base
|
||||
/// address.
|
||||
/// @{
|
||||
LLVM_ABI AddressRange decodeRange(DataExtractor &Data, uint64_t BaseAddr,
|
||||
LLVM_ABI AddressRange decodeRange(GsymDataExtractor &Data, uint64_t BaseAddr,
|
||||
uint64_t &Offset);
|
||||
LLVM_ABI void encodeRange(const AddressRange &Range, FileWriter &O,
|
||||
uint64_t BaseAddr);
|
||||
@@ -49,13 +49,13 @@ LLVM_ABI void encodeRange(const AddressRange &Range, FileWriter &O,
|
||||
/// \param Data The binary stream to read the data from.
|
||||
///
|
||||
/// \param Offset The byte offset within \a Data.
|
||||
LLVM_ABI void skipRange(DataExtractor &Data, uint64_t &Offset);
|
||||
LLVM_ABI void skipRange(GsymDataExtractor &Data, uint64_t &Offset);
|
||||
|
||||
/// Address ranges are decoded and encoded to be relative to a base address.
|
||||
/// See the AddressRange comment for the encode and decode methods for full
|
||||
/// details.
|
||||
/// @{
|
||||
LLVM_ABI void decodeRanges(AddressRanges &Ranges, DataExtractor &Data,
|
||||
LLVM_ABI void decodeRanges(AddressRanges &Ranges, GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr, uint64_t &Offset);
|
||||
LLVM_ABI void encodeRanges(const AddressRanges &Ranges, FileWriter &O,
|
||||
uint64_t BaseAddr);
|
||||
@@ -69,7 +69,7 @@ LLVM_ABI void encodeRanges(const AddressRanges &Ranges, FileWriter &O,
|
||||
/// \param Offset The byte offset within \a Data.
|
||||
///
|
||||
/// \returns The number of address ranges that were skipped.
|
||||
LLVM_ABI uint64_t skipRanges(DataExtractor &Data, uint64_t &Offset);
|
||||
LLVM_ABI uint64_t skipRanges(GsymDataExtractor &Data, uint64_t &Offset);
|
||||
|
||||
} // namespace gsym
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymTypes.h"
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -25,12 +26,18 @@ struct FileEntry {
|
||||
|
||||
/// Offsets in the string table.
|
||||
/// @{
|
||||
uint32_t Dir = 0;
|
||||
uint32_t Base = 0;
|
||||
gsym_strp_t Dir = 0;
|
||||
gsym_strp_t Base = 0;
|
||||
/// @}
|
||||
|
||||
FileEntry() = default;
|
||||
FileEntry(uint32_t D, uint32_t B) : Dir(D), Base(B) {}
|
||||
FileEntry(gsym_strp_t D, gsym_strp_t B) : Dir(D), Base(B) {}
|
||||
|
||||
/// Returns the on-disk encoded size of a FileEntry for the given string
|
||||
/// offset size. It's different from sizeof(FileEntry) because of padding.
|
||||
static constexpr uint64_t getEncodedSize(uint8_t StringOffsetSize) {
|
||||
return 2 * StringOffsetSize;
|
||||
}
|
||||
|
||||
// Implement operator== so that FileEntry can be used as key in
|
||||
// unordered containers.
|
||||
@@ -46,16 +53,17 @@ struct FileEntry {
|
||||
|
||||
template <> struct DenseMapInfo<gsym::FileEntry> {
|
||||
static inline gsym::FileEntry getEmptyKey() {
|
||||
uint32_t key = DenseMapInfo<uint32_t>::getEmptyKey();
|
||||
gsym::gsym_strp_t key = DenseMapInfo<gsym::gsym_strp_t>::getEmptyKey();
|
||||
return gsym::FileEntry(key, key);
|
||||
}
|
||||
static inline gsym::FileEntry getTombstoneKey() {
|
||||
uint32_t key = DenseMapInfo<uint32_t>::getTombstoneKey();
|
||||
gsym::gsym_strp_t key = DenseMapInfo<gsym::gsym_strp_t>::getTombstoneKey();
|
||||
return gsym::FileEntry(key, key);
|
||||
}
|
||||
static unsigned getHashValue(const gsym::FileEntry &Val) {
|
||||
return llvm::hash_combine(DenseMapInfo<uint32_t>::getHashValue(Val.Dir),
|
||||
DenseMapInfo<uint32_t>::getHashValue(Val.Base));
|
||||
return llvm::hash_combine(
|
||||
DenseMapInfo<gsym::gsym_strp_t>::getHashValue(Val.Dir),
|
||||
DenseMapInfo<gsym::gsym_strp_t>::getHashValue(Val.Base));
|
||||
}
|
||||
static bool isEqual(const gsym::FileEntry &LHS, const gsym::FileEntry &RHS) {
|
||||
return LHS == RHS;
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace gsym {
|
||||
class FileWriter {
|
||||
llvm::raw_pwrite_stream &OS;
|
||||
llvm::endianness ByteOrder;
|
||||
uint8_t StringOffsetSize = 4;
|
||||
|
||||
public:
|
||||
FileWriter(llvm::raw_pwrite_stream &S, llvm::endianness B)
|
||||
@@ -84,6 +85,11 @@ public:
|
||||
/// \param ByteSize The size of the value to write in bytes. Can be 1-8.
|
||||
LLVM_ABI void writeUnsigned(uint64_t Value, size_t ByteSize);
|
||||
|
||||
/// Write a string table offset of StringOffsetSize bytes into the stream.
|
||||
///
|
||||
/// \param Value The string table offset to write.
|
||||
LLVM_ABI void writeStringOffset(uint64_t Value);
|
||||
|
||||
/// Write an array of uint8_t values into the stream at the current file
|
||||
/// position.
|
||||
///
|
||||
@@ -127,6 +133,11 @@ public:
|
||||
|
||||
llvm::endianness getByteOrder() const { return ByteOrder; }
|
||||
|
||||
/// Get the string offset size for this writer.
|
||||
uint8_t getStringOffsetSize() const { return StringOffsetSize; }
|
||||
/// Set the string offset size for this writer.
|
||||
void setStringOffsetSize(uint8_t Size) { StringOffsetSize = Size; }
|
||||
|
||||
private:
|
||||
FileWriter(const FileWriter &rhs) = delete;
|
||||
void operator=(const FileWriter &rhs) = delete;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/DebugInfo/GSYM/CallSiteInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/ExtractRanges.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymTypes.h"
|
||||
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/LineTable.h"
|
||||
#include "llvm/DebugInfo/GSYM/LookupResult.h"
|
||||
@@ -25,6 +26,7 @@ class raw_ostream;
|
||||
|
||||
namespace gsym {
|
||||
|
||||
class GsymCreator;
|
||||
class GsymReader;
|
||||
/// Function information in GSYM files encodes information for one contiguous
|
||||
/// address range. If a function has discontiguous address ranges, they will
|
||||
@@ -92,7 +94,7 @@ class GsymReader;
|
||||
/// Where "N" is the number of tuples.
|
||||
struct FunctionInfo {
|
||||
AddressRange Range;
|
||||
uint32_t Name; ///< String table offset in the string table.
|
||||
gsym_strp_t Name; ///< String table offset in the string table.
|
||||
std::optional<LineTable> OptLineTable;
|
||||
std::optional<InlineInfo> Inline;
|
||||
std::optional<MergedFunctionsInfo> MergedFunctions;
|
||||
@@ -102,8 +104,8 @@ struct FunctionInfo {
|
||||
/// GSYM file.
|
||||
SmallString<32> EncodingCache;
|
||||
|
||||
FunctionInfo(uint64_t Addr = 0, uint64_t Size = 0, uint32_t N = 0)
|
||||
: Range(Addr, Addr + Size), Name(N) {}
|
||||
FunctionInfo(uint64_t Addr = 0, uint64_t Size = 0, gsym_strp_t Name = 0)
|
||||
: Range(Addr, Addr + Size), Name(Name) {}
|
||||
|
||||
/// Query if a FunctionInfo has rich debug info.
|
||||
///
|
||||
@@ -139,7 +141,7 @@ struct FunctionInfo {
|
||||
///
|
||||
/// \returns An FunctionInfo or an error describing the issue that was
|
||||
/// encountered during decoding.
|
||||
LLVM_ABI static llvm::Expected<FunctionInfo> decode(DataExtractor &Data,
|
||||
LLVM_ABI static llvm::Expected<FunctionInfo> decode(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr);
|
||||
|
||||
/// Encode this object into FileWriter stream.
|
||||
@@ -169,7 +171,7 @@ struct FunctionInfo {
|
||||
///
|
||||
/// \returns The size in bytes of the FunctionInfo if it were to be encoded
|
||||
/// into a byte stream.
|
||||
LLVM_ABI uint64_t cacheEncoding();
|
||||
LLVM_ABI uint64_t cacheEncoding(GsymCreator &GC);
|
||||
|
||||
/// Lookup an address within a FunctionInfo object's data stream.
|
||||
///
|
||||
@@ -189,7 +191,7 @@ struct FunctionInfo {
|
||||
///
|
||||
/// \param Addr The address to lookup.
|
||||
///
|
||||
/// \param MergedFuncsData A pointer to an optional DataExtractor that, if
|
||||
/// \param MergedFuncsData A pointer to an optional GsymDataExtractor that, if
|
||||
/// non-null, will be set to the raw data of the MergedFunctionInfo, if
|
||||
/// present.
|
||||
///
|
||||
@@ -197,9 +199,9 @@ struct FunctionInfo {
|
||||
/// encountered during decoding. An error should only be returned if the
|
||||
/// address is not contained in the FunctionInfo or if the data is corrupted.
|
||||
LLVM_ABI static llvm::Expected<LookupResult>
|
||||
lookup(DataExtractor &Data, const GsymReader &GR, uint64_t FuncAddr,
|
||||
lookup(GsymDataExtractor &Data, const GsymReader &GR, uint64_t FuncAddr,
|
||||
uint64_t Addr,
|
||||
std::optional<DataExtractor> *MergedFuncsData = nullptr);
|
||||
std::optional<GsymDataExtractor> *MergedFuncsData = nullptr);
|
||||
|
||||
uint64_t startAddress() const { return Range.start(); }
|
||||
uint64_t endAddress() const { return Range.end(); }
|
||||
|
||||
85
llvm/include/llvm/DebugInfo/GSYM/GlobalData.h
Normal file
85
llvm/include/llvm/DebugInfo/GSYM/GlobalData.h
Normal file
@@ -0,0 +1,85 @@
|
||||
//===- GlobalData.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_GLOBALDATA_H
|
||||
#define LLVM_DEBUGINFO_GSYM_GLOBALDATA_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
namespace gsym {
|
||||
class FileWriter;
|
||||
class GsymDataExtractor;
|
||||
|
||||
/// GlobalInfoType allows GSYM files to encode global information within a GSYM
|
||||
/// file in a way that is extensible for future versions of GSYM. It is
|
||||
/// designed to contain information needed by the GSYM encoding, along with any
|
||||
/// common data that FunctionInfo InfoType entries might require.
|
||||
enum class GlobalInfoType : uint32_t {
|
||||
EndOfList = 0u,
|
||||
// The address offsets table. It's a list of function addresses subtracted by
|
||||
// the base address, hence "offset".
|
||||
//
|
||||
// This table and the address info offsets table (see below) have the same
|
||||
// number of items. The items are 1-1 mapped.
|
||||
//
|
||||
// Given an address, this table is used to do a binary search to find the
|
||||
// index into the address info offsets table, where the location of the
|
||||
// FunctionInfo for the same function can be found in the GSYM.
|
||||
AddrOffsets = 1u,
|
||||
// The address info offsets table. Each entry is an offset relative to a
|
||||
// version-dependent reference position in the GSYM data where the
|
||||
// FunctionInfo for the corresponding function can be found.
|
||||
//
|
||||
// In version 1, the reference position is the start of the GSYM data.
|
||||
// In version 2 and later, the reference position is the start of the
|
||||
// FunctionInfo section.
|
||||
AddrInfoOffsets = 2u,
|
||||
// The string table. It contains all the strings used by the rest of the GSYM.
|
||||
// The exact storage of the strings is determined by
|
||||
// HeaderV2::StrTableEncoding.
|
||||
StringTable = 3u,
|
||||
// The file table. It's a list of files, referred by FunctionInfo objects.
|
||||
FileTable = 4u,
|
||||
// A list of FunctionInfo objects, terminated by EndOfList.
|
||||
FunctionInfo = 5u,
|
||||
// Optional UUID of the GSYM.
|
||||
UUID = 6u,
|
||||
};
|
||||
|
||||
/// GlobalData describes a section of data in a GSYM file by its type, file
|
||||
/// offset, and size. This is used to support 64-bit GSYM files where data
|
||||
/// sections can be located at arbitrary file offsets.
|
||||
struct GlobalData {
|
||||
GlobalInfoType Type;
|
||||
uint64_t FileOffset;
|
||||
uint64_t FileSize;
|
||||
|
||||
/// Encode this GlobalData entry into a FileWriter stream.
|
||||
///
|
||||
/// \param O The binary stream to write the data to.
|
||||
LLVM_ABI void encode(FileWriter &O) const;
|
||||
|
||||
/// Decode a GlobalData entry from a binary data stream.
|
||||
///
|
||||
/// \param GsymData The binary stream to read from.
|
||||
/// \param Offset The offset to start reading from. Updated on success.
|
||||
/// \returns A GlobalData entry or an error.
|
||||
LLVM_ABI static llvm::Expected<GlobalData> decode(GsymDataExtractor &GsymData,
|
||||
uint64_t &Offset);
|
||||
};
|
||||
|
||||
LLVM_ABI StringRef getNameForGlobalInfoType(GlobalInfoType Type);
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_GLOBALDATA_H
|
||||
@@ -133,6 +133,7 @@ class OutputAggregator;
|
||||
/// entry in the Function Info Offsets Table. For details on the exact encoding
|
||||
/// of FunctionInfo objects, see "llvm/DebugInfo/GSYM/FunctionInfo.h".
|
||||
class GsymCreator {
|
||||
protected:
|
||||
// Private member variables require Mutex protections
|
||||
mutable std::mutex Mutex;
|
||||
std::vector<FunctionInfo> Funcs;
|
||||
@@ -191,18 +192,10 @@ class GsymCreator {
|
||||
|
||||
/// Calculate the byte size of the GSYM header and tables sizes.
|
||||
///
|
||||
/// This function will calculate the exact size in bytes of the encocded GSYM
|
||||
/// for the following items:
|
||||
/// - The GSYM header
|
||||
/// - The Address offset table
|
||||
/// - The Address info offset table
|
||||
/// - The file table
|
||||
/// - The string table
|
||||
///
|
||||
/// This is used to help split GSYM files into segments.
|
||||
///
|
||||
/// \returns Size in bytes the GSYM header and tables.
|
||||
uint64_t calculateHeaderAndTableSize() const;
|
||||
virtual uint64_t calculateHeaderAndTableSize() const = 0;
|
||||
|
||||
/// Copy a FunctionInfo from the \a SrcGC GSYM creator into this creator.
|
||||
///
|
||||
@@ -228,7 +221,7 @@ class GsymCreator {
|
||||
/// \param SrcGC The source gsym creator to copy from.
|
||||
/// \param StrOff The string table offset from \a SrcGC to copy.
|
||||
/// \returns The new string table offset of the string within this object.
|
||||
uint32_t copyString(const GsymCreator &SrcGC, uint32_t StrOff);
|
||||
gsym_strp_t copyString(const GsymCreator &SrcGC, gsym_strp_t StrOff);
|
||||
|
||||
/// Copy a file from \a SrcGC into this object.
|
||||
///
|
||||
@@ -292,8 +285,41 @@ class GsymCreator {
|
||||
IsSegment = true;
|
||||
}
|
||||
|
||||
/// Validate that the creator is ready for encoding.
|
||||
///
|
||||
/// Checks that functions exist, the creator is finalized, the function count
|
||||
/// fits in 32 bits, and the base address is valid.
|
||||
///
|
||||
/// \param[out] BaseAddr Set to the base address on success.
|
||||
/// \returns An error if validation fails, or Error::success().
|
||||
llvm::Error validateForEncoding(std::optional<uint64_t> &BaseAddr) const;
|
||||
|
||||
/// Write the address offsets table to the output stream.
|
||||
///
|
||||
/// \param O The file writer to write to.
|
||||
/// \param AddrOffSize The byte width of each address offset.
|
||||
/// \param BaseAddr The base address to subtract from each function address.
|
||||
void encodeAddrOffsets(FileWriter &O, uint8_t AddrOffSize,
|
||||
uint64_t BaseAddr) const;
|
||||
|
||||
/// Write the file table to the output stream.
|
||||
///
|
||||
/// \param O The file writer to write to.
|
||||
/// \returns An error if the file table is too large, or Error::success().
|
||||
llvm::Error encodeFileTable(FileWriter &O) const;
|
||||
|
||||
/// Create a new empty creator of the same version.
|
||||
///
|
||||
/// Used by createSegment() to create segment creators of the correct
|
||||
/// version type.
|
||||
virtual std::unique_ptr<GsymCreator> createNew(bool Quiet) const = 0;
|
||||
|
||||
public:
|
||||
LLVM_ABI GsymCreator(bool Quiet = false);
|
||||
virtual ~GsymCreator() = default;
|
||||
|
||||
/// Get the size in bytes needed for encoding string offsets.
|
||||
virtual uint8_t getStringOffsetSize() const = 0;
|
||||
|
||||
/// Save a GSYM file to a stand alone file.
|
||||
///
|
||||
@@ -317,7 +343,7 @@ public:
|
||||
///
|
||||
/// \param O The stream to save the binary data to
|
||||
/// \returns An error object that indicates success or failure of the save.
|
||||
LLVM_ABI llvm::Error encode(FileWriter &O) const;
|
||||
virtual llvm::Error encode(FileWriter &O) const = 0;
|
||||
|
||||
/// Insert a string into the GSYM string table.
|
||||
///
|
||||
@@ -329,7 +355,7 @@ public:
|
||||
/// the string is owned by another object that will stay around
|
||||
/// long enough for the GsymCreator to save the GSYM file.
|
||||
/// \returns The unique 32 bit offset into the string table.
|
||||
LLVM_ABI uint32_t insertString(StringRef S, bool Copy = true);
|
||||
LLVM_ABI gsym_strp_t insertString(StringRef S, bool Copy = true);
|
||||
|
||||
/// Retrieve a string from the GSYM string table given its offset.
|
||||
///
|
||||
@@ -339,7 +365,7 @@ public:
|
||||
/// \param Offset The offset of the string to retrieve, previously returned by
|
||||
/// insertString.
|
||||
/// \returns The string at the given offset in the string table.
|
||||
LLVM_ABI StringRef getString(uint32_t Offset);
|
||||
LLVM_ABI StringRef getString(gsym_strp_t Offset);
|
||||
|
||||
/// Insert a file into this GSYM creator.
|
||||
///
|
||||
|
||||
36
llvm/include/llvm/DebugInfo/GSYM/GsymCreatorV1.h
Normal file
36
llvm/include/llvm/DebugInfo/GSYM/GsymCreatorV1.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//===- GsymCreatorV1.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_GSYMCREATORV1_H
|
||||
#define LLVM_DEBUGINFO_GSYM_GSYMCREATORV1_H
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace gsym {
|
||||
|
||||
class GsymCreatorV1 : public GsymCreator {
|
||||
uint64_t calculateHeaderAndTableSize() const override;
|
||||
std::unique_ptr<GsymCreator> createNew(bool Quiet) const override {
|
||||
return std::make_unique<GsymCreatorV1>(Quiet);
|
||||
}
|
||||
|
||||
public:
|
||||
GsymCreatorV1(bool Quiet = false) : GsymCreator(Quiet) {}
|
||||
|
||||
uint8_t getStringOffsetSize() const override {
|
||||
return Header::getStringOffsetSize();
|
||||
}
|
||||
LLVM_ABI llvm::Error encode(FileWriter &O) const override;
|
||||
};
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_GSYMCREATORV1_H
|
||||
37
llvm/include/llvm/DebugInfo/GSYM/GsymCreatorV2.h
Normal file
37
llvm/include/llvm/DebugInfo/GSYM/GsymCreatorV2.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//===- GsymCreatorV2.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_GSYMCREATORV2_H
|
||||
#define LLVM_DEBUGINFO_GSYM_GSYMCREATORV2_H
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
|
||||
#include "llvm/DebugInfo/GSYM/HeaderV2.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace gsym {
|
||||
|
||||
/// GsymCreatorV2 emits GSYM V2 data with a GlobalData-based section layout.
|
||||
class GsymCreatorV2 : public GsymCreator {
|
||||
uint64_t calculateHeaderAndTableSize() const override;
|
||||
std::unique_ptr<GsymCreator> createNew(bool Quiet) const override {
|
||||
return std::make_unique<GsymCreatorV2>(Quiet);
|
||||
}
|
||||
|
||||
public:
|
||||
GsymCreatorV2(bool Quiet = false) : GsymCreator(Quiet) {}
|
||||
|
||||
uint8_t getStringOffsetSize() const override {
|
||||
return HeaderV2::getStringOffsetSize();
|
||||
}
|
||||
LLVM_ABI llvm::Error encode(FileWriter &O) const override;
|
||||
};
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_GSYMCREATORV2_H
|
||||
58
llvm/include/llvm/DebugInfo/GSYM/GsymDataExtractor.h
Normal file
58
llvm/include/llvm/DebugInfo/GSYM/GsymDataExtractor.h
Normal file
@@ -0,0 +1,58 @@
|
||||
//===- GsymDataExtractor.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_GSYMDATAEXTRACTOR_H
|
||||
#define LLVM_DEBUGINFO_GSYM_GSYMDATAEXTRACTOR_H
|
||||
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace gsym {
|
||||
|
||||
/// A DataExtractor subclass that adds GSYM-specific string offset support.
|
||||
///
|
||||
/// GSYM files use variable-width string offsets (1-8 bytes). This subclass
|
||||
/// adds getStringOffsetSize() and getStringOffset() methods to support reading
|
||||
/// string offsets of the configured size.
|
||||
class GsymDataExtractor : public DataExtractor {
|
||||
uint8_t StringOffsetSize;
|
||||
|
||||
public:
|
||||
/// Construct from raw bytes.
|
||||
GsymDataExtractor(StringRef Data, bool IsLittleEndian,
|
||||
uint8_t StringOffsetSize = 8)
|
||||
: DataExtractor(Data, IsLittleEndian),
|
||||
StringOffsetSize(StringOffsetSize) {}
|
||||
|
||||
/// Construct a sub-range extractor from a parent, copying its endianness
|
||||
/// and string offset size.
|
||||
GsymDataExtractor(const GsymDataExtractor &Parent, uint64_t Offset,
|
||||
uint64_t Length)
|
||||
: DataExtractor(Parent.getData().substr(Offset, Length),
|
||||
Parent.isLittleEndian()),
|
||||
StringOffsetSize(Parent.getStringOffsetSize()) {}
|
||||
|
||||
/// Get the string offset size in bytes.
|
||||
uint8_t getStringOffsetSize() const { return StringOffsetSize; }
|
||||
|
||||
/// Extract a string offset of StringOffsetSize bytes from \a *offset_ptr.
|
||||
uint64_t getStringOffset(uint64_t *offset_ptr) const {
|
||||
return getUnsigned(offset_ptr, StringOffsetSize);
|
||||
}
|
||||
|
||||
/// Extract a string offset of StringOffsetSize bytes from the location given
|
||||
/// by the cursor.
|
||||
uint64_t getStringOffset(Cursor &C) const {
|
||||
return getUnsigned(C, StringOffsetSize);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_GSYMDATAEXTRACTOR_H
|
||||
@@ -12,14 +12,17 @@
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileEntry.h"
|
||||
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/GlobalData.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
#include "llvm/DebugInfo/GSYM/LineEntry.h"
|
||||
#include "llvm/DebugInfo/GSYM/StringTable.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <inttypes.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
@@ -33,49 +36,61 @@ 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.
|
||||
/// the host system. The header and the address table are designed to be mmap'ed
|
||||
/// as read only into memory and used without any parsing needed. If the
|
||||
/// endianness doesn't match, we swap the byte order of the address table into a
|
||||
/// separate buffer for efficient binary search. All the other data are parsed
|
||||
/// on demand with the correct endianness.
|
||||
///
|
||||
/// 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();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<MemoryBuffer> MemBuffer;
|
||||
StringRef GsymBytes;
|
||||
llvm::endianness Endian;
|
||||
const Header *Hdr = nullptr;
|
||||
/// Parsed GlobalData entries, keyed by type. Populated by
|
||||
/// parseHeaderAndGlobalDataEntries().
|
||||
std::map<GlobalInfoType, GlobalData> GlobalDataSections;
|
||||
ArrayRef<uint8_t> AddrOffsets;
|
||||
ArrayRef<uint32_t> AddrInfoOffsets;
|
||||
ArrayRef<FileEntry> Files;
|
||||
std::vector<uint8_t> SwappedAddrOffsets;
|
||||
GsymDataExtractor AddrInfoOffsetsData;
|
||||
GsymDataExtractor FileEntryData;
|
||||
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;
|
||||
|
||||
GsymReader(std::unique_ptr<MemoryBuffer> Buffer, llvm::endianness Endian);
|
||||
|
||||
public:
|
||||
LLVM_ABI GsymReader(GsymReader &&RHS);
|
||||
LLVM_ABI ~GsymReader();
|
||||
LLVM_ABI GsymReader(GsymReader &&RHS) = default;
|
||||
virtual ~GsymReader() = default;
|
||||
|
||||
bool isLittleEndian() const { return Endian == llvm::endianness::little; }
|
||||
|
||||
/// Get the GSYM version for this reader.
|
||||
virtual uint16_t getVersion() const = 0;
|
||||
|
||||
/// Get the base address of this GSYM file.
|
||||
virtual uint64_t getBaseAddress() const = 0;
|
||||
|
||||
/// Get the number of addresses in this GSYM file.
|
||||
virtual uint64_t getNumAddresses() const = 0;
|
||||
|
||||
/// Get the address offset byte size for this GSYM file.
|
||||
virtual uint8_t getAddressOffsetSize() const = 0;
|
||||
|
||||
/// Get the address info offset byte size for this GSYM file.
|
||||
virtual uint8_t getAddressInfoOffsetSize() const = 0;
|
||||
|
||||
/// Get the string offset byte size for this GSYM file.
|
||||
virtual uint8_t getStringOffsetSize() const = 0;
|
||||
|
||||
/// 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.
|
||||
LLVM_ABI static llvm::Expected<GsymReader> openFile(StringRef Path);
|
||||
LLVM_ABI static llvm::Expected<std::unique_ptr<GsymReader>>
|
||||
openFile(StringRef Path);
|
||||
|
||||
/// Construct a GsymReader from a buffer.
|
||||
///
|
||||
@@ -83,11 +98,8 @@ public:
|
||||
/// 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.
|
||||
LLVM_ABI static llvm::Expected<GsymReader> copyBuffer(StringRef Bytes);
|
||||
|
||||
/// Access the GSYM header.
|
||||
/// \returns A native endian version of the GSYM header.
|
||||
LLVM_ABI const Header &getHeader() const;
|
||||
LLVM_ABI static llvm::Expected<std::unique_ptr<GsymReader>>
|
||||
copyBuffer(StringRef Bytes);
|
||||
|
||||
/// Get the full function info for an address.
|
||||
///
|
||||
@@ -130,7 +142,7 @@ public:
|
||||
///
|
||||
/// \param Addr A virtual address from the orignal object file to lookup.
|
||||
///
|
||||
/// \param MergedFuncsData A pointer to an optional DataExtractor that, if
|
||||
/// \param MergedFuncsData A pointer to an optional GsymDataExtractor that, if
|
||||
/// non-null, will be set to the raw data of the MergedFunctionInfo, if
|
||||
/// present.
|
||||
///
|
||||
@@ -139,7 +151,7 @@ public:
|
||||
/// for failing to lookup the address.
|
||||
LLVM_ABI llvm::Expected<LookupResult>
|
||||
lookup(uint64_t Addr,
|
||||
std::optional<DataExtractor> *MergedFuncsData = nullptr) const;
|
||||
std::optional<GsymDataExtractor> *MergedFuncsData = nullptr) const;
|
||||
|
||||
/// Lookup all merged functions for a given address.
|
||||
///
|
||||
@@ -158,7 +170,7 @@ public:
|
||||
///
|
||||
/// \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]; }
|
||||
StringRef getString(gsym_strp_t Offset) const { return StrTab[Offset]; }
|
||||
|
||||
/// Get the a file entry for the suppplied file index.
|
||||
///
|
||||
@@ -170,15 +182,21 @@ public:
|
||||
/// \returns An optional FileInfo that will be valid if the file index is
|
||||
/// valid, or std::nullopt if the file index is out of bounds,
|
||||
std::optional<FileEntry> getFile(uint32_t Index) const {
|
||||
if (Index < Files.size())
|
||||
return Files[Index];
|
||||
return std::nullopt;
|
||||
uint64_t EntrySize =
|
||||
FileEntry::getEncodedSize(FileEntryData.getStringOffsetSize());
|
||||
uint64_t Offset = Index * EntrySize;
|
||||
if (!FileEntryData.isValidOffsetForDataOfSize(Offset, EntrySize))
|
||||
return std::nullopt;
|
||||
FileEntry FE;
|
||||
FE.Dir = FileEntryData.getStringOffset(&Offset);
|
||||
FE.Base = FileEntryData.getStringOffset(&Offset);
|
||||
return FE;
|
||||
}
|
||||
|
||||
/// Dump the entire Gsym data contained in this object.
|
||||
///
|
||||
/// \param OS The output stream to dump to.
|
||||
LLVM_ABI void dump(raw_ostream &OS);
|
||||
virtual void dump(raw_ostream &OS) = 0;
|
||||
|
||||
/// Dump a FunctionInfo object.
|
||||
///
|
||||
@@ -266,11 +284,6 @@ public:
|
||||
/// \param FE The object to dump.
|
||||
LLVM_ABI void dump(raw_ostream &OS, std::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.
|
||||
@@ -281,6 +294,101 @@ public:
|
||||
LLVM_ABI std::optional<uint64_t> getAddress(size_t Index) const;
|
||||
|
||||
protected:
|
||||
/// Get the GlobalData entry for a section type.
|
||||
///
|
||||
/// \param Type The section type to retrieve.
|
||||
/// \returns The GlobalData entry, or std::nullopt if the section is not
|
||||
/// present.
|
||||
LLVM_ABI std::optional<GlobalData> getGlobalData(GlobalInfoType Type) const;
|
||||
|
||||
/// Get the raw bytes for a required GlobalData section as a StringRef.
|
||||
///
|
||||
/// \param Type The section type to retrieve.
|
||||
/// \returns The section data, or an error if the section is not present or
|
||||
/// any bytes are not present in the file.
|
||||
LLVM_ABI llvm::Expected<StringRef>
|
||||
getRequiredGlobalDataBytes(GlobalInfoType Type) const;
|
||||
|
||||
/// Get the raw bytes for an optional GlobalData section as a StringRef.
|
||||
///
|
||||
/// \param Type The section type to retrieve.
|
||||
/// \returns The section data, or std::nullopt if the section is not present
|
||||
/// or any bytes are not present in the file.
|
||||
LLVM_ABI std::optional<StringRef>
|
||||
getOptionalGlobalDataBytes(GlobalInfoType Type) const;
|
||||
|
||||
/// Parse the GSYM data from the memory buffer.
|
||||
///
|
||||
/// \returns Error on failure.
|
||||
LLVM_ABI llvm::Error parse();
|
||||
|
||||
/// Parse the version-specific header and populate GlobalDataSections.
|
||||
///
|
||||
/// \returns Error on failure.
|
||||
virtual llvm::Error parseHeaderAndGlobalDataEntries() = 0;
|
||||
|
||||
/// Parse and validate the header from the beginning of the memory buffer.
|
||||
///
|
||||
/// \param OutHdr Output pointer to the parsed header.
|
||||
/// \param OutSwappedHdr Storage for byte-swapped header if needed.
|
||||
/// \returns Error on failure.
|
||||
template <class HeaderT>
|
||||
llvm::Error parseHeader(const HeaderT *&OutHdr,
|
||||
std::unique_ptr<HeaderT> &OutSwappedHdr) {
|
||||
const StringRef Buf = MemBuffer->getBuffer();
|
||||
if (Buf.size() < HeaderT::getEncodedSize())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"not enough data for a GSYM header");
|
||||
if (Endian == llvm::endianness::native) {
|
||||
// Non-swap case. Mmap the header.
|
||||
OutHdr = reinterpret_cast<const HeaderT *>(Buf.data());
|
||||
} else {
|
||||
// Swap case. Decode with a GsymDataExtractor with the correct endianness.
|
||||
GsymDataExtractor Data(Buf, isLittleEndian());
|
||||
OutSwappedHdr = std::make_unique<HeaderT>();
|
||||
auto ExpectedHdr = HeaderT::decode(Data);
|
||||
if (!ExpectedHdr)
|
||||
return ExpectedHdr.takeError();
|
||||
*OutSwappedHdr = *ExpectedHdr;
|
||||
OutHdr = OutSwappedHdr.get();
|
||||
}
|
||||
if (Error Err = OutHdr->checkForError())
|
||||
return Err;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
/// Parse GlobalData entries starting at \p Offset into GlobalDataSections.
|
||||
///
|
||||
/// This should only be called by any GSYM version >= 2. If called by V1, an
|
||||
/// error will be returned.
|
||||
///
|
||||
/// \param Offset The byte offset where GlobalData entries begin.
|
||||
/// \returns Error on failure.
|
||||
llvm::Error parseGlobalDataEntries(uint64_t Offset);
|
||||
|
||||
/// Parse address offsets section bytes into AddrOffsets.
|
||||
///
|
||||
/// \param Bytes The raw section bytes.
|
||||
/// \returns Error on failure.
|
||||
llvm::Error parseAddrOffsets(StringRef Bytes);
|
||||
|
||||
/// Set address info offsets section bytes into AddrInfoOffsetsData.
|
||||
///
|
||||
/// \param Bytes The raw section bytes.
|
||||
/// \returns Error on failure.
|
||||
llvm::Error setAddrInfoOffsetsData(StringRef Bytes);
|
||||
|
||||
/// Set string table section bytes into StrTab.
|
||||
///
|
||||
/// \param Bytes The raw section bytes.
|
||||
/// \returns Error on failure.
|
||||
llvm::Error setStringTableData(StringRef Bytes);
|
||||
|
||||
/// Set file table section bytes into FileEntryData.
|
||||
///
|
||||
/// \param Bytes The raw section bytes.
|
||||
/// \returns Error on failure.
|
||||
llvm::Error setFileTableData(StringRef Bytes);
|
||||
|
||||
/// Get an appropriate address info offsets array.
|
||||
///
|
||||
@@ -314,9 +422,10 @@ protected:
|
||||
std::optional<uint64_t> addressForIndex(size_t Index) const {
|
||||
ArrayRef<T> AIO = getAddrOffsets<T>();
|
||||
if (Index < AIO.size())
|
||||
return AIO[Index] + Hdr->BaseAddress;
|
||||
return AIO[Index] + getBaseAddress();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Lookup an address offset in the AddrOffsets table.
|
||||
///
|
||||
/// Given an address offset, look it up using a binary search of the
|
||||
@@ -364,7 +473,7 @@ protected:
|
||||
/// GsymReader.
|
||||
/// \returns An expected GsymReader that contains the object or an error
|
||||
/// object that indicates reason for failing to read the GSYM.
|
||||
LLVM_ABI static llvm::Expected<llvm::gsym::GsymReader>
|
||||
LLVM_ABI static llvm::Expected<std::unique_ptr<GsymReader>>
|
||||
create(std::unique_ptr<MemoryBuffer> &MemBuffer);
|
||||
|
||||
/// Given an address, find the address index.
|
||||
@@ -408,7 +517,7 @@ protected:
|
||||
///
|
||||
/// \returns An valid data extractor on success, or an error if we fail to
|
||||
/// find the address in a function info or corrrectly decode the data
|
||||
LLVM_ABI llvm::Expected<llvm::DataExtractor>
|
||||
LLVM_ABI llvm::Expected<GsymDataExtractor>
|
||||
getFunctionInfoDataForAddress(uint64_t Addr, uint64_t &FuncStartAddr) const;
|
||||
|
||||
/// Get the function data and address given an address index.
|
||||
@@ -418,7 +527,7 @@ protected:
|
||||
/// \returns An expected FunctionInfo that contains the function info object
|
||||
/// or an error object that indicates reason for failing to lookup the
|
||||
/// address.
|
||||
LLVM_ABI llvm::Expected<llvm::DataExtractor>
|
||||
LLVM_ABI llvm::Expected<GsymDataExtractor>
|
||||
getFunctionInfoDataAtIndex(uint64_t AddrIdx, uint64_t &FuncStartAddr) const;
|
||||
};
|
||||
|
||||
|
||||
53
llvm/include/llvm/DebugInfo/GSYM/GsymReaderV1.h
Normal file
53
llvm/include/llvm/DebugInfo/GSYM/GsymReaderV1.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//===- GsymReaderV1.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_GSYMREADERV1_H
|
||||
#define LLVM_DEBUGINFO_GSYM_GSYMREADERV1_H
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/GsymReader.h"
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
|
||||
namespace llvm {
|
||||
class MemoryBuffer;
|
||||
|
||||
namespace gsym {
|
||||
|
||||
/// GsymReaderV1 reads GSYM V1 data from a buffer.
|
||||
class GsymReaderV1 : public GsymReader {
|
||||
friend class GsymReader;
|
||||
const Header *Hdr = nullptr;
|
||||
std::unique_ptr<Header> SwappedHdr;
|
||||
|
||||
protected:
|
||||
GsymReaderV1(std::unique_ptr<MemoryBuffer> Buffer, llvm::endianness Endian);
|
||||
llvm::Error parseHeaderAndGlobalDataEntries() override;
|
||||
|
||||
public:
|
||||
GsymReaderV1(GsymReaderV1 &&RHS) = default;
|
||||
~GsymReaderV1() override = default;
|
||||
|
||||
// Header accessors
|
||||
uint16_t getVersion() const override { return Header::getVersion(); }
|
||||
uint64_t getBaseAddress() const override { return Hdr->BaseAddress; }
|
||||
uint64_t getNumAddresses() const override { return Hdr->NumAddresses; }
|
||||
uint8_t getAddressOffsetSize() const override { return Hdr->AddrOffSize; }
|
||||
uint8_t getAddressInfoOffsetSize() const override {
|
||||
return Header::getAddressInfoOffsetSize();
|
||||
}
|
||||
uint8_t getStringOffsetSize() const override {
|
||||
return Header::getStringOffsetSize();
|
||||
}
|
||||
|
||||
using GsymReader::dump;
|
||||
LLVM_ABI void dump(raw_ostream &OS) override;
|
||||
};
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_GSYMREADERV1_H
|
||||
53
llvm/include/llvm/DebugInfo/GSYM/GsymReaderV2.h
Normal file
53
llvm/include/llvm/DebugInfo/GSYM/GsymReaderV2.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//===- GsymReaderV2.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_GSYMREADERV2_H
|
||||
#define LLVM_DEBUGINFO_GSYM_GSYMREADERV2_H
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/GsymReader.h"
|
||||
#include "llvm/DebugInfo/GSYM/HeaderV2.h"
|
||||
|
||||
namespace llvm {
|
||||
class MemoryBuffer;
|
||||
|
||||
namespace gsym {
|
||||
|
||||
/// GsymReaderV2 reads GSYM V2 data from a buffer.
|
||||
class GsymReaderV2 : public GsymReader {
|
||||
friend class GsymReader;
|
||||
const HeaderV2 *Hdr = nullptr;
|
||||
std::unique_ptr<HeaderV2> SwappedHdr;
|
||||
|
||||
protected:
|
||||
GsymReaderV2(std::unique_ptr<MemoryBuffer> Buffer, llvm::endianness Endian);
|
||||
llvm::Error parseHeaderAndGlobalDataEntries() override;
|
||||
|
||||
public:
|
||||
GsymReaderV2(GsymReaderV2 &&RHS) = default;
|
||||
~GsymReaderV2() override = default;
|
||||
|
||||
// Header accessors
|
||||
uint16_t getVersion() const override { return HeaderV2::getVersion(); }
|
||||
uint64_t getBaseAddress() const override { return Hdr->BaseAddress; }
|
||||
uint64_t getNumAddresses() const override { return Hdr->NumAddresses; }
|
||||
uint8_t getAddressOffsetSize() const override { return Hdr->AddrOffSize; }
|
||||
uint8_t getAddressInfoOffsetSize() const override {
|
||||
return HeaderV2::getAddressInfoOffsetSize();
|
||||
}
|
||||
uint8_t getStringOffsetSize() const override {
|
||||
return HeaderV2::getStringOffsetSize();
|
||||
}
|
||||
|
||||
using GsymReader::dump;
|
||||
LLVM_ABI void dump(raw_ostream &OS) override;
|
||||
};
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_GSYMREADERV2_H
|
||||
26
llvm/include/llvm/DebugInfo/GSYM/GsymTypes.h
Normal file
26
llvm/include/llvm/DebugInfo/GSYM/GsymTypes.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//===- GsymTypes.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_GSYMTYPES_H
|
||||
#define LLVM_DEBUGINFO_GSYM_GSYMTYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace llvm {
|
||||
namespace gsym {
|
||||
|
||||
/// The type of string offset used in the code.
|
||||
///
|
||||
/// Note: This may be different from what's serialized into GSYM files, which
|
||||
/// is version dependent (e.g. V1 uses uint32_t, V2+ uses uint64_t).
|
||||
typedef uint64_t gsym_strp_t;
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_GSYMTYPES_H
|
||||
@@ -17,14 +17,13 @@
|
||||
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
class DataExtractor;
|
||||
|
||||
namespace gsym {
|
||||
class FileWriter;
|
||||
class GsymDataExtractor;
|
||||
|
||||
constexpr uint32_t GSYM_MAGIC = 0x4753594d; // 'GSYM'
|
||||
constexpr uint32_t GSYM_CIGAM = 0x4d595347; // 'MYSG'
|
||||
constexpr uint32_t GSYM_VERSION = 1;
|
||||
constexpr size_t GSYM_MAX_UUID_SIZE = 20;
|
||||
|
||||
/// The GSYM header.
|
||||
@@ -86,6 +85,18 @@ struct Header {
|
||||
/// be set to zero.
|
||||
uint8_t UUID[GSYM_MAX_UUID_SIZE];
|
||||
|
||||
/// Return the version of this header.
|
||||
static constexpr uint32_t getVersion() { return 1; }
|
||||
|
||||
/// Return the on-disk encoded size of the header in bytes.
|
||||
static constexpr uint64_t getEncodedSize() { return sizeof(Header); }
|
||||
|
||||
/// Return the size in bytes of address info offsets.
|
||||
static constexpr uint8_t getAddressInfoOffsetSize() { return 4; }
|
||||
|
||||
/// Return the size in bytes of string table offsets.
|
||||
static constexpr uint8_t getStringOffsetSize() { return 4; }
|
||||
|
||||
/// Check if a header is valid and return an error if anything is wrong.
|
||||
///
|
||||
/// This function can be used prior to encoding a header to ensure it is
|
||||
@@ -109,7 +120,7 @@ struct Header {
|
||||
///
|
||||
/// \returns A Header or an error describing the issue that was
|
||||
/// encountered during decoding.
|
||||
LLVM_ABI static llvm::Expected<Header> decode(DataExtractor &Data);
|
||||
LLVM_ABI static llvm::Expected<Header> decode(GsymDataExtractor &Data);
|
||||
|
||||
/// Encode this object into FileWriter stream.
|
||||
///
|
||||
|
||||
137
llvm/include/llvm/DebugInfo/GSYM/HeaderV2.h
Normal file
137
llvm/include/llvm/DebugInfo/GSYM/HeaderV2.h
Normal file
@@ -0,0 +1,137 @@
|
||||
//===- HeaderV2.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_HEADERV2_H
|
||||
#define LLVM_DEBUGINFO_GSYM_HEADERV2_H
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
|
||||
namespace gsym {
|
||||
class FileWriter;
|
||||
class GsymDataExtractor;
|
||||
|
||||
/// Encoding format for the string table.
|
||||
enum class StringTableEncoding : uint8_t {
|
||||
/// A list of NULL-terminated strings (same as V1). The first string at
|
||||
/// offset zero must be the empty C string.
|
||||
Default = 0,
|
||||
};
|
||||
|
||||
/// The GSYM V2 header.
|
||||
///
|
||||
/// The GSYM V2 header is found at the start of a stand alone GSYM file, or as
|
||||
/// the first bytes in a section when GSYM is contained in a section of an
|
||||
/// executable file (ELF, mach-o, COFF).
|
||||
///
|
||||
/// The header structure is encoded exactly as it appears in the structure
|
||||
/// definition with no gaps between members. Alignment should not change from
|
||||
/// system to system as the members are laid out so that they will align the
|
||||
/// same on different architectures.
|
||||
///
|
||||
/// When endianness of the system loading a GSYM file matches, the file can
|
||||
/// be mmap'ed in and a pointer to the header can be cast to the first bytes
|
||||
/// of the file (stand alone GSYM file) or section data (GSYM in a section).
|
||||
/// When endianness is swapped, HeaderV2::decode() is used to read the header.
|
||||
///
|
||||
/// The V2 file layout is:
|
||||
/// [HeaderV2 - 20 bytes fixed]
|
||||
/// [GlobalData entries - array of 20-byte entries, terminated by EndOfList]
|
||||
/// [Data sections at arbitrary file offsets, zero-padded for alignment]
|
||||
///
|
||||
/// Each GlobalData entry (see GlobalData.h) describes a section by its type,
|
||||
/// file offset, and file size. The sections can appear in any order in the
|
||||
/// file since each GlobalData entry contains an offset from the first byte of
|
||||
/// the GSYM header. The GlobalData array is terminated by an entry with type
|
||||
/// EndOfList and all other fields set to zero. See GlobalInfoType (in
|
||||
/// GlobalData.h) for all section types.
|
||||
struct HeaderV2 {
|
||||
/// The magic bytes should be set to GSYM_MAGIC. This helps detect if a file
|
||||
/// is a GSYM file by scanning the first 4 bytes of a file or section.
|
||||
/// This value might appear byte swapped when endianness is swapped.
|
||||
uint32_t Magic;
|
||||
/// The version number determines how the header is decoded. As version
|
||||
/// numbers increase, "Magic" and "Version" members should always appear at
|
||||
/// offset zero and 4 respectively to ensure clients figure out if they can
|
||||
/// parse the format.
|
||||
uint16_t Version;
|
||||
/// The size in bytes of each address offset in the address offsets table.
|
||||
uint8_t AddrOffSize;
|
||||
/// String table encoding. Allows for future encoding for string table.
|
||||
StringTableEncoding StrTableEncoding;
|
||||
/// The 64 bit base address that all address offsets in the address offsets
|
||||
/// table are relative to. Storing a full 64 bit address allows our address
|
||||
/// offsets table to be smaller on disk.
|
||||
uint64_t BaseAddress;
|
||||
/// The number of addresses stored in the address offsets table and the
|
||||
/// address info offsets table.
|
||||
uint32_t NumAddresses;
|
||||
|
||||
/// Return the version of this header.
|
||||
static constexpr uint32_t getVersion() { return 2; }
|
||||
|
||||
/// Return the on-disk encoded size of the header in bytes.
|
||||
/// This may differ from sizeof(HeaderV2) due to struct padding at the end.
|
||||
static constexpr uint64_t getEncodedSize() { return 20; }
|
||||
|
||||
/// Return the size in bytes of address info offsets.
|
||||
static constexpr uint8_t getAddressInfoOffsetSize() { return 8; }
|
||||
|
||||
/// Return the size in bytes of string table offsets.
|
||||
static constexpr uint8_t getStringOffsetSize() { return 8; }
|
||||
|
||||
/// Check if a header is valid and return an error if anything is wrong.
|
||||
///
|
||||
/// This function can be used prior to encoding a header to ensure it is
|
||||
/// valid, or after decoding a header to ensure it is valid and supported.
|
||||
///
|
||||
/// Check a correctly byte swapped header for errors:
|
||||
/// - check magic value
|
||||
/// - check that version number is supported
|
||||
/// - check that the address offset size is supported
|
||||
/// - check that the string table encoding is supported
|
||||
///
|
||||
/// \returns An error if anything is wrong in the header, or Error::success()
|
||||
/// if there are no errors.
|
||||
LLVM_ABI llvm::Error checkForError() const;
|
||||
|
||||
/// Decode an object from a binary data stream.
|
||||
///
|
||||
/// \param Data The binary stream to read the data from. This object must
|
||||
/// have the data for the object starting at offset zero. The data
|
||||
/// can contain more data than needed.
|
||||
///
|
||||
/// \returns A HeaderV2 or an error describing the issue that was
|
||||
/// encountered during decoding.
|
||||
LLVM_ABI static llvm::Expected<HeaderV2> decode(GsymDataExtractor &Data);
|
||||
|
||||
/// Encode this object into FileWriter stream.
|
||||
///
|
||||
/// \param O The binary stream to write the data to at the current file
|
||||
/// position.
|
||||
///
|
||||
/// \returns An error object that indicates success or failure of the
|
||||
/// encoding process.
|
||||
LLVM_ABI llvm::Error encode(FileWriter &O) const;
|
||||
};
|
||||
|
||||
LLVM_ABI bool operator==(const HeaderV2 &LHS, const HeaderV2 &RHS);
|
||||
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS,
|
||||
const llvm::gsym::HeaderV2 &H);
|
||||
|
||||
} // namespace gsym
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_GSYM_HEADERV2_H
|
||||
@@ -10,6 +10,7 @@
|
||||
#define LLVM_DEBUGINFO_GSYM_INLINEINFO_H
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/ExtractRanges.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymTypes.h"
|
||||
#include "llvm/DebugInfo/GSYM/LineEntry.h"
|
||||
#include "llvm/DebugInfo/GSYM/LookupResult.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
@@ -59,7 +60,7 @@ class GsymReader;
|
||||
///
|
||||
struct InlineInfo {
|
||||
|
||||
uint32_t Name = 0; ///< String table offset in the string table.
|
||||
gsym_strp_t Name = 0; ///< String table offset in the string table.
|
||||
uint32_t CallFile = 0; ///< 1 based file index in the file table.
|
||||
uint32_t CallLine = 0; ///< Source line number.
|
||||
AddressRanges Ranges;
|
||||
@@ -118,9 +119,9 @@ struct InlineInfo {
|
||||
/// \returns An error if the inline information is corrupt, or
|
||||
/// Error::success() for all other cases, even when no information
|
||||
/// is added to \a SrcLocs.
|
||||
LLVM_ABI static llvm::Error lookup(const GsymReader &GR, DataExtractor &Data,
|
||||
uint64_t BaseAddr, uint64_t Addr,
|
||||
SourceLocations &SrcLocs);
|
||||
LLVM_ABI static llvm::Error lookup(const GsymReader &GR,
|
||||
GsymDataExtractor &Data, uint64_t BaseAddr,
|
||||
uint64_t Addr, SourceLocations &SrcLocs);
|
||||
|
||||
/// Lookup an address in the InlineInfo object
|
||||
///
|
||||
@@ -148,7 +149,7 @@ struct InlineInfo {
|
||||
/// another InlineInfo object.
|
||||
/// \returns An InlineInfo or an error describing the issue that was
|
||||
/// encountered during decoding.
|
||||
LLVM_ABI static llvm::Expected<InlineInfo> decode(DataExtractor &Data,
|
||||
LLVM_ABI static llvm::Expected<InlineInfo> decode(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr);
|
||||
|
||||
/// Encode this InlineInfo object into FileWriter stream.
|
||||
|
||||
@@ -137,7 +137,7 @@ public:
|
||||
/// initialize the line table row prior to parsing any opcodes.
|
||||
///
|
||||
/// \returns An LineEntry object if a match is found, error otherwise.
|
||||
LLVM_ABI static Expected<LineEntry> lookup(DataExtractor &Data,
|
||||
LLVM_ABI static Expected<LineEntry> lookup(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr, uint64_t Addr);
|
||||
|
||||
/// Decode an LineTable object from a binary data stream.
|
||||
@@ -152,7 +152,7 @@ public:
|
||||
///
|
||||
/// \returns An LineTable or an error describing the issue that was
|
||||
/// encountered during decoding.
|
||||
LLVM_ABI static llvm::Expected<LineTable> decode(DataExtractor &Data,
|
||||
LLVM_ABI static llvm::Expected<LineTable> decode(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr);
|
||||
/// Encode this LineTable object into FileWriter stream.
|
||||
///
|
||||
|
||||
@@ -32,17 +32,17 @@ struct MergedFunctionsInfo {
|
||||
/// \returns A boolean indicating if this FunctionInfo is valid.
|
||||
bool isValid() { return !MergedFunctions.empty(); }
|
||||
|
||||
/// Get a vector of DataExtractor objects for the functions in this
|
||||
/// Get a vector of GsymDataExtractor objects for the functions in this
|
||||
/// MergedFunctionsInfo object.
|
||||
///
|
||||
/// \param Data The binary stream to read the data from. This object must have
|
||||
/// the data for the MergedFunctionsInfo object starting at offset zero. The
|
||||
/// data can contain more data than needed.
|
||||
///
|
||||
/// \returns An llvm::Expected containing a vector of DataExtractor objects on
|
||||
/// success, or an error object if parsing fails.
|
||||
LLVM_ABI static llvm::Expected<std::vector<DataExtractor>>
|
||||
getFuncsDataExtractors(DataExtractor &Data);
|
||||
/// \returns An llvm::Expected containing a vector of GsymDataExtractor
|
||||
/// objects on success, or an error object if parsing fails.
|
||||
LLVM_ABI static llvm::Expected<std::vector<GsymDataExtractor>>
|
||||
getFuncsDataExtractors(GsymDataExtractor &Data);
|
||||
|
||||
/// Decode an MergedFunctionsInfo object from a binary data stream.
|
||||
///
|
||||
@@ -55,7 +55,7 @@ struct MergedFunctionsInfo {
|
||||
/// \returns An MergedFunctionsInfo or an error describing the issue that was
|
||||
/// encountered during decoding.
|
||||
LLVM_ABI static llvm::Expected<MergedFunctionsInfo>
|
||||
decode(DataExtractor &Data, uint64_t BaseAddr);
|
||||
decode(GsymDataExtractor &Data, uint64_t BaseAddr);
|
||||
|
||||
/// Encode this MergedFunctionsInfo object into FileWriter stream.
|
||||
///
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/DebugInfo/GSYM/ExtractRanges.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymTypes.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace llvm {
|
||||
@@ -23,7 +24,7 @@ struct StringTable {
|
||||
StringTable() = default;
|
||||
StringTable(StringRef D) : Data(D) {}
|
||||
StringRef operator[](size_t Offset) const { return getString(Offset); }
|
||||
StringRef getString(uint32_t Offset) const {
|
||||
StringRef getString(gsym_strp_t Offset) const {
|
||||
if (Offset < Data.size()) {
|
||||
auto End = Data.find('\0', Offset);
|
||||
return Data.substr(Offset, End - Offset);
|
||||
@@ -33,16 +34,32 @@ struct StringTable {
|
||||
void clear() { Data = StringRef(); }
|
||||
};
|
||||
|
||||
inline raw_ostream &operator<<(raw_ostream &OS, const StringTable &S) {
|
||||
inline void dump(raw_ostream &OS, const StringTable &S,
|
||||
uint8_t StringOffsetSize) {
|
||||
OS << "String table:\n";
|
||||
uint32_t Offset = 0;
|
||||
gsym_strp_t Offset = 0;
|
||||
const size_t Size = S.Data.size();
|
||||
while (Offset < Size) {
|
||||
StringRef Str = S.getString(Offset);
|
||||
OS << HEX32(Offset) << ": \"" << Str << "\"\n";
|
||||
switch (StringOffsetSize) {
|
||||
case 1:
|
||||
OS << HEX8(Offset);
|
||||
break;
|
||||
case 2:
|
||||
OS << HEX16(Offset);
|
||||
break;
|
||||
case 4:
|
||||
OS << HEX32(Offset);
|
||||
break;
|
||||
case 8:
|
||||
OS << HEX64(Offset);
|
||||
break;
|
||||
default:
|
||||
OS << HEX64(Offset);
|
||||
}
|
||||
OS << ": \"" << Str << "\"\n";
|
||||
Offset += Str.size() + 1;
|
||||
}
|
||||
return OS;
|
||||
}
|
||||
|
||||
} // namespace gsym
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
add_llvm_component_library(LLVMDebugInfoGSYM
|
||||
DwarfTransformer.cpp
|
||||
Header.cpp
|
||||
HeaderV2.cpp
|
||||
FileWriter.cpp
|
||||
FunctionInfo.cpp
|
||||
GlobalData.cpp
|
||||
GsymCreator.cpp
|
||||
GsymCreatorV1.cpp
|
||||
GsymCreatorV2.cpp
|
||||
GsymContext.cpp
|
||||
GsymReader.cpp
|
||||
GsymReaderV1.cpp
|
||||
GsymReaderV2.cpp
|
||||
InlineInfo.cpp
|
||||
LineTable.cpp
|
||||
LookupResult.cpp
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/Support/InterleavedRange.h"
|
||||
#include "llvm/Support/YAMLParser.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
@@ -26,12 +26,12 @@ Error CallSiteInfo::encode(FileWriter &O) const {
|
||||
O.writeU64(ReturnOffset);
|
||||
O.writeU8(Flags);
|
||||
O.writeU32(MatchRegex.size());
|
||||
for (uint32_t Entry : MatchRegex)
|
||||
O.writeU32(Entry);
|
||||
for (gsym_strp_t Entry : MatchRegex)
|
||||
O.writeStringOffset(Entry);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<CallSiteInfo> CallSiteInfo::decode(DataExtractor &Data,
|
||||
Expected<CallSiteInfo> CallSiteInfo::decode(GsymDataExtractor &Data,
|
||||
uint64_t &Offset) {
|
||||
CallSiteInfo CSI;
|
||||
|
||||
@@ -56,11 +56,11 @@ Expected<CallSiteInfo> CallSiteInfo::decode(DataExtractor &Data,
|
||||
|
||||
CSI.MatchRegex.reserve(NumEntries);
|
||||
for (uint32_t i = 0; i < NumEntries; ++i) {
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, Data.getStringOffsetSize()))
|
||||
return createStringError(std::errc::io_error,
|
||||
"0x%8.8" PRIx64 ": missing MatchRegex entry",
|
||||
Offset);
|
||||
uint32_t Entry = Data.getU32(&Offset);
|
||||
gsym_strp_t Entry = Data.getStringOffset(&Offset);
|
||||
CSI.MatchRegex.push_back(Entry);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ Error CallSiteInfoCollection::encode(FileWriter &O) const {
|
||||
}
|
||||
|
||||
Expected<CallSiteInfoCollection>
|
||||
CallSiteInfoCollection::decode(DataExtractor &Data) {
|
||||
CallSiteInfoCollection::decode(GsymDataExtractor &Data) {
|
||||
CallSiteInfoCollection CSC;
|
||||
uint64_t Offset = 0;
|
||||
|
||||
@@ -207,7 +207,7 @@ Error CallSiteInfoLoader::processYAMLFunctions(
|
||||
// start address to make the offset absolute.
|
||||
CSI.ReturnOffset = CallSiteYAML.return_offset;
|
||||
for (const auto &Regex : CallSiteYAML.match_regex) {
|
||||
uint32_t StrOffset = GCreator.insertString(Regex);
|
||||
gsym_strp_t StrOffset = GCreator.insertString(Regex);
|
||||
CSI.MatchRegex.push_back(StrOffset);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ static DWARFDie GetParentDeclContextDIE(DWARFDie &Die) {
|
||||
/// .debug_info. If we create a qualified name string in this function by
|
||||
/// combining multiple strings in the DWARF string table or info, we will make
|
||||
/// a copy of the string when we add it to the string table.
|
||||
static std::optional<uint32_t>
|
||||
static std::optional<gsym_strp_t>
|
||||
getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) {
|
||||
// If the dwarf has mangled name, use mangled name
|
||||
if (auto LinkageName = Die.getLinkageName()) {
|
||||
@@ -627,10 +627,10 @@ void DwarfTransformer::parseCallSiteInfoFromDwarf(CUInfo &CUI, DWARFDie Die,
|
||||
|
||||
// Include the full unmangled name if available, otherwise the short name.
|
||||
if (const char *LinkName = OriginDie.getLinkageName()) {
|
||||
uint32_t LinkNameOff = Gsym.insertString(LinkName, /*Copy=*/false);
|
||||
gsym_strp_t LinkNameOff = Gsym.insertString(LinkName, /*Copy=*/false);
|
||||
CSI.MatchRegex.push_back(LinkNameOff);
|
||||
} else if (const char *ShortName = OriginDie.getShortName()) {
|
||||
uint32_t ShortNameOff = Gsym.insertString(ShortName, /*Copy=*/false);
|
||||
gsym_strp_t ShortNameOff = Gsym.insertString(ShortName, /*Copy=*/false);
|
||||
CSI.MatchRegex.push_back(ShortNameOff);
|
||||
}
|
||||
}
|
||||
@@ -735,9 +735,11 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath,
|
||||
OutputAggregator &Out) {
|
||||
Out << "Verifying GSYM file \"" << GsymPath << "\":\n";
|
||||
|
||||
auto Gsym = GsymReader::openFile(GsymPath);
|
||||
if (!Gsym)
|
||||
return Gsym.takeError();
|
||||
llvm::Expected<std::unique_ptr<GsymReader>> GsymOrErr =
|
||||
GsymReader::openFile(GsymPath);
|
||||
if (!GsymOrErr)
|
||||
return GsymOrErr.takeError();
|
||||
std::unique_ptr<GsymReader> &Gsym = *GsymOrErr;
|
||||
|
||||
auto NumAddrs = Gsym->getNumAddresses();
|
||||
DILineInfoSpecifier DLIS(
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/ExtractRanges.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace llvm {
|
||||
@@ -20,7 +20,7 @@ void encodeRange(const AddressRange &Range, FileWriter &O, uint64_t BaseAddr) {
|
||||
O.writeULEB(Range.size());
|
||||
}
|
||||
|
||||
AddressRange decodeRange(DataExtractor &Data, uint64_t BaseAddr,
|
||||
AddressRange decodeRange(GsymDataExtractor &Data, uint64_t BaseAddr,
|
||||
uint64_t &Offset) {
|
||||
const uint64_t AddrOffset = Data.getULEB128(&Offset);
|
||||
const uint64_t Size = Data.getULEB128(&Offset);
|
||||
@@ -38,8 +38,8 @@ void encodeRanges(const AddressRanges &Ranges, FileWriter &O,
|
||||
encodeRange(Range, O, BaseAddr);
|
||||
}
|
||||
|
||||
void decodeRanges(AddressRanges &Ranges, DataExtractor &Data, uint64_t BaseAddr,
|
||||
uint64_t &Offset) {
|
||||
void decodeRanges(AddressRanges &Ranges, GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr, uint64_t &Offset) {
|
||||
Ranges.clear();
|
||||
uint64_t NumRanges = Data.getULEB128(&Offset);
|
||||
Ranges.reserve(NumRanges);
|
||||
@@ -47,12 +47,12 @@ void decodeRanges(AddressRanges &Ranges, DataExtractor &Data, uint64_t BaseAddr,
|
||||
Ranges.insert(decodeRange(Data, BaseAddr, Offset));
|
||||
}
|
||||
|
||||
void skipRange(DataExtractor &Data, uint64_t &Offset) {
|
||||
void skipRange(GsymDataExtractor &Data, uint64_t &Offset) {
|
||||
Data.getULEB128(&Offset);
|
||||
Data.getULEB128(&Offset);
|
||||
}
|
||||
|
||||
uint64_t skipRanges(DataExtractor &Data, uint64_t &Offset) {
|
||||
uint64_t skipRanges(GsymDataExtractor &Data, uint64_t &Offset) {
|
||||
uint64_t NumRanges = Data.getULEB128(&Offset);
|
||||
for (uint64_t I = 0; I < NumRanges; ++I)
|
||||
skipRange(Data, Offset);
|
||||
|
||||
@@ -16,6 +16,10 @@ using namespace gsym;
|
||||
|
||||
FileWriter::~FileWriter() { OS.flush(); }
|
||||
|
||||
void FileWriter::writeStringOffset(uint64_t Value) {
|
||||
writeUnsigned(Value, StringOffsetSize);
|
||||
}
|
||||
|
||||
void FileWriter::writeSLEB(int64_t S) {
|
||||
uint8_t Bytes[32];
|
||||
auto Length = encodeSLEB128(S, Bytes);
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymReader.h"
|
||||
#include "llvm/DebugInfo/GSYM/LineTable.h"
|
||||
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/LineTable.h"
|
||||
#include <optional>
|
||||
|
||||
using namespace llvm;
|
||||
@@ -38,7 +39,7 @@ raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) {
|
||||
return OS;
|
||||
}
|
||||
|
||||
llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
|
||||
llvm::Expected<FunctionInfo> FunctionInfo::decode(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr) {
|
||||
FunctionInfo FI;
|
||||
uint64_t Offset = 0;
|
||||
@@ -49,11 +50,12 @@ llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, 4))
|
||||
return createStringError(std::errc::io_error,
|
||||
"0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset);
|
||||
FI.Name = Data.getU32(&Offset);
|
||||
FI.Name = Data.getStringOffset(&Offset);
|
||||
if (FI.Name == 0)
|
||||
return createStringError(std::errc::io_error,
|
||||
"0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x",
|
||||
Offset - 4, FI.Name);
|
||||
"0x%8.8" PRIx64
|
||||
": invalid FunctionInfo Name value 0x%" PRIx64,
|
||||
Offset - Data.getStringOffsetSize(), FI.Name);
|
||||
bool Done = false;
|
||||
while (!Done) {
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, 4))
|
||||
@@ -68,8 +70,7 @@ llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
|
||||
return createStringError(std::errc::io_error,
|
||||
"0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u",
|
||||
Offset, IT);
|
||||
DataExtractor InfoData(Data.getData().substr(Offset, InfoLength),
|
||||
Data.isLittleEndian());
|
||||
GsymDataExtractor InfoData(Data, Offset, InfoLength);
|
||||
switch (IT) {
|
||||
case InfoType::EndOfList:
|
||||
Done = true;
|
||||
@@ -115,12 +116,13 @@ llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
|
||||
return std::move(FI);
|
||||
}
|
||||
|
||||
uint64_t FunctionInfo::cacheEncoding() {
|
||||
uint64_t FunctionInfo::cacheEncoding(GsymCreator &GC) {
|
||||
EncodingCache.clear();
|
||||
if (!isValid())
|
||||
return 0;
|
||||
raw_svector_ostream OutStrm(EncodingCache);
|
||||
FileWriter FW(OutStrm, llvm::endianness::native);
|
||||
FW.setStringOffsetSize(GC.getStringOffsetSize());
|
||||
llvm::Expected<uint64_t> Result = encode(FW);
|
||||
if (!Result) {
|
||||
EncodingCache.clear();
|
||||
@@ -153,8 +155,8 @@ llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &Out,
|
||||
// Write the size in bytes of this function as a uint32_t. This can be zero
|
||||
// if we just have a symbol from a symbol table and that symbol has no size.
|
||||
Out.writeU32(size());
|
||||
// Write the name of this function as a uint32_t string table offset.
|
||||
Out.writeU32(Name);
|
||||
// Write the name of this function as a string table offset.
|
||||
Out.writeStringOffset(Name);
|
||||
|
||||
if (OptLineTable) {
|
||||
Out.writeU32(InfoType::LineTableInfo);
|
||||
@@ -235,14 +237,14 @@ llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &Out,
|
||||
}
|
||||
|
||||
llvm::Expected<LookupResult>
|
||||
FunctionInfo::lookup(DataExtractor &Data, const GsymReader &GR,
|
||||
FunctionInfo::lookup(GsymDataExtractor &Data, const GsymReader &GR,
|
||||
uint64_t FuncAddr, uint64_t Addr,
|
||||
std::optional<DataExtractor> *MergedFuncsData) {
|
||||
std::optional<GsymDataExtractor> *MergedFuncsData) {
|
||||
LookupResult LR;
|
||||
LR.LookupAddr = Addr;
|
||||
uint64_t Offset = 0;
|
||||
LR.FuncRange = {FuncAddr, FuncAddr + Data.getU32(&Offset)};
|
||||
uint32_t NameOffset = Data.getU32(&Offset);
|
||||
gsym_strp_t NameOffset = Data.getStringOffset(&Offset);
|
||||
// The "lookup" functions doesn't report errors as accurately as the "decode"
|
||||
// function as it is meant to be fast. For more accurage errors we could call
|
||||
// "decode".
|
||||
@@ -258,12 +260,13 @@ FunctionInfo::lookup(DataExtractor &Data, const GsymReader &GR,
|
||||
|
||||
if (NameOffset == 0)
|
||||
return createStringError(std::errc::io_error,
|
||||
"0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000",
|
||||
Offset - 4);
|
||||
"0x%8.8" PRIx64
|
||||
": invalid FunctionInfo Name value 0x0",
|
||||
Offset - Data.getStringOffsetSize());
|
||||
LR.FuncName = GR.getString(NameOffset);
|
||||
bool Done = false;
|
||||
std::optional<LineEntry> LineEntry;
|
||||
std::optional<DataExtractor> InlineInfoData;
|
||||
std::optional<GsymDataExtractor> InlineInfoData;
|
||||
while (!Done) {
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, 8))
|
||||
return createStringError(std::errc::io_error,
|
||||
@@ -274,7 +277,7 @@ FunctionInfo::lookup(DataExtractor &Data, const GsymReader &GR,
|
||||
if (InfoLength != InfoBytes.size())
|
||||
return createStringError(std::errc::io_error,
|
||||
"FunctionInfo data is truncated");
|
||||
DataExtractor InfoData(InfoBytes, Data.isLittleEndian());
|
||||
GsymDataExtractor InfoData(Data, Offset, InfoLength);
|
||||
switch (IT) {
|
||||
case InfoType::EndOfList:
|
||||
Done = true;
|
||||
@@ -306,7 +309,7 @@ FunctionInfo::lookup(DataExtractor &Data, const GsymReader &GR,
|
||||
// Check if the call site matches the lookup address
|
||||
if (CS.ReturnOffset == Addr - FuncAddr) {
|
||||
// Get regex patterns
|
||||
for (uint32_t RegexOffset : CS.MatchRegex) {
|
||||
for (gsym_strp_t RegexOffset : CS.MatchRegex) {
|
||||
LR.CallSiteFuncRegex.push_back(GR.getString(RegexOffset));
|
||||
}
|
||||
break;
|
||||
|
||||
53
llvm/lib/DebugInfo/GSYM/GlobalData.cpp
Normal file
53
llvm/lib/DebugInfo/GSYM/GlobalData.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
//===- GlobalData.cpp -----------------------------------------------------===//
|
||||
//
|
||||
// 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/GlobalData.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
void GlobalData::encode(FileWriter &O) const {
|
||||
O.writeU32(static_cast<uint32_t>(Type));
|
||||
O.writeU64(FileOffset);
|
||||
O.writeU64(FileSize);
|
||||
}
|
||||
|
||||
llvm::Expected<GlobalData> GlobalData::decode(GsymDataExtractor &GsymData,
|
||||
uint64_t &Offset) {
|
||||
if (!GsymData.isValidOffsetForDataOfSize(Offset, 20))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"not enough data for a GlobalData entry");
|
||||
GlobalData GD;
|
||||
GD.Type = static_cast<GlobalInfoType>(GsymData.getU32(&Offset));
|
||||
GD.FileOffset = GsymData.getU64(&Offset);
|
||||
GD.FileSize = GsymData.getU64(&Offset);
|
||||
return GD;
|
||||
}
|
||||
|
||||
StringRef llvm::gsym::getNameForGlobalInfoType(GlobalInfoType Type) {
|
||||
switch (Type) {
|
||||
case GlobalInfoType::EndOfList:
|
||||
return "EndOfList";
|
||||
case GlobalInfoType::AddrOffsets:
|
||||
return "AddrOffsets";
|
||||
case GlobalInfoType::AddrInfoOffsets:
|
||||
return "AddrInfoOffsets";
|
||||
case GlobalInfoType::StringTable:
|
||||
return "StringTable";
|
||||
case GlobalInfoType::FileTable:
|
||||
return "FileTable";
|
||||
case GlobalInfoType::FunctionInfo:
|
||||
return "FunctionInfo";
|
||||
case GlobalInfoType::UUID:
|
||||
return "UUID";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
@@ -33,8 +33,8 @@ uint32_t GsymCreator::insertFile(StringRef Path, llvm::sys::path::Style Style) {
|
||||
// If we inline the insertString() function call into the constructor, the
|
||||
// call order is undefined due to parameter lists not having any ordering
|
||||
// requirements.
|
||||
const uint32_t Dir = insertString(directory);
|
||||
const uint32_t Base = insertString(filename);
|
||||
const gsym_strp_t Dir = insertString(directory);
|
||||
const gsym_strp_t Base = insertString(filename);
|
||||
return insertFileEntry(FileEntry(Dir, Base));
|
||||
}
|
||||
|
||||
@@ -56,11 +56,11 @@ uint32_t GsymCreator::copyFile(const GsymCreator &SrcGC, uint32_t FileIdx) {
|
||||
return 0;
|
||||
const FileEntry SrcFE = SrcGC.Files[FileIdx];
|
||||
// Copy the strings for the file and then add the newly converted file entry.
|
||||
uint32_t Dir =
|
||||
gsym_strp_t Dir =
|
||||
SrcFE.Dir == 0
|
||||
? 0
|
||||
: StrTab.add(SrcGC.StringOffsetMap.find(SrcFE.Dir)->second);
|
||||
uint32_t Base = StrTab.add(SrcGC.StringOffsetMap.find(SrcFE.Base)->second);
|
||||
gsym_strp_t Base = StrTab.add(SrcGC.StringOffsetMap.find(SrcFE.Base)->second);
|
||||
FileEntry DstFE(Dir, Base);
|
||||
return insertFileEntry(DstFE);
|
||||
}
|
||||
@@ -74,135 +74,10 @@ llvm::Error GsymCreator::save(StringRef Path, llvm::endianness ByteOrder,
|
||||
if (EC)
|
||||
return llvm::errorCodeToError(EC);
|
||||
FileWriter O(OutStrm, ByteOrder);
|
||||
O.setStringOffsetSize(getStringOffsetSize());
|
||||
return encode(O);
|
||||
}
|
||||
|
||||
llvm::Error GsymCreator::encode(FileWriter &O) const {
|
||||
std::lock_guard<std::mutex> Guard(Mutex);
|
||||
if (Funcs.empty())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"no functions to encode");
|
||||
if (!Finalized)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"GsymCreator wasn't finalized prior to encoding");
|
||||
|
||||
if (Funcs.size() > UINT32_MAX)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"too many FunctionInfos");
|
||||
|
||||
std::optional<uint64_t> BaseAddress = getBaseAddress();
|
||||
// Base address should be valid if we have any functions.
|
||||
if (!BaseAddress)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid base address");
|
||||
Header Hdr;
|
||||
Hdr.Magic = GSYM_MAGIC;
|
||||
Hdr.Version = GSYM_VERSION;
|
||||
Hdr.AddrOffSize = getAddressOffsetSize();
|
||||
Hdr.UUIDSize = static_cast<uint8_t>(UUID.size());
|
||||
Hdr.BaseAddress = *BaseAddress;
|
||||
Hdr.NumAddresses = static_cast<uint32_t>(Funcs.size());
|
||||
Hdr.StrtabOffset = 0; // We will fix this up later.
|
||||
Hdr.StrtabSize = 0; // We will fix this up later.
|
||||
memset(Hdr.UUID, 0, sizeof(Hdr.UUID));
|
||||
if (UUID.size() > sizeof(Hdr.UUID))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid UUID size %u", (uint32_t)UUID.size());
|
||||
// Copy the UUID value if we have one.
|
||||
if (UUID.size() > 0)
|
||||
memcpy(Hdr.UUID, UUID.data(), UUID.size());
|
||||
// Write out the header.
|
||||
llvm::Error Err = Hdr.encode(O);
|
||||
if (Err)
|
||||
return Err;
|
||||
|
||||
const uint64_t MaxAddressOffset = getMaxAddressOffset();
|
||||
// Write out the address offsets.
|
||||
O.alignTo(Hdr.AddrOffSize);
|
||||
for (const auto &FuncInfo : Funcs) {
|
||||
uint64_t AddrOffset = FuncInfo.startAddress() - Hdr.BaseAddress;
|
||||
// Make sure we calculated the address offsets byte size correctly by
|
||||
// verifying the current address offset is within ranges. We have seen bugs
|
||||
// introduced when the code changes that can cause problems here so it is
|
||||
// good to catch this during testing.
|
||||
assert(AddrOffset <= MaxAddressOffset);
|
||||
(void)MaxAddressOffset;
|
||||
switch (Hdr.AddrOffSize) {
|
||||
case 1:
|
||||
O.writeU8(static_cast<uint8_t>(AddrOffset));
|
||||
break;
|
||||
case 2:
|
||||
O.writeU16(static_cast<uint16_t>(AddrOffset));
|
||||
break;
|
||||
case 4:
|
||||
O.writeU32(static_cast<uint32_t>(AddrOffset));
|
||||
break;
|
||||
case 8:
|
||||
O.writeU64(AddrOffset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Write out all zeros for the AddrInfoOffsets.
|
||||
O.alignTo(4);
|
||||
const uint64_t AddrInfoOffsetsOffset = O.tell();
|
||||
for (size_t i = 0, n = Funcs.size(); i < n; ++i)
|
||||
O.writeU32(0);
|
||||
|
||||
// Write out the file table
|
||||
O.alignTo(4);
|
||||
assert(!Files.empty());
|
||||
assert(Files[0].Dir == 0);
|
||||
assert(Files[0].Base == 0);
|
||||
size_t NumFiles = Files.size();
|
||||
if (NumFiles > UINT32_MAX)
|
||||
return createStringError(std::errc::invalid_argument, "too many files");
|
||||
O.writeU32(static_cast<uint32_t>(NumFiles));
|
||||
for (auto File : Files) {
|
||||
O.writeU32(File.Dir);
|
||||
O.writeU32(File.Base);
|
||||
}
|
||||
|
||||
// Write out the string table.
|
||||
const uint64_t StrtabOffset = O.tell();
|
||||
StrTab.write(O.get_stream());
|
||||
const uint64_t StrtabSize = O.tell() - StrtabOffset;
|
||||
std::vector<uint32_t> AddrInfoOffsets;
|
||||
|
||||
// Verify that the size of the string table does not exceed 32-bit max.
|
||||
// This means the offsets in the string table will not exceed 32-bit max.
|
||||
if (StrtabSize > UINT32_MAX) {
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"string table size exceeded 32-bit max");
|
||||
}
|
||||
|
||||
// Write out the address infos for each function info.
|
||||
for (const auto &FuncInfo : Funcs) {
|
||||
if (Expected<uint64_t> OffsetOrErr = FuncInfo.encode(O)) {
|
||||
// Verify that the address info offsets do not exceed 32-bit max.
|
||||
uint64_t Offset = OffsetOrErr.get();
|
||||
if (Offset > UINT32_MAX) {
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"address info offset exceeded 32-bit max");
|
||||
}
|
||||
|
||||
AddrInfoOffsets.push_back(Offset);
|
||||
} else
|
||||
return OffsetOrErr.takeError();
|
||||
}
|
||||
// Fixup the string table offset and size in the header
|
||||
O.fixup32((uint32_t)StrtabOffset, offsetof(Header, StrtabOffset));
|
||||
O.fixup32((uint32_t)StrtabSize, offsetof(Header, StrtabSize));
|
||||
|
||||
// Fixup all address info offsets
|
||||
uint64_t Offset = 0;
|
||||
for (auto AddrInfoOffset : AddrInfoOffsets) {
|
||||
O.fixup32(AddrInfoOffset, AddrInfoOffsetsOffset + Offset);
|
||||
Offset += 4;
|
||||
}
|
||||
return ErrorSuccess();
|
||||
}
|
||||
|
||||
llvm::Error GsymCreator::loadCallSitesFromYAML(StringRef YAMLFile) {
|
||||
// Use the loader to load call site information from the YAML file.
|
||||
CallSiteInfoLoader Loader(*this, Funcs);
|
||||
@@ -367,14 +242,15 @@ llvm::Error GsymCreator::finalize(OutputAggregator &Out) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
uint32_t GsymCreator::copyString(const GsymCreator &SrcGC, uint32_t StrOff) {
|
||||
gsym_strp_t GsymCreator::copyString(const GsymCreator &SrcGC,
|
||||
gsym_strp_t StrOff) {
|
||||
// String offset at zero is always the empty string, no copying needed.
|
||||
if (StrOff == 0)
|
||||
return 0;
|
||||
return StrTab.add(SrcGC.StringOffsetMap.find(StrOff)->second);
|
||||
}
|
||||
|
||||
uint32_t GsymCreator::insertString(StringRef S, bool Copy) {
|
||||
gsym_strp_t GsymCreator::insertString(StringRef S, bool Copy) {
|
||||
if (S.empty())
|
||||
return 0;
|
||||
|
||||
@@ -392,7 +268,7 @@ uint32_t GsymCreator::insertString(StringRef S, bool Copy) {
|
||||
CHStr = CachedHashStringRef{StringStorage.insert(S).first->getKey(),
|
||||
CHStr.hash()};
|
||||
}
|
||||
const uint32_t StrOff = StrTab.add(CHStr);
|
||||
const gsym_strp_t StrOff = StrTab.add(CHStr);
|
||||
// Save a mapping of string offsets to the cached string reference in case
|
||||
// we need to segment the GSYM file and copy string from one string table to
|
||||
// another.
|
||||
@@ -400,7 +276,7 @@ uint32_t GsymCreator::insertString(StringRef S, bool Copy) {
|
||||
return StrOff;
|
||||
}
|
||||
|
||||
StringRef GsymCreator::getString(uint32_t Offset) {
|
||||
StringRef GsymCreator::getString(gsym_strp_t Offset) {
|
||||
auto I = StringOffsetMap.find(Offset);
|
||||
assert(I != StringOffsetMap.end() &&
|
||||
"GsymCreator::getString expects a valid offset as parameter.");
|
||||
@@ -493,19 +369,67 @@ uint8_t GsymCreator::getAddressOffsetSize() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t GsymCreator::calculateHeaderAndTableSize() const {
|
||||
uint64_t Size = sizeof(Header);
|
||||
const size_t NumFuncs = Funcs.size();
|
||||
// Add size of address offset table
|
||||
Size += NumFuncs * getAddressOffsetSize();
|
||||
// Add size of address info offsets which are 32 bit integers in version 1.
|
||||
Size += NumFuncs * sizeof(uint32_t);
|
||||
// Add file table size
|
||||
Size += Files.size() * sizeof(FileEntry);
|
||||
// Add string table size
|
||||
Size += StrTab.getSize();
|
||||
llvm::Error
|
||||
GsymCreator::validateForEncoding(std::optional<uint64_t> &BaseAddr) const {
|
||||
if (Funcs.empty())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"no functions to encode");
|
||||
if (!Finalized)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"GsymCreator wasn't finalized prior to encoding");
|
||||
if (Funcs.size() > UINT32_MAX)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"too many FunctionInfos");
|
||||
BaseAddr = getBaseAddress();
|
||||
if (!BaseAddr)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid base address");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
return Size;
|
||||
void GsymCreator::encodeAddrOffsets(FileWriter &O, uint8_t AddrOffSize,
|
||||
uint64_t BaseAddr) const {
|
||||
const uint64_t MaxAddressOffset = getMaxAddressOffset();
|
||||
O.alignTo(AddrOffSize);
|
||||
for (const auto &FI : Funcs) {
|
||||
uint64_t AddrOffset = FI.startAddress() - BaseAddr;
|
||||
// Make sure we calculated the address offsets byte size correctly by
|
||||
// verifying the current address offset is within ranges. We have seen bugs
|
||||
// introduced when the code changes that can cause problems here so it is
|
||||
// good to catch this during testing.
|
||||
assert(AddrOffset <= MaxAddressOffset);
|
||||
(void)MaxAddressOffset;
|
||||
switch (AddrOffSize) {
|
||||
case 1:
|
||||
O.writeU8(static_cast<uint8_t>(AddrOffset));
|
||||
break;
|
||||
case 2:
|
||||
O.writeU16(static_cast<uint16_t>(AddrOffset));
|
||||
break;
|
||||
case 4:
|
||||
O.writeU32(static_cast<uint32_t>(AddrOffset));
|
||||
break;
|
||||
case 8:
|
||||
O.writeU64(AddrOffset);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unsupported address offset size");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Error GsymCreator::encodeFileTable(FileWriter &O) const {
|
||||
assert(!Files.empty());
|
||||
assert(Files[0].Dir == 0);
|
||||
assert(Files[0].Base == 0);
|
||||
if (Files.size() > UINT32_MAX)
|
||||
return createStringError(std::errc::invalid_argument, "too many files");
|
||||
O.writeU32(static_cast<uint32_t>(Files.size()));
|
||||
for (const auto &File : Files) {
|
||||
O.writeStringOffset(File.Dir);
|
||||
O.writeStringOffset(File.Base);
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
// This function takes a InlineInfo class that was copy constructed from an
|
||||
@@ -549,7 +473,7 @@ uint64_t GsymCreator::copyFunctionInfo(const GsymCreator &SrcGC, size_t FuncIdx)
|
||||
}
|
||||
std::lock_guard<std::mutex> Guard(Mutex);
|
||||
Funcs.emplace_back(DstFI);
|
||||
return Funcs.back().cacheEncoding();
|
||||
return Funcs.back().cacheEncoding(*this);
|
||||
}
|
||||
|
||||
llvm::Error GsymCreator::saveSegments(StringRef Path,
|
||||
@@ -595,7 +519,7 @@ GsymCreator::createSegment(uint64_t SegmentSize, size_t &FuncIdx) const {
|
||||
if (FuncIdx >= Funcs.size())
|
||||
return std::unique_ptr<GsymCreator>();
|
||||
|
||||
std::unique_ptr<GsymCreator> GC(new GsymCreator(/*Quiet=*/true));
|
||||
std::unique_ptr<GsymCreator> GC = createNew(/*Quiet=*/true);
|
||||
|
||||
// Tell the creator that this is a segment.
|
||||
GC->setIsSegment();
|
||||
|
||||
93
llvm/lib/DebugInfo/GSYM/GsymCreatorV1.cpp
Normal file
93
llvm/lib/DebugInfo/GSYM/GsymCreatorV1.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//===- GsymCreatorV1.cpp --------------------------------------------------===//
|
||||
//
|
||||
// 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/GsymCreatorV1.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
uint64_t GsymCreatorV1::calculateHeaderAndTableSize() const {
|
||||
uint64_t Size = sizeof(Header);
|
||||
const size_t NumFuncs = Funcs.size();
|
||||
Size += NumFuncs * getAddressOffsetSize();
|
||||
Size += NumFuncs * sizeof(uint32_t);
|
||||
Size += Files.size() * FileEntry::getEncodedSize(getStringOffsetSize());
|
||||
Size += StrTab.getSize();
|
||||
return Size;
|
||||
}
|
||||
|
||||
llvm::Error GsymCreatorV1::encode(FileWriter &O) const {
|
||||
std::lock_guard<std::mutex> Guard(Mutex);
|
||||
std::optional<uint64_t> BaseAddress;
|
||||
if (auto Err = validateForEncoding(BaseAddress))
|
||||
return Err;
|
||||
Header Hdr;
|
||||
Hdr.Magic = GSYM_MAGIC;
|
||||
Hdr.Version = Header::getVersion();
|
||||
Hdr.AddrOffSize = getAddressOffsetSize();
|
||||
Hdr.UUIDSize = static_cast<uint8_t>(UUID.size());
|
||||
Hdr.BaseAddress = *BaseAddress;
|
||||
Hdr.NumAddresses = static_cast<uint32_t>(Funcs.size());
|
||||
Hdr.StrtabOffset = 0;
|
||||
Hdr.StrtabSize = 0;
|
||||
memset(Hdr.UUID, 0, sizeof(Hdr.UUID));
|
||||
if (UUID.size() > sizeof(Hdr.UUID))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid UUID size %u", (uint32_t)UUID.size());
|
||||
if (UUID.size() > 0)
|
||||
memcpy(Hdr.UUID, UUID.data(), UUID.size());
|
||||
llvm::Error Err = Hdr.encode(O);
|
||||
if (Err)
|
||||
return Err;
|
||||
|
||||
O.setStringOffsetSize(getStringOffsetSize());
|
||||
encodeAddrOffsets(O, Hdr.AddrOffSize, Hdr.BaseAddress);
|
||||
|
||||
O.alignTo(4);
|
||||
const uint64_t AddrInfoOffsetsOffset = O.tell();
|
||||
for (size_t i = 0, n = Funcs.size(); i < n; ++i)
|
||||
O.writeU32(0);
|
||||
|
||||
O.alignTo(4);
|
||||
if (auto Err = encodeFileTable(O))
|
||||
return Err;
|
||||
|
||||
const uint64_t StrtabOffset = O.tell();
|
||||
StrTab.write(O.get_stream());
|
||||
const uint64_t StrtabSize = O.tell() - StrtabOffset;
|
||||
std::vector<uint32_t> AddrInfoOffsets;
|
||||
|
||||
if (StrtabSize > UINT32_MAX) {
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"string table size exceeded 32-bit max");
|
||||
}
|
||||
|
||||
for (const auto &FuncInfo : Funcs) {
|
||||
if (Expected<uint64_t> OffsetOrErr = FuncInfo.encode(O)) {
|
||||
uint64_t Offset = OffsetOrErr.get();
|
||||
if (Offset > UINT32_MAX) {
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"address info offset exceeded 32-bit max");
|
||||
}
|
||||
AddrInfoOffsets.push_back(Offset);
|
||||
} else
|
||||
return OffsetOrErr.takeError();
|
||||
}
|
||||
O.fixup32((uint32_t)StrtabOffset, offsetof(Header, StrtabOffset));
|
||||
O.fixup32((uint32_t)StrtabSize, offsetof(Header, StrtabSize));
|
||||
|
||||
uint64_t Offset = 0;
|
||||
for (auto AddrInfoOffset : AddrInfoOffsets) {
|
||||
O.fixup32(AddrInfoOffset, AddrInfoOffsetsOffset + Offset);
|
||||
Offset += 4;
|
||||
}
|
||||
return ErrorSuccess();
|
||||
}
|
||||
166
llvm/lib/DebugInfo/GSYM/GsymCreatorV2.cpp
Normal file
166
llvm/lib/DebugInfo/GSYM/GsymCreatorV2.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
//===- GsymCreatorV2.cpp --------------------------------------------------===//
|
||||
//
|
||||
// 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/GsymCreatorV2.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/GlobalData.h"
|
||||
#include "llvm/DebugInfo/GSYM/HeaderV2.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
uint64_t GsymCreatorV2::calculateHeaderAndTableSize() const {
|
||||
const uint64_t HeaderSize = HeaderV2::getEncodedSize();
|
||||
const size_t NumFuncs = Funcs.size();
|
||||
const uint32_t NumEntries = 5 + (UUID.empty() ? 0 : 1) + 1;
|
||||
uint64_t Size = HeaderSize + NumEntries * 20;
|
||||
Size = llvm::alignTo(Size, getAddressOffsetSize());
|
||||
Size += NumFuncs * getAddressOffsetSize();
|
||||
Size = llvm::alignTo(Size, HeaderV2::getAddressInfoOffsetSize());
|
||||
Size += NumFuncs * HeaderV2::getAddressInfoOffsetSize();
|
||||
Size = llvm::alignTo(Size, 4);
|
||||
Size += 4 + Files.size() * FileEntry::getEncodedSize(getStringOffsetSize());
|
||||
Size += StrTab.getSize();
|
||||
Size += UUID.size();
|
||||
return Size;
|
||||
}
|
||||
|
||||
/// For V2 file layout, see HeaderV2.h
|
||||
llvm::Error GsymCreatorV2::encode(FileWriter &O) const {
|
||||
std::lock_guard<std::mutex> Guard(Mutex);
|
||||
std::optional<uint64_t> BaseAddr;
|
||||
if (auto Err = validateForEncoding(BaseAddr))
|
||||
return Err;
|
||||
|
||||
const uint8_t AddrOffSize = getAddressOffsetSize();
|
||||
|
||||
// Pre-encode all FunctionInfo objects into a temporary buffer so we know the
|
||||
// total FunctionInfo section size and each function's offset within it.
|
||||
SmallVector<char, 0> FIBuf;
|
||||
raw_svector_ostream FIOS(FIBuf);
|
||||
FileWriter FIFW(FIOS, O.getByteOrder());
|
||||
FIFW.setStringOffsetSize(getStringOffsetSize());
|
||||
std::vector<uint64_t> FIRelativeOffsets;
|
||||
for (const auto &FI : Funcs) {
|
||||
if (auto OffOrErr = FI.encode(FIFW))
|
||||
FIRelativeOffsets.push_back(*OffOrErr);
|
||||
else
|
||||
return OffOrErr.takeError();
|
||||
}
|
||||
const uint64_t FISectionSize = FIBuf.size();
|
||||
const uint64_t StringTableSize = StrTab.getSize();
|
||||
|
||||
const uint8_t StrpSize = 8;
|
||||
|
||||
const bool HasUUID = !UUID.empty();
|
||||
const uint32_t NumGlobalDataEntries = 5 + (HasUUID ? 1 : 0) + 1;
|
||||
const uint64_t GlobalDataArraySize =
|
||||
static_cast<uint64_t>(NumGlobalDataEntries) * 20;
|
||||
|
||||
const uint64_t HeaderSize = HeaderV2::getEncodedSize();
|
||||
uint64_t CurOffset = HeaderSize + GlobalDataArraySize;
|
||||
|
||||
// UUID section (first, no alignment requirement).
|
||||
const uint64_t UUIDOffset = CurOffset;
|
||||
const uint64_t UUIDSectionSize = UUID.size();
|
||||
if (HasUUID)
|
||||
CurOffset += UUIDSectionSize;
|
||||
|
||||
// AddrOffsets section.
|
||||
CurOffset = llvm::alignTo(CurOffset, AddrOffSize);
|
||||
const uint64_t AddrOffsetsOffset = CurOffset;
|
||||
const uint64_t AddrOffsetsSize = Funcs.size() * AddrOffSize;
|
||||
CurOffset += AddrOffsetsSize;
|
||||
|
||||
// AddrInfoOffsets section.
|
||||
const uint8_t AddrInfoOffSize = 8;
|
||||
CurOffset = llvm::alignTo(CurOffset, AddrInfoOffSize);
|
||||
const uint64_t AddrInfoOffsetsOffset = CurOffset;
|
||||
const uint64_t AddrInfoOffsetsSize = Funcs.size() * AddrInfoOffSize;
|
||||
CurOffset += AddrInfoOffsetsSize;
|
||||
|
||||
// FileTable section.
|
||||
CurOffset = llvm::alignTo(CurOffset, 4);
|
||||
const uint64_t FileTableOffset = CurOffset;
|
||||
const uint64_t FileTableSize =
|
||||
4 + Files.size() * FileEntry::getEncodedSize(StrpSize);
|
||||
CurOffset += FileTableSize;
|
||||
|
||||
// StringTable section.
|
||||
const uint64_t StringTableOffset = CurOffset;
|
||||
CurOffset += StringTableSize;
|
||||
|
||||
// FunctionInfo section.
|
||||
CurOffset = llvm::alignTo(CurOffset, 4);
|
||||
const uint64_t FISectionOffset = CurOffset;
|
||||
CurOffset += FISectionSize;
|
||||
|
||||
// Build and write the header.
|
||||
HeaderV2 Hdr;
|
||||
Hdr.Magic = GSYM_MAGIC;
|
||||
Hdr.Version = HeaderV2::getVersion();
|
||||
Hdr.BaseAddress = *BaseAddr;
|
||||
Hdr.NumAddresses = static_cast<uint32_t>(Funcs.size());
|
||||
Hdr.AddrOffSize = AddrOffSize;
|
||||
Hdr.StrTableEncoding = StringTableEncoding::Default;
|
||||
if (auto Err = Hdr.encode(O))
|
||||
return Err;
|
||||
|
||||
// Write GlobalData entries.
|
||||
if (HasUUID)
|
||||
GlobalData{GlobalInfoType::UUID, UUIDOffset, UUIDSectionSize}.encode(O);
|
||||
GlobalData{GlobalInfoType::AddrOffsets, AddrOffsetsOffset, AddrOffsetsSize}
|
||||
.encode(O);
|
||||
GlobalData{GlobalInfoType::AddrInfoOffsets, AddrInfoOffsetsOffset,
|
||||
AddrInfoOffsetsSize}
|
||||
.encode(O);
|
||||
GlobalData{GlobalInfoType::FileTable, FileTableOffset, FileTableSize}.encode(
|
||||
O);
|
||||
GlobalData{GlobalInfoType::StringTable, StringTableOffset, StringTableSize}
|
||||
.encode(O);
|
||||
GlobalData{GlobalInfoType::FunctionInfo, FISectionOffset, FISectionSize}
|
||||
.encode(O);
|
||||
GlobalData{GlobalInfoType::EndOfList, 0, 0}.encode(O);
|
||||
|
||||
// Write UUID section.
|
||||
if (HasUUID) {
|
||||
assert(O.tell() == UUIDOffset);
|
||||
O.writeData(ArrayRef<uint8_t>(UUID.data(), UUID.size()));
|
||||
}
|
||||
|
||||
// Write AddrOffsets section.
|
||||
O.alignTo(AddrOffSize);
|
||||
assert(O.tell() == AddrOffsetsOffset);
|
||||
encodeAddrOffsets(O, AddrOffSize, *BaseAddr);
|
||||
|
||||
// Write AddrInfoOffsets section. Values are relative to FunctionInfo section.
|
||||
O.alignTo(AddrInfoOffSize);
|
||||
assert(O.tell() == AddrInfoOffsetsOffset);
|
||||
for (uint64_t RelOff : FIRelativeOffsets)
|
||||
O.writeU64(RelOff);
|
||||
|
||||
// Write FileTable section.
|
||||
O.alignTo(4);
|
||||
assert(O.tell() == FileTableOffset);
|
||||
if (auto Err = encodeFileTable(O))
|
||||
return Err;
|
||||
|
||||
// Write StringTable section.
|
||||
assert(O.tell() == StringTableOffset);
|
||||
StrTab.write(O.get_stream());
|
||||
|
||||
// Write FunctionInfo section.
|
||||
O.alignTo(4);
|
||||
assert(O.tell() == FISectionOffset);
|
||||
O.writeData(ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(FIBuf.data()),
|
||||
FIBuf.size()));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
@@ -13,23 +13,58 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/GsymReaderV1.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymReaderV2.h"
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
#include "llvm/DebugInfo/GSYM/HeaderV2.h"
|
||||
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/LineTable.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer)
|
||||
: MemBuffer(std::move(Buffer)), Endian(llvm::endianness::native) {}
|
||||
GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer,
|
||||
llvm::endianness Endian)
|
||||
: MemBuffer(std::move(Buffer)), Endian(Endian),
|
||||
AddrInfoOffsetsData(StringRef(), true), FileEntryData(StringRef(), true) {
|
||||
}
|
||||
|
||||
GsymReader::GsymReader(GsymReader &&RHS) = default;
|
||||
/// Check magic bytes, determine endianness, and return the GSYM version and
|
||||
/// endianness. If magic bytes are invalid, return error.
|
||||
static Expected<std::pair<uint16_t, llvm::endianness>>
|
||||
checkMagicAndDetectVersionEndian(StringRef Bytes) {
|
||||
if (Bytes.size() < 6)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"data too small to be a GSYM file");
|
||||
// Detect host endian
|
||||
const auto HostEndian = llvm::endianness::native;
|
||||
const bool IsHostLittleEndian = (HostEndian == llvm::endianness::little);
|
||||
// Read magic bytes using host endian
|
||||
GsymDataExtractor Data(Bytes, IsHostLittleEndian);
|
||||
uint64_t Offset = 0;
|
||||
uint32_t Magic = Data.getU32(&Offset);
|
||||
llvm::endianness FileEndian;
|
||||
// If magic bytes looks alright, the host and the file have the same
|
||||
// endianness, vice versa.
|
||||
if (Magic == GSYM_MAGIC) {
|
||||
FileEndian = HostEndian;
|
||||
} else if (Magic == GSYM_CIGAM) {
|
||||
FileEndian =
|
||||
IsHostLittleEndian ? llvm::endianness::big : llvm::endianness::little;
|
||||
// Re-create GsymDataExtractor with correct endianness to read version.
|
||||
Data = GsymDataExtractor(Bytes, !IsHostLittleEndian);
|
||||
} else {
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"not a GSYM file (bad magic)");
|
||||
}
|
||||
// Read version using the correct endian
|
||||
uint16_t Version = Data.getU16(&Offset);
|
||||
return std::make_pair(Version, FileEndian);
|
||||
}
|
||||
|
||||
GsymReader::~GsymReader() = default;
|
||||
|
||||
llvm::Expected<GsymReader> GsymReader::openFile(StringRef Filename) {
|
||||
llvm::Expected<std::unique_ptr<GsymReader>>
|
||||
GsymReader::openFile(StringRef Filename) {
|
||||
// Open the input file and return an appropriate error if needed.
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
|
||||
MemoryBuffer::getFileOrSTDIN(Filename);
|
||||
@@ -39,194 +74,273 @@ llvm::Expected<GsymReader> GsymReader::openFile(StringRef Filename) {
|
||||
return create(BuffOrErr.get());
|
||||
}
|
||||
|
||||
llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) {
|
||||
llvm::Expected<std::unique_ptr<GsymReader>>
|
||||
GsymReader::copyBuffer(StringRef Bytes) {
|
||||
auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");
|
||||
return create(MemBuffer);
|
||||
}
|
||||
|
||||
llvm::Expected<llvm::gsym::GsymReader>
|
||||
llvm::Expected<std::unique_ptr<GsymReader>>
|
||||
GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
|
||||
if (!MemBuffer)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid memory buffer");
|
||||
GsymReader GR(std::move(MemBuffer));
|
||||
llvm::Error Err = GR.parse();
|
||||
if (Err)
|
||||
Expected<std::pair<uint16_t, llvm::endianness>> VersionEndianOrErr =
|
||||
checkMagicAndDetectVersionEndian(MemBuffer->getBuffer());
|
||||
if (!VersionEndianOrErr)
|
||||
return VersionEndianOrErr.takeError();
|
||||
uint16_t Version;
|
||||
llvm::endianness Endian;
|
||||
std::tie(Version, Endian) = *VersionEndianOrErr;
|
||||
std::unique_ptr<GsymReader> GR;
|
||||
switch (Version) {
|
||||
case Header::getVersion():
|
||||
GR.reset(new GsymReaderV1(std::move(MemBuffer), Endian));
|
||||
break;
|
||||
case HeaderV2::getVersion():
|
||||
GR.reset(new GsymReaderV2(std::move(MemBuffer), Endian));
|
||||
break;
|
||||
default:
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"unsupported GSYM version %u", Version);
|
||||
}
|
||||
if (auto Err = GR->parse())
|
||||
return std::move(Err);
|
||||
return std::move(GR);
|
||||
}
|
||||
|
||||
llvm::Error
|
||||
GsymReader::parse() {
|
||||
BinaryStreamReader FileData(MemBuffer->getBuffer(), llvm::endianness::native);
|
||||
// Check for the magic bytes. This file format is designed to be mmap'ed
|
||||
// into a process and accessed as read only. This is done for performance
|
||||
// and efficiency for symbolicating and parsing GSYM data.
|
||||
if (FileData.readObject(Hdr))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"not enough data for a GSYM header");
|
||||
|
||||
const auto HostByteOrder = llvm::endianness::native;
|
||||
switch (Hdr->Magic) {
|
||||
case GSYM_MAGIC:
|
||||
Endian = HostByteOrder;
|
||||
break;
|
||||
case GSYM_CIGAM:
|
||||
// This is a GSYM file, but not native endianness.
|
||||
Endian = sys::IsBigEndianHost ? llvm::endianness::little
|
||||
: llvm::endianness::big;
|
||||
Swap.reset(new SwappedData);
|
||||
break;
|
||||
default:
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"not a GSYM file");
|
||||
}
|
||||
|
||||
bool DataIsLittleEndian = HostByteOrder != llvm::endianness::little;
|
||||
// Read a correctly byte swapped header if we need to.
|
||||
if (Swap) {
|
||||
DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
|
||||
if (auto ExpectedHdr = Header::decode(Data))
|
||||
Swap->Hdr = ExpectedHdr.get();
|
||||
else
|
||||
return ExpectedHdr.takeError();
|
||||
Hdr = &Swap->Hdr;
|
||||
}
|
||||
|
||||
// Detect errors in the header and report any that are found. If we make it
|
||||
// past this without errors, we know we have a good magic value, a supported
|
||||
// version number, verified address offset size and a valid UUID size.
|
||||
if (Error Err = Hdr->checkForError())
|
||||
llvm::Error GsymReader::parse() {
|
||||
// Step 1: Parse the version-specific header and populate GlobalDataSections.
|
||||
if (auto Err = parseHeaderAndGlobalDataEntries())
|
||||
return Err;
|
||||
|
||||
if (!Swap) {
|
||||
// This is the native endianness case that is most common and optimized for
|
||||
// efficient lookups. Here we just grab pointers to the native data and
|
||||
// use ArrayRef objects to allow efficient read only access.
|
||||
// Step 2: Validate that all required sections are present and consistent.
|
||||
for (auto Type :
|
||||
{GlobalInfoType::AddrOffsets, GlobalInfoType::AddrInfoOffsets,
|
||||
GlobalInfoType::StringTable, GlobalInfoType::FileTable,
|
||||
GlobalInfoType::FunctionInfo})
|
||||
if (!GlobalDataSections.count(Type))
|
||||
return createStringError(
|
||||
std::errc::invalid_argument, "missing required section type %s (%u)",
|
||||
getNameForGlobalInfoType(Type).data(), static_cast<uint32_t>(Type));
|
||||
|
||||
// Read the address offsets.
|
||||
if (FileData.padToAlignment(Hdr->AddrOffSize) ||
|
||||
FileData.readArray(AddrOffsets,
|
||||
Hdr->NumAddresses * Hdr->AddrOffSize))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
if (GlobalDataSections[GlobalInfoType::AddrOffsets].FileSize !=
|
||||
static_cast<uint64_t>(getNumAddresses()) * getAddressOffsetSize())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"AddrOffsets section size mismatch");
|
||||
|
||||
// Read the address info offsets.
|
||||
if (FileData.padToAlignment(4) ||
|
||||
FileData.readArray(AddrInfoOffsets, Hdr->NumAddresses))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address info offsets table");
|
||||
if (GlobalDataSections[GlobalInfoType::AddrInfoOffsets].FileSize !=
|
||||
static_cast<uint64_t>(getNumAddresses()) * getAddressInfoOffsetSize())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"AddrInfoOffsets section size mismatch");
|
||||
|
||||
// Read the file table.
|
||||
uint32_t NumFiles = 0;
|
||||
if (FileData.readInteger(NumFiles) || FileData.readArray(Files, NumFiles))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read file table");
|
||||
// Step 3: Parse each global data section.
|
||||
llvm::Expected<StringRef> Bytes =
|
||||
getRequiredGlobalDataBytes(GlobalInfoType::AddrOffsets);
|
||||
if (!Bytes)
|
||||
return Bytes.takeError();
|
||||
if (auto Err = parseAddrOffsets(*Bytes))
|
||||
return Err;
|
||||
|
||||
// Get the string table.
|
||||
FileData.setOffset(Hdr->StrtabOffset);
|
||||
if (FileData.readFixedString(StrTab.Data, Hdr->StrtabSize))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read string table");
|
||||
} else {
|
||||
// This is the non native endianness case that is not common and not
|
||||
// optimized for lookups. Here we decode the important tables into local
|
||||
// storage and then set the ArrayRef objects to point to these swapped
|
||||
// copies of the read only data so lookups can be as efficient as possible.
|
||||
DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
|
||||
Bytes = getRequiredGlobalDataBytes(GlobalInfoType::AddrInfoOffsets);
|
||||
if (!Bytes)
|
||||
return Bytes.takeError();
|
||||
if (auto Err = setAddrInfoOffsetsData(*Bytes))
|
||||
return Err;
|
||||
|
||||
// Read the address offsets.
|
||||
uint64_t Offset = alignTo(sizeof(Header), Hdr->AddrOffSize);
|
||||
Swap->AddrOffsets.resize(Hdr->NumAddresses * Hdr->AddrOffSize);
|
||||
switch (Hdr->AddrOffSize) {
|
||||
case 1:
|
||||
if (!Data.getU8(&Offset, Swap->AddrOffsets.data(), Hdr->NumAddresses))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
break;
|
||||
case 2:
|
||||
if (!Data.getU16(&Offset,
|
||||
reinterpret_cast<uint16_t *>(Swap->AddrOffsets.data()),
|
||||
Hdr->NumAddresses))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
break;
|
||||
case 4:
|
||||
if (!Data.getU32(&Offset,
|
||||
reinterpret_cast<uint32_t *>(Swap->AddrOffsets.data()),
|
||||
Hdr->NumAddresses))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
break;
|
||||
case 8:
|
||||
if (!Data.getU64(&Offset,
|
||||
reinterpret_cast<uint64_t *>(Swap->AddrOffsets.data()),
|
||||
Hdr->NumAddresses))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
}
|
||||
AddrOffsets = ArrayRef<uint8_t>(Swap->AddrOffsets);
|
||||
Bytes = getRequiredGlobalDataBytes(GlobalInfoType::StringTable);
|
||||
if (!Bytes)
|
||||
return Bytes.takeError();
|
||||
if (auto Err = setStringTableData(*Bytes))
|
||||
return Err;
|
||||
|
||||
Bytes = getRequiredGlobalDataBytes(GlobalInfoType::FileTable);
|
||||
if (!Bytes)
|
||||
return Bytes.takeError();
|
||||
if (auto Err = setFileTableData(*Bytes))
|
||||
return Err;
|
||||
|
||||
// Read the address info offsets.
|
||||
Offset = alignTo(Offset, 4);
|
||||
Swap->AddrInfoOffsets.resize(Hdr->NumAddresses);
|
||||
if (Data.getU32(&Offset, Swap->AddrInfoOffsets.data(), Hdr->NumAddresses))
|
||||
AddrInfoOffsets = ArrayRef<uint32_t>(Swap->AddrInfoOffsets);
|
||||
else
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
// Read the file table.
|
||||
const uint32_t NumFiles = Data.getU32(&Offset);
|
||||
if (NumFiles > 0) {
|
||||
Swap->Files.resize(NumFiles);
|
||||
if (Data.getU32(&Offset, &Swap->Files[0].Dir, NumFiles*2))
|
||||
Files = ArrayRef<FileEntry>(Swap->Files);
|
||||
else
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read file table");
|
||||
}
|
||||
// Get the string table.
|
||||
StrTab.Data = MemBuffer->getBuffer().substr(Hdr->StrtabOffset,
|
||||
Hdr->StrtabSize);
|
||||
if (StrTab.Data.empty())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read string table");
|
||||
}
|
||||
return Error::success();
|
||||
|
||||
}
|
||||
|
||||
const Header &GsymReader::getHeader() const {
|
||||
// The only way to get a GsymReader is from GsymReader::openFile(...) or
|
||||
// GsymReader::copyBuffer() and the header must be valid and initialized to
|
||||
// a valid pointer value, so the assert below should not trigger.
|
||||
assert(Hdr);
|
||||
return *Hdr;
|
||||
llvm::Error GsymReader::parseGlobalDataEntries(uint64_t Offset) {
|
||||
if (getVersion() < HeaderV2::getVersion())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"GlobalData section not supported in GSYM V1");
|
||||
|
||||
const StringRef Buf = MemBuffer->getBuffer();
|
||||
const uint64_t BufSize = Buf.size();
|
||||
GsymDataExtractor Data(Buf, isLittleEndian());
|
||||
while (Offset + sizeof(GlobalData) <= BufSize) {
|
||||
auto GDOrErr = GlobalData::decode(Data, Offset);
|
||||
if (!GDOrErr)
|
||||
return GDOrErr.takeError();
|
||||
const GlobalData &GD = *GDOrErr;
|
||||
|
||||
if (GD.Type == GlobalInfoType::EndOfList)
|
||||
return Error::success();
|
||||
|
||||
if (GD.FileSize == 0)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"GlobalData section type %u has zero size",
|
||||
static_cast<uint32_t>(GD.Type));
|
||||
|
||||
if (GD.FileOffset + GD.FileSize > BufSize)
|
||||
return createStringError(
|
||||
std::errc::invalid_argument,
|
||||
"GlobalData section type %u extends beyond "
|
||||
"buffer (offset=%" PRIu64 ", size=%" PRIu64 ", bufsize=%" PRIu64 ")",
|
||||
static_cast<uint32_t>(GD.Type), GD.FileOffset, GD.FileSize, BufSize);
|
||||
|
||||
GlobalDataSections[GD.Type] = GD;
|
||||
}
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"GlobalData array not terminated by EndOfList");
|
||||
}
|
||||
|
||||
llvm::Error GsymReader::parseAddrOffsets(StringRef Bytes) {
|
||||
const uint8_t AddrOffSize = getAddressOffsetSize();
|
||||
const uint32_t NumAddrs = getNumAddresses();
|
||||
const size_t TotalBytes = NumAddrs * AddrOffSize;
|
||||
if (Bytes.size() < TotalBytes)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
|
||||
// Parse the non-swap case
|
||||
if (Endian == llvm::endianness::native) {
|
||||
AddrOffsets = ArrayRef<uint8_t>(
|
||||
reinterpret_cast<const uint8_t *>(Bytes.data()), TotalBytes);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
// Parse the swap case
|
||||
GsymDataExtractor Data(Bytes, isLittleEndian());
|
||||
uint64_t Offset = 0;
|
||||
SwappedAddrOffsets.resize(TotalBytes);
|
||||
switch (AddrOffSize) {
|
||||
case 1:
|
||||
if (!Data.getU8(&Offset, SwappedAddrOffsets.data(), NumAddrs))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
break;
|
||||
case 2:
|
||||
if (!Data.getU16(&Offset,
|
||||
reinterpret_cast<uint16_t *>(SwappedAddrOffsets.data()),
|
||||
NumAddrs))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
break;
|
||||
case 4:
|
||||
if (!Data.getU32(&Offset,
|
||||
reinterpret_cast<uint32_t *>(SwappedAddrOffsets.data()),
|
||||
NumAddrs))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
break;
|
||||
case 8:
|
||||
if (!Data.getU64(&Offset,
|
||||
reinterpret_cast<uint64_t *>(SwappedAddrOffsets.data()),
|
||||
NumAddrs))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to read address table");
|
||||
break;
|
||||
}
|
||||
AddrOffsets = ArrayRef<uint8_t>(SwappedAddrOffsets);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
llvm::Error GsymReader::setAddrInfoOffsetsData(StringRef Bytes) {
|
||||
AddrInfoOffsetsData = GsymDataExtractor(Bytes, isLittleEndian());
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
llvm::Error GsymReader::setStringTableData(StringRef Bytes) {
|
||||
StrTab.Data = Bytes;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
llvm::Error GsymReader::setFileTableData(StringRef Bytes) {
|
||||
const uint8_t StrpSize = getStringOffsetSize();
|
||||
GsymDataExtractor Data(Bytes, isLittleEndian(), StrpSize);
|
||||
uint64_t Offset = 0;
|
||||
uint32_t NumFiles = Data.getU32(&Offset);
|
||||
uint64_t EntriesSize =
|
||||
static_cast<uint64_t>(NumFiles) * FileEntry::getEncodedSize(StrpSize);
|
||||
if (Bytes.size() < Offset + EntriesSize)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"FileTable section too small for %u files",
|
||||
NumFiles);
|
||||
FileEntryData = GsymDataExtractor(Data, Offset, EntriesSize);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
std::optional<GlobalData> GsymReader::getGlobalData(GlobalInfoType Type) const {
|
||||
auto It = GlobalDataSections.find(Type);
|
||||
if (It == GlobalDataSections.end())
|
||||
return std::nullopt;
|
||||
return It->second;
|
||||
}
|
||||
|
||||
llvm::Expected<StringRef>
|
||||
GsymReader::getRequiredGlobalDataBytes(GlobalInfoType Type) const {
|
||||
if (auto Data = getOptionalGlobalDataBytes(Type))
|
||||
return *Data;
|
||||
const char *TypeName = getNameForGlobalInfoType(Type).data();
|
||||
std::optional<GlobalData> GD = getGlobalData(Type);
|
||||
// We have a GlobalData entry but didn't get any bytes — the file may be
|
||||
// truncated.
|
||||
if (GD)
|
||||
return createStringError(
|
||||
std::errc::invalid_argument,
|
||||
"missing bytes for %s, GSYM file might be truncated", TypeName);
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"missing required section type %s", TypeName);
|
||||
}
|
||||
|
||||
std::optional<StringRef>
|
||||
GsymReader::getOptionalGlobalDataBytes(GlobalInfoType Type) const {
|
||||
std::optional<GlobalData> GD = getGlobalData(Type);
|
||||
if (!GD)
|
||||
return std::nullopt;
|
||||
StringRef Buf = MemBuffer->getBuffer();
|
||||
if (GD->FileSize == 0 || GD->FileOffset + GD->FileSize > Buf.size())
|
||||
return std::nullopt;
|
||||
return Buf.substr(GD->FileOffset, GD->FileSize);
|
||||
}
|
||||
|
||||
std::optional<uint64_t> GsymReader::getAddress(size_t Index) const {
|
||||
switch (Hdr->AddrOffSize) {
|
||||
switch (getAddressOffsetSize()) {
|
||||
case 1: return addressForIndex<uint8_t>(Index);
|
||||
case 2: return addressForIndex<uint16_t>(Index);
|
||||
case 4: return addressForIndex<uint32_t>(Index);
|
||||
case 8: return addressForIndex<uint64_t>(Index);
|
||||
default:
|
||||
llvm_unreachable("unsupported address offset size");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
|
||||
const auto NumAddrInfoOffsets = AddrInfoOffsets.size();
|
||||
if (Index < NumAddrInfoOffsets)
|
||||
return AddrInfoOffsets[Index];
|
||||
return std::nullopt;
|
||||
if (Index >= getNumAddresses())
|
||||
return std::nullopt;
|
||||
const uint8_t AddrInfoOffsetSize = getAddressInfoOffsetSize();
|
||||
uint64_t Offset = Index * AddrInfoOffsetSize;
|
||||
uint64_t AddrInfoOffset =
|
||||
AddrInfoOffsetsData.getUnsigned(&Offset, AddrInfoOffsetSize);
|
||||
// V1 stores absolute file offsets in AddrInfoOffsets, so no base offset is
|
||||
// needed. V2+ stores offsets relative to the FunctionInfo section start.
|
||||
if (getVersion() != Header::getVersion())
|
||||
AddrInfoOffset +=
|
||||
GlobalDataSections.at(GlobalInfoType::FunctionInfo).FileOffset;
|
||||
return AddrInfoOffset;
|
||||
}
|
||||
|
||||
Expected<uint64_t>
|
||||
GsymReader::getAddressIndex(const uint64_t Addr) const {
|
||||
if (Addr >= Hdr->BaseAddress) {
|
||||
const uint64_t AddrOffset = Addr - Hdr->BaseAddress;
|
||||
Expected<uint64_t> GsymReader::getAddressIndex(const uint64_t Addr) const {
|
||||
const uint64_t BaseAddr = getBaseAddress();
|
||||
if (Addr >= BaseAddr) {
|
||||
const uint64_t AddrOffset = Addr - BaseAddr;
|
||||
std::optional<uint64_t> AddrOffsetIndex;
|
||||
switch (Hdr->AddrOffSize) {
|
||||
switch (getAddressOffsetSize()) {
|
||||
case 1:
|
||||
AddrOffsetIndex = getAddressOffsetIndex<uint8_t>(AddrOffset);
|
||||
break;
|
||||
@@ -242,17 +356,16 @@ GsymReader::getAddressIndex(const uint64_t Addr) const {
|
||||
default:
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"unsupported address offset size %u",
|
||||
Hdr->AddrOffSize);
|
||||
getAddressOffsetSize());
|
||||
}
|
||||
if (AddrOffsetIndex)
|
||||
return *AddrOffsetIndex;
|
||||
}
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"address 0x%" PRIx64 " is not in GSYM", Addr);
|
||||
|
||||
}
|
||||
|
||||
llvm::Expected<DataExtractor>
|
||||
llvm::Expected<GsymDataExtractor>
|
||||
GsymReader::getFunctionInfoDataForAddress(uint64_t Addr,
|
||||
uint64_t &FuncStartAddr) const {
|
||||
Expected<uint64_t> ExpectedAddrIdx = getAddressIndex(Addr);
|
||||
@@ -295,26 +408,27 @@ GsymReader::getFunctionInfoDataForAddress(uint64_t Addr,
|
||||
"address 0x%" PRIx64 " is not in GSYM", Addr);
|
||||
}
|
||||
|
||||
llvm::Expected<DataExtractor>
|
||||
llvm::Expected<GsymDataExtractor>
|
||||
GsymReader::getFunctionInfoDataAtIndex(uint64_t AddrIdx,
|
||||
uint64_t &FuncStartAddr) const {
|
||||
if (AddrIdx >= getNumAddresses())
|
||||
const std::optional<uint64_t> AddrInfoOffset = getAddressInfoOffset(AddrIdx);
|
||||
if (AddrInfoOffset == std::nullopt)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid address index %" PRIu64, AddrIdx);
|
||||
const uint32_t AddrInfoOffset = AddrInfoOffsets[AddrIdx];
|
||||
assert((Endian == endianness::big || Endian == endianness::little) &&
|
||||
"Endian must be either big or little");
|
||||
StringRef Bytes = MemBuffer->getBuffer().substr(AddrInfoOffset);
|
||||
StringRef Bytes = MemBuffer->getBuffer().substr(*AddrInfoOffset);
|
||||
if (Bytes.empty())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid address info offset 0x%" PRIx32,
|
||||
AddrInfoOffset);
|
||||
"invalid address info offset 0x%" PRIx64,
|
||||
*AddrInfoOffset);
|
||||
std::optional<uint64_t> OptFuncStartAddr = getAddress(AddrIdx);
|
||||
if (!OptFuncStartAddr)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to extract address[%" PRIu64 "]", AddrIdx);
|
||||
FuncStartAddr = *OptFuncStartAddr;
|
||||
return DataExtractor(Bytes, Endian == llvm::endianness::little, 4);
|
||||
GsymDataExtractor Data(Bytes, isLittleEndian(), getStringOffsetSize());
|
||||
return Data;
|
||||
}
|
||||
|
||||
llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {
|
||||
@@ -334,9 +448,9 @@ GsymReader::getFunctionInfoAtIndex(uint64_t Idx) const {
|
||||
return ExpectedData.takeError();
|
||||
}
|
||||
|
||||
llvm::Expected<LookupResult>
|
||||
GsymReader::lookup(uint64_t Addr,
|
||||
std::optional<DataExtractor> *MergedFunctionsData) const {
|
||||
llvm::Expected<LookupResult> GsymReader::lookup(
|
||||
uint64_t Addr,
|
||||
std::optional<GsymDataExtractor> *MergedFunctionsData) const {
|
||||
uint64_t FuncStartAddr = 0;
|
||||
if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))
|
||||
return FunctionInfo::lookup(*ExpectedData, *this, FuncStartAddr, Addr,
|
||||
@@ -348,7 +462,7 @@ GsymReader::lookup(uint64_t Addr,
|
||||
llvm::Expected<std::vector<LookupResult>>
|
||||
GsymReader::lookupAll(uint64_t Addr) const {
|
||||
std::vector<LookupResult> Results;
|
||||
std::optional<DataExtractor> MergedFunctionsData;
|
||||
std::optional<GsymDataExtractor> MergedFunctionsData;
|
||||
|
||||
// First perform a lookup to get the primary function info result.
|
||||
auto MainResult = lookup(Addr, &MergedFunctionsData);
|
||||
@@ -367,7 +481,7 @@ GsymReader::lookupAll(uint64_t Addr) const {
|
||||
return ExpectedMergedFuncExtractors.takeError();
|
||||
|
||||
// Process each merged function data.
|
||||
for (DataExtractor &MergedData : *ExpectedMergedFuncExtractors) {
|
||||
for (GsymDataExtractor &MergedData : *ExpectedMergedFuncExtractors) {
|
||||
if (auto FI = FunctionInfo::lookup(MergedData, *this,
|
||||
MainResult->FuncRange.start(), Addr)) {
|
||||
Results.push_back(std::move(*FI));
|
||||
@@ -380,61 +494,6 @@ GsymReader::lookupAll(uint64_t Addr) const {
|
||||
return Results;
|
||||
}
|
||||
|
||||
void GsymReader::dump(raw_ostream &OS) {
|
||||
const auto &Header = getHeader();
|
||||
// Dump the GSYM header.
|
||||
OS << Header << "\n";
|
||||
// Dump the address table.
|
||||
OS << "Address Table:\n";
|
||||
OS << "INDEX OFFSET";
|
||||
|
||||
switch (Hdr->AddrOffSize) {
|
||||
case 1: OS << "8 "; break;
|
||||
case 2: OS << "16"; break;
|
||||
case 4: OS << "32"; break;
|
||||
case 8: OS << "64"; break;
|
||||
default: OS << "??"; break;
|
||||
}
|
||||
OS << " (ADDRESS)\n";
|
||||
OS << "====== =============================== \n";
|
||||
for (uint32_t I = 0; I < Header.NumAddresses; ++I) {
|
||||
OS << format("[%4u] ", I);
|
||||
switch (Hdr->AddrOffSize) {
|
||||
case 1: OS << HEX8(getAddrOffsets<uint8_t>()[I]); break;
|
||||
case 2: OS << HEX16(getAddrOffsets<uint16_t>()[I]); break;
|
||||
case 4: OS << HEX32(getAddrOffsets<uint32_t>()[I]); break;
|
||||
case 8: OS << HEX32(getAddrOffsets<uint64_t>()[I]); break;
|
||||
default: break;
|
||||
}
|
||||
OS << " (" << HEX64(*getAddress(I)) << ")\n";
|
||||
}
|
||||
// Dump the address info offsets table.
|
||||
OS << "\nAddress Info Offsets:\n";
|
||||
OS << "INDEX Offset\n";
|
||||
OS << "====== ==========\n";
|
||||
for (uint32_t I = 0; I < Header.NumAddresses; ++I)
|
||||
OS << format("[%4u] ", I) << HEX32(AddrInfoOffsets[I]) << "\n";
|
||||
// Dump the file table.
|
||||
OS << "\nFiles:\n";
|
||||
OS << "INDEX DIRECTORY BASENAME PATH\n";
|
||||
OS << "====== ========== ========== ==============================\n";
|
||||
for (uint32_t I = 0; I < Files.size(); ++I) {
|
||||
OS << format("[%4u] ", I) << HEX32(Files[I].Dir) << ' '
|
||||
<< HEX32(Files[I].Base) << ' ';
|
||||
dump(OS, getFile(I));
|
||||
OS << "\n";
|
||||
}
|
||||
OS << "\n" << StrTab << "\n";
|
||||
|
||||
for (uint32_t I = 0; I < Header.NumAddresses; ++I) {
|
||||
OS << "FunctionInfo @ " << HEX32(AddrInfoOffsets[I]) << ": ";
|
||||
if (auto FI = getFunctionInfoAtIndex(I))
|
||||
dump(OS, *FI);
|
||||
else
|
||||
logAllUnhandledErrors(FI.takeError(), OS, "FunctionInfo:");
|
||||
}
|
||||
}
|
||||
|
||||
void GsymReader::dump(raw_ostream &OS, const FunctionInfo &FI,
|
||||
uint32_t Indent) {
|
||||
OS.indent(Indent);
|
||||
@@ -507,7 +566,7 @@ void GsymReader::dump(raw_ostream &OS, const CallSiteInfoCollection &CSIC,
|
||||
void GsymReader::dump(raw_ostream &OS, const LineTable <, uint32_t Indent) {
|
||||
OS.indent(Indent);
|
||||
OS << "LineTable:\n";
|
||||
for (auto &LE: LT) {
|
||||
for (auto &LE : LT) {
|
||||
OS.indent(Indent);
|
||||
OS << " " << HEX64(LE.Addr) << ' ';
|
||||
if (LE.File)
|
||||
@@ -530,7 +589,7 @@ void GsymReader::dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent) {
|
||||
}
|
||||
}
|
||||
OS << '\n';
|
||||
for (const auto &ChildII: II.Children)
|
||||
for (const auto &ChildII : II.Children)
|
||||
dump(OS, ChildII, Indent + 2);
|
||||
}
|
||||
|
||||
|
||||
144
llvm/lib/DebugInfo/GSYM/GsymReaderV1.cpp
Normal file
144
llvm/lib/DebugInfo/GSYM/GsymReaderV1.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
//===- GsymReaderV1.cpp ---------------------------------------------------===//
|
||||
//
|
||||
// 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/GsymReaderV1.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
GsymReaderV1::GsymReaderV1(std::unique_ptr<MemoryBuffer> Buffer,
|
||||
llvm::endianness Endian)
|
||||
: GsymReader(std::move(Buffer), Endian) {}
|
||||
|
||||
llvm::Error GsymReaderV1::parseHeaderAndGlobalDataEntries() {
|
||||
if (auto Err = parseHeader(Hdr, SwappedHdr))
|
||||
return Err;
|
||||
|
||||
// Compute section offsets from the fixed V1 layout and populate the
|
||||
// GlobalDataSections map. V1 sections are laid out sequentially:
|
||||
// [Header] [AddrOffsets] [AddrInfoOffsets] [FileTable] ... [StringTable]
|
||||
const StringRef Buf = MemBuffer->getBuffer();
|
||||
const uint64_t NumAddrs = Hdr->NumAddresses;
|
||||
const uint8_t AddrOffSize = Hdr->AddrOffSize;
|
||||
|
||||
// AddrOffsets
|
||||
uint64_t Offset = alignTo(sizeof(Header), AddrOffSize);
|
||||
uint64_t AddrOffsetsSize = NumAddrs * AddrOffSize;
|
||||
GlobalDataSections[GlobalInfoType::AddrOffsets] = {
|
||||
GlobalInfoType::AddrOffsets, Offset, AddrOffsetsSize};
|
||||
Offset += AddrOffsetsSize;
|
||||
|
||||
// AddrInfoOffsets
|
||||
Offset = alignTo(Offset, 4);
|
||||
uint64_t AddrInfoOffsetsSize = NumAddrs * Header::getAddressInfoOffsetSize();
|
||||
GlobalDataSections[GlobalInfoType::AddrInfoOffsets] = {
|
||||
GlobalInfoType::AddrInfoOffsets, Offset, AddrInfoOffsetsSize};
|
||||
Offset += AddrInfoOffsetsSize;
|
||||
|
||||
// FileTable: read NumFiles to compute the size.
|
||||
GsymDataExtractor Data(Buf, isLittleEndian());
|
||||
uint64_t FTOffset = Offset;
|
||||
uint32_t NumFiles = Data.getU32(&FTOffset);
|
||||
uint64_t FileTableSize =
|
||||
4 + static_cast<uint64_t>(NumFiles) *
|
||||
FileEntry::getEncodedSize(Header::getStringOffsetSize());
|
||||
GlobalDataSections[GlobalInfoType::FileTable] = {GlobalInfoType::FileTable,
|
||||
Offset, FileTableSize};
|
||||
|
||||
// StringTable: offset and size are in the header.
|
||||
GlobalDataSections[GlobalInfoType::StringTable] = {
|
||||
GlobalInfoType::StringTable, Hdr->StrtabOffset, Hdr->StrtabSize};
|
||||
|
||||
// FunctionInfo: starts after the string table and extends to end of file.
|
||||
const uint64_t FIOffset = Hdr->StrtabOffset + Hdr->StrtabSize;
|
||||
GlobalDataSections[GlobalInfoType::FunctionInfo] = {
|
||||
GlobalInfoType::FunctionInfo, FIOffset, Buf.size() - FIOffset};
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void GsymReaderV1::dump(raw_ostream &OS) {
|
||||
OS << *Hdr << "\n";
|
||||
OS << "Address Table:\n";
|
||||
OS << "INDEX OFFSET";
|
||||
|
||||
switch (getAddressOffsetSize()) {
|
||||
case 1:
|
||||
OS << "8 ";
|
||||
break;
|
||||
case 2:
|
||||
OS << "16";
|
||||
break;
|
||||
case 4:
|
||||
OS << "32";
|
||||
break;
|
||||
case 8:
|
||||
OS << "64";
|
||||
break;
|
||||
default:
|
||||
OS << "??";
|
||||
break;
|
||||
}
|
||||
OS << " (ADDRESS)\n";
|
||||
OS << "====== =============================== \n";
|
||||
for (uint32_t I = 0; I < getNumAddresses(); ++I) {
|
||||
OS << format("[%4u] ", I);
|
||||
switch (getAddressOffsetSize()) {
|
||||
case 1:
|
||||
OS << HEX8(getAddrOffsets<uint8_t>()[I]);
|
||||
break;
|
||||
case 2:
|
||||
OS << HEX16(getAddrOffsets<uint16_t>()[I]);
|
||||
break;
|
||||
case 4:
|
||||
OS << HEX32(getAddrOffsets<uint32_t>()[I]);
|
||||
break;
|
||||
case 8:
|
||||
OS << HEX32(getAddrOffsets<uint64_t>()[I]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
OS << " (" << HEX64(*getAddress(I)) << ")\n";
|
||||
}
|
||||
OS << "\nAddress Info Offsets:\n";
|
||||
OS << "INDEX Offset\n";
|
||||
OS << "====== ==========\n";
|
||||
for (uint32_t I = 0; I < getNumAddresses(); ++I)
|
||||
OS << format("[%4u] ", I) << HEX32(*getAddressInfoOffset(I)) << "\n";
|
||||
OS << "\nFiles:\n";
|
||||
OS << "INDEX DIRECTORY BASENAME PATH\n";
|
||||
OS << "====== ========== ========== ==============================\n";
|
||||
for (uint32_t I = 0;; ++I) {
|
||||
auto FE = getFile(I);
|
||||
if (!FE)
|
||||
break;
|
||||
OS << format("[%4u] ", I) << HEX32(FE->Dir) << ' ' << HEX32(FE->Base)
|
||||
<< ' ';
|
||||
dump(OS, FE);
|
||||
OS << "\n";
|
||||
}
|
||||
OS << "\n";
|
||||
gsym::dump(OS, StrTab, 4);
|
||||
OS << "\n";
|
||||
|
||||
for (uint32_t I = 0; I < getNumAddresses(); ++I) {
|
||||
OS << "FunctionInfo @ " << HEX32(*getAddressInfoOffset(I)) << ": ";
|
||||
if (auto FI = getFunctionInfoAtIndex(I))
|
||||
dump(OS, *FI);
|
||||
else
|
||||
logAllUnhandledErrors(FI.takeError(), OS, "FunctionInfo:");
|
||||
}
|
||||
}
|
||||
145
llvm/lib/DebugInfo/GSYM/GsymReaderV2.cpp
Normal file
145
llvm/lib/DebugInfo/GSYM/GsymReaderV2.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
//===- GsymReaderV2.cpp ---------------------------------------------------===//
|
||||
//
|
||||
// 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/GsymReaderV2.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/GSYM/GlobalData.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
GsymReaderV2::GsymReaderV2(std::unique_ptr<MemoryBuffer> Buffer,
|
||||
llvm::endianness Endian)
|
||||
: GsymReader(std::move(Buffer), Endian) {}
|
||||
|
||||
/// For V2 file layout, see HeaderV2.h
|
||||
llvm::Error GsymReaderV2::parseHeaderAndGlobalDataEntries() {
|
||||
if (auto Err = parseHeader(Hdr, SwappedHdr))
|
||||
return Err;
|
||||
return parseGlobalDataEntries(HeaderV2::getEncodedSize());
|
||||
}
|
||||
|
||||
void GsymReaderV2::dump(raw_ostream &OS) {
|
||||
OS << *Hdr << "\n";
|
||||
|
||||
// Print GlobalData entries.
|
||||
OS << "Global Data Sections:\n";
|
||||
OS << "TYPE FILE OFFSET FILE SIZE\n";
|
||||
OS << "=============== ================== ==================\n";
|
||||
/// Re-parse the GlobalData entries to ensure we show the GlobalData
|
||||
/// in the exact order it appears in the GSYM data.
|
||||
const StringRef Buf = MemBuffer->getBuffer();
|
||||
const uint64_t BufSize = Buf.size();
|
||||
GsymDataExtractor Data(Buf, isLittleEndian());
|
||||
uint64_t Offset = HeaderV2::getEncodedSize();
|
||||
while (Offset + sizeof(GlobalData) <= BufSize) {
|
||||
auto GDOrErr = GlobalData::decode(Data, Offset);
|
||||
assert(GDOrErr && "GlobalData::decode() should not fail");
|
||||
const GlobalData &GD = *GDOrErr;
|
||||
|
||||
OS << format("%-15s ", getNameForGlobalInfoType(GD.Type).data())
|
||||
<< HEX64(GD.FileOffset) << " " << HEX64(GD.FileSize) << "\n";
|
||||
|
||||
// Stop printing after the end of list entry.
|
||||
if (GD.Type == GlobalInfoType::EndOfList)
|
||||
break;
|
||||
}
|
||||
OS << "\n";
|
||||
|
||||
// Print UUID if present.
|
||||
if (auto UUIDBytes = getOptionalGlobalDataBytes(GlobalInfoType::UUID)) {
|
||||
OS << "UUID:\n";
|
||||
for (uint8_t Byte : *UUIDBytes)
|
||||
OS << format_hex_no_prefix(Byte, 2);
|
||||
OS << "\n\n";
|
||||
}
|
||||
|
||||
OS << "Address Table:\n";
|
||||
OS << "INDEX OFFSET ";
|
||||
switch (getAddressOffsetSize()) {
|
||||
case 1:
|
||||
OS << "8 ";
|
||||
break;
|
||||
case 2:
|
||||
OS << "16";
|
||||
break;
|
||||
case 4:
|
||||
OS << "32";
|
||||
break;
|
||||
case 8:
|
||||
OS << "64";
|
||||
break;
|
||||
default:
|
||||
OS << "??";
|
||||
break;
|
||||
}
|
||||
OS << " (ADDRESS 64)\n";
|
||||
OS << "====== ========================================\n";
|
||||
for (uint32_t I = 0; I < getNumAddresses(); ++I) {
|
||||
OS << format("[%4u] ", I);
|
||||
switch (getAddressOffsetSize()) {
|
||||
case 1:
|
||||
OS << HEX8(getAddrOffsets<uint8_t>()[I]);
|
||||
break;
|
||||
case 2:
|
||||
OS << HEX16(getAddrOffsets<uint16_t>()[I]);
|
||||
break;
|
||||
case 4:
|
||||
OS << HEX32(getAddrOffsets<uint32_t>()[I]);
|
||||
break;
|
||||
case 8:
|
||||
OS << HEX32(getAddrOffsets<uint64_t>()[I]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
OS << " (" << HEX64(*getAddress(I)) << ")\n";
|
||||
}
|
||||
OS << "\nAddress Info Offsets:\n";
|
||||
OS << "INDEX OFFSET 64 (FILE OFFSET 64)\n";
|
||||
OS << "====== ========================================\n";
|
||||
for (uint32_t I = 0; I < getNumAddresses(); ++I) {
|
||||
uint64_t RelOffset = I * getAddressInfoOffsetSize();
|
||||
uint64_t RelValue =
|
||||
AddrInfoOffsetsData.getUnsigned(&RelOffset, getAddressInfoOffsetSize());
|
||||
OS << format("[%4u] ", I) << HEX64(RelValue) << " ("
|
||||
<< HEX64(*getAddressInfoOffset(I)) << ")\n";
|
||||
}
|
||||
OS << "\nFiles:\n";
|
||||
OS << "INDEX DIRECTORY BASENAME PATH\n";
|
||||
OS << "====== ========== ========== "
|
||||
"========================================\n";
|
||||
// Since we don't store the total number of files in the file table, loop
|
||||
// until we get a null entry which means the index is out of range.
|
||||
for (uint32_t I = 0;; ++I) {
|
||||
auto FE = getFile(I);
|
||||
if (!FE)
|
||||
break;
|
||||
OS << format("[%4u] ", I) << HEX32(FE->Dir) << ' ' << HEX32(FE->Base)
|
||||
<< ' ';
|
||||
dump(OS, FE);
|
||||
OS << "\n";
|
||||
}
|
||||
OS << "\n";
|
||||
gsym::dump(OS, StrTab, 8);
|
||||
OS << "\n";
|
||||
|
||||
for (uint32_t I = 0; I < getNumAddresses(); ++I) {
|
||||
OS << "FunctionInfo @ " << HEX32(*getAddressInfoOffset(I)) << ": ";
|
||||
if (auto FI = getFunctionInfoAtIndex(I))
|
||||
dump(OS, *FI);
|
||||
else
|
||||
logAllUnhandledErrors(FI.takeError(), OS, "FunctionInfo:");
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
@@ -42,7 +42,7 @@ llvm::Error Header::checkForError() const {
|
||||
if (Magic != GSYM_MAGIC)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid GSYM magic 0x%8.8x", Magic);
|
||||
if (Version != GSYM_VERSION)
|
||||
if (Version != Header::getVersion())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"unsupported GSYM version %u", Version);
|
||||
switch (AddrOffSize) {
|
||||
@@ -61,7 +61,7 @@ llvm::Error Header::checkForError() const {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
llvm::Expected<Header> Header::decode(DataExtractor &Data) {
|
||||
llvm::Expected<Header> Header::decode(GsymDataExtractor &Data) {
|
||||
uint64_t Offset = 0;
|
||||
// The header is stored as a single blob of data that has a fixed byte size.
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Header)))
|
||||
|
||||
95
llvm/lib/DebugInfo/GSYM/HeaderV2.cpp
Normal file
95
llvm/lib/DebugInfo/GSYM/HeaderV2.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
//===- HeaderV2.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/HeaderV2.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#define HEX8(v) llvm::format_hex(v, 4)
|
||||
#define HEX16(v) llvm::format_hex(v, 6)
|
||||
#define HEX32(v) llvm::format_hex(v, 10)
|
||||
#define HEX64(v) llvm::format_hex(v, 18)
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const HeaderV2 &H) {
|
||||
OS << "Header:\n";
|
||||
OS << " Magic = " << HEX32(H.Magic) << "\n";
|
||||
OS << " Version = " << HEX16(H.Version) << '\n';
|
||||
OS << " AddrOffSize = " << HEX8(H.AddrOffSize) << '\n';
|
||||
OS << " StrTableEnc = " << HEX8(static_cast<uint8_t>(H.StrTableEncoding))
|
||||
<< '\n';
|
||||
OS << " BaseAddress = " << HEX64(H.BaseAddress) << '\n';
|
||||
OS << " NumAddresses = " << HEX32(H.NumAddresses) << '\n';
|
||||
return OS;
|
||||
}
|
||||
|
||||
llvm::Error HeaderV2::checkForError() const {
|
||||
if (Magic != GSYM_MAGIC)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid GSYM magic 0x%8.8x", Magic);
|
||||
if (Version != HeaderV2::getVersion())
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"unsupported GSYM version %u", Version);
|
||||
if (AddrOffSize < 1 || AddrOffSize > 8)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid address offset size %u", AddrOffSize);
|
||||
uint8_t Encoding = static_cast<uint8_t>(StrTableEncoding);
|
||||
switch (Encoding) {
|
||||
case static_cast<uint8_t>(StringTableEncoding::Default):
|
||||
break;
|
||||
default:
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"unsupported string table encoding %u", Encoding);
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
llvm::Expected<HeaderV2> HeaderV2::decode(GsymDataExtractor &Data) {
|
||||
uint64_t Offset = 0;
|
||||
// The fixed portion of the HeaderV2 is 20 bytes:
|
||||
// Magic(4) + Version(2) + AddrOffSize(1) + StrTableEncoding(1) +
|
||||
// BaseAddress(8) + NumAddresses(4)
|
||||
const uint64_t FixedHeaderSize = HeaderV2::getEncodedSize();
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, FixedHeaderSize))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"not enough data for a gsym::HeaderV2");
|
||||
HeaderV2 H;
|
||||
H.Magic = Data.getU32(&Offset);
|
||||
H.Version = Data.getU16(&Offset);
|
||||
H.AddrOffSize = Data.getU8(&Offset);
|
||||
H.StrTableEncoding = static_cast<StringTableEncoding>(Data.getU8(&Offset));
|
||||
H.BaseAddress = Data.getU64(&Offset);
|
||||
H.NumAddresses = Data.getU32(&Offset);
|
||||
if (llvm::Error Err = H.checkForError())
|
||||
return std::move(Err);
|
||||
return H;
|
||||
}
|
||||
|
||||
llvm::Error HeaderV2::encode(FileWriter &O) const {
|
||||
if (llvm::Error Err = checkForError())
|
||||
return Err;
|
||||
O.writeU32(Magic);
|
||||
O.writeU16(Version);
|
||||
O.writeU8(AddrOffSize);
|
||||
O.writeU8(static_cast<uint8_t>(StrTableEncoding));
|
||||
O.writeU64(BaseAddress);
|
||||
O.writeU32(NumAddresses);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
bool llvm::gsym::operator==(const HeaderV2 &LHS, const HeaderV2 &RHS) {
|
||||
return LHS.Magic == RHS.Magic && LHS.Version == RHS.Version &&
|
||||
LHS.AddrOffSize == RHS.AddrOffSize &&
|
||||
LHS.StrTableEncoding == RHS.StrTableEncoding &&
|
||||
LHS.BaseAddress == RHS.BaseAddress &&
|
||||
LHS.NumAddresses == RHS.NumAddresses;
|
||||
}
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileEntry.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymReader.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/Support/InterleavedRange.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
@@ -68,13 +68,14 @@ InlineInfo::getInlineStack(uint64_t Addr) const {
|
||||
///
|
||||
/// \param SkippedRanges If true, address ranges have already been skipped.
|
||||
|
||||
static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) {
|
||||
static bool skip(GsymDataExtractor &Data, uint64_t &Offset,
|
||||
bool SkippedRanges) {
|
||||
if (!SkippedRanges) {
|
||||
if (skipRanges(Data, Offset) == 0)
|
||||
return false;
|
||||
}
|
||||
bool HasChildren = Data.getU8(&Offset) != 0;
|
||||
Data.getU32(&Offset); // Skip Inline.Name.
|
||||
Data.getStringOffset(&Offset); // Skip Inline.Name.
|
||||
Data.getULEB128(&Offset); // Skip Inline.CallFile.
|
||||
Data.getULEB128(&Offset); // Skip Inline.CallLine.
|
||||
if (HasChildren) {
|
||||
@@ -100,9 +101,9 @@ static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) {
|
||||
/// \param BaseAddr The address that the relative address range offsets are
|
||||
/// relative to.
|
||||
|
||||
static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,
|
||||
uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs,
|
||||
llvm::Error &Err) {
|
||||
static bool lookup(const GsymReader &GR, GsymDataExtractor &Data,
|
||||
uint64_t &Offset, uint64_t BaseAddr, uint64_t Addr,
|
||||
SourceLocations &SrcLocs, llvm::Error &Err) {
|
||||
InlineInfo Inline;
|
||||
decodeRanges(Inline.Ranges, Data, BaseAddr, Offset);
|
||||
if (Inline.Ranges.empty())
|
||||
@@ -117,7 +118,7 @@ static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,
|
||||
// The address range is contained within this InlineInfo, add the source
|
||||
// location for this InlineInfo and any children that contain the address.
|
||||
bool HasChildren = Data.getU8(&Offset) != 0;
|
||||
Inline.Name = Data.getU32(&Offset);
|
||||
Inline.Name = Data.getStringOffset(&Offset);
|
||||
Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
|
||||
Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
|
||||
if (HasChildren) {
|
||||
@@ -151,7 +152,7 @@ static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data,
|
||||
llvm::Error InlineInfo::lookup(const GsymReader &GR, GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr, uint64_t Addr,
|
||||
SourceLocations &SrcLocs) {
|
||||
// Call our recursive helper function starting at offset zero.
|
||||
@@ -171,8 +172,8 @@ llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data,
|
||||
/// \param BaseAddr The base address to use when decoding address ranges.
|
||||
/// \returns An InlineInfo or an error describing the issue that was
|
||||
/// encountered during decoding.
|
||||
static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
|
||||
uint64_t BaseAddr) {
|
||||
static llvm::Expected<InlineInfo> decode(GsymDataExtractor &Data,
|
||||
uint64_t &Offset, uint64_t BaseAddr) {
|
||||
InlineInfo Inline;
|
||||
if (!Data.isValidOffset(Offset))
|
||||
return createStringError(std::errc::io_error,
|
||||
@@ -185,10 +186,11 @@ static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
|
||||
"0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children",
|
||||
Offset);
|
||||
bool HasChildren = Data.getU8(&Offset) != 0;
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, 4))
|
||||
if (!Data.isValidOffsetForDataOfSize(Offset, Data.getStringOffsetSize()))
|
||||
return createStringError(std::errc::io_error,
|
||||
"0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset);
|
||||
Inline.Name = Data.getU32(&Offset);
|
||||
"0x%8.8" PRIx64 ": missing InlineInfo name",
|
||||
Offset);
|
||||
Inline.Name = Data.getStringOffset(&Offset);
|
||||
if (!Data.isValidOffset(Offset))
|
||||
return createStringError(std::errc::io_error,
|
||||
"0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset);
|
||||
@@ -214,7 +216,7 @@ static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
|
||||
return Inline;
|
||||
}
|
||||
|
||||
llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data,
|
||||
llvm::Expected<InlineInfo> InlineInfo::decode(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr) {
|
||||
uint64_t Offset = 0;
|
||||
return ::decode(Data, Offset, BaseAddr);
|
||||
@@ -230,7 +232,7 @@ llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const {
|
||||
encodeRanges(Ranges, O, BaseAddr);
|
||||
bool HasChildren = !Children.empty();
|
||||
O.writeU8(HasChildren);
|
||||
O.writeU32(Name);
|
||||
O.writeStringOffset(Name);
|
||||
O.writeULEB(CallFile);
|
||||
O.writeULEB(CallLine);
|
||||
if (HasChildren) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/LineTable.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
@@ -51,7 +51,7 @@ static bool encodeSpecial(int64_t MinLineDelta, int64_t MaxLineDelta,
|
||||
|
||||
typedef std::function<bool(const LineEntry &Row)> LineEntryCallback;
|
||||
|
||||
static llvm::Error parse(DataExtractor &Data, uint64_t BaseAddr,
|
||||
static llvm::Error parse(GsymDataExtractor &Data, uint64_t BaseAddr,
|
||||
LineEntryCallback const &Callback) {
|
||||
uint64_t Offset = 0;
|
||||
if (!Data.isValidOffset(Offset))
|
||||
@@ -248,7 +248,7 @@ llvm::Error LineTable::encode(FileWriter &Out, uint64_t BaseAddr) const {
|
||||
// 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,
|
||||
llvm::Expected<LineTable> LineTable::decode(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr) {
|
||||
LineTable LT;
|
||||
llvm::Error Err = parse(Data, BaseAddr, [&](const LineEntry &Row) -> bool {
|
||||
@@ -263,7 +263,8 @@ llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data,
|
||||
// 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) {
|
||||
Expected<LineEntry> LineTable::lookup(GsymDataExtractor &Data,
|
||||
uint64_t BaseAddr, uint64_t Addr) {
|
||||
LineEntry Result;
|
||||
llvm::Error Err = parse(Data, BaseAddr,
|
||||
[Addr, &Result](const LineEntry &Row) -> bool {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "llvm/DebugInfo/GSYM/MergedFunctionsInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
||||
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
|
||||
#include "llvm/Support/DataExtractor.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymDataExtractor.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
@@ -33,14 +33,14 @@ llvm::Error MergedFunctionsInfo::encode(FileWriter &Out) const {
|
||||
}
|
||||
|
||||
llvm::Expected<MergedFunctionsInfo>
|
||||
MergedFunctionsInfo::decode(DataExtractor &Data, uint64_t BaseAddr) {
|
||||
MergedFunctionsInfo::decode(GsymDataExtractor &Data, uint64_t BaseAddr) {
|
||||
MergedFunctionsInfo MFI;
|
||||
auto FuncExtractorsOrError = MFI.getFuncsDataExtractors(Data);
|
||||
|
||||
if (!FuncExtractorsOrError)
|
||||
return FuncExtractorsOrError.takeError();
|
||||
|
||||
for (DataExtractor &FuncData : *FuncExtractorsOrError) {
|
||||
for (GsymDataExtractor &FuncData : *FuncExtractorsOrError) {
|
||||
llvm::Expected<FunctionInfo> FI = FunctionInfo::decode(FuncData, BaseAddr);
|
||||
if (!FI)
|
||||
return FI.takeError();
|
||||
@@ -50,9 +50,9 @@ MergedFunctionsInfo::decode(DataExtractor &Data, uint64_t BaseAddr) {
|
||||
return MFI;
|
||||
}
|
||||
|
||||
llvm::Expected<std::vector<DataExtractor>>
|
||||
MergedFunctionsInfo::getFuncsDataExtractors(DataExtractor &Data) {
|
||||
std::vector<DataExtractor> Results;
|
||||
llvm::Expected<std::vector<GsymDataExtractor>>
|
||||
MergedFunctionsInfo::getFuncsDataExtractors(GsymDataExtractor &Data) {
|
||||
std::vector<GsymDataExtractor> Results;
|
||||
uint64_t Offset = 0;
|
||||
|
||||
// Ensure there is enough data to read the function count.
|
||||
@@ -82,8 +82,7 @@ MergedFunctionsInfo::getFuncsDataExtractors(DataExtractor &Data) {
|
||||
i, Offset, FnSize);
|
||||
|
||||
// Extract the function data.
|
||||
Results.emplace_back(Data.getData().substr(Offset, FnSize),
|
||||
Data.isLittleEndian());
|
||||
Results.emplace_back(Data, Offset, FnSize);
|
||||
|
||||
Offset += FnSize;
|
||||
}
|
||||
|
||||
@@ -781,12 +781,8 @@ LLVMSymbolizer::getOrCreateModuleInfo(StringRef ModuleName) {
|
||||
if (!GsymFile.empty()) {
|
||||
auto ReaderOrErr = gsym::GsymReader::openFile(GsymFile);
|
||||
|
||||
if (ReaderOrErr) {
|
||||
std::unique_ptr<gsym::GsymReader> Reader =
|
||||
std::make_unique<gsym::GsymReader>(std::move(*ReaderOrErr));
|
||||
|
||||
Context = std::make_unique<gsym::GsymContext>(std::move(Reader));
|
||||
}
|
||||
if (ReaderOrErr)
|
||||
Context = std::make_unique<gsym::GsymContext>(std::move(*ReaderOrErr));
|
||||
}
|
||||
if (!Context) {
|
||||
if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
|
||||
|
||||
@@ -51,3 +51,10 @@ defm merged_functions_filter :
|
||||
"When used with --address/--addresses-from-stdin and --merged-functions,\n"
|
||||
"filters the merged functions output to only show functions matching any of the specified regex patterns.\n"
|
||||
"Can be specified multiple times.">;
|
||||
defm output_version :
|
||||
Eq<"output-version",
|
||||
"Set the GSYM output version (1 or 2). Default: 1.">;
|
||||
def benchmark_reader :
|
||||
FF<"benchmark-reader",
|
||||
"Open a GSYM file and perform a lookup for every function start address to benchmark the reader performance">,
|
||||
Flags<[HelpHidden]>;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/DIContext.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/DebugInfo/GSYM/CallSiteInfo.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
#include "llvm/Object/MachOUniversal.h"
|
||||
@@ -38,10 +37,15 @@
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/DebugInfo/GSYM/CallSiteInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/DwarfTransformer.h"
|
||||
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymCreatorV1.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymCreatorV2.h"
|
||||
#include "llvm/DebugInfo/GSYM/GsymReader.h"
|
||||
#include "llvm/DebugInfo/GSYM/Header.h"
|
||||
#include "llvm/DebugInfo/GSYM/HeaderV2.h"
|
||||
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
|
||||
#include "llvm/DebugInfo/GSYM/LookupResult.h"
|
||||
#include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h"
|
||||
@@ -92,6 +96,7 @@ static std::vector<std::string> ArchFilters;
|
||||
static std::string OutputFilename;
|
||||
static std::string JsonSummaryFile;
|
||||
static bool Verify;
|
||||
static bool BenchmarkReader;
|
||||
static unsigned NumThreads;
|
||||
static uint64_t SegmentSize;
|
||||
static bool Quiet;
|
||||
@@ -101,6 +106,8 @@ static bool UseMergedFunctions = false;
|
||||
static bool LoadDwarfCallSites = false;
|
||||
static std::string CallSiteYamlPath;
|
||||
static std::vector<std::string> MergedFunctionsFilters;
|
||||
// Default output version. Can be overridden by --output-version.
|
||||
static uint32_t OutputVersion = Header::getVersion();
|
||||
|
||||
static void parseArgs(int argc, char **argv) {
|
||||
GSYMUtilOptTable Tbl;
|
||||
@@ -150,6 +157,7 @@ static void parseArgs(int argc, char **argv) {
|
||||
JsonSummaryFile = A->getValue();
|
||||
|
||||
Verify = Args.hasArg(OPT_verify);
|
||||
BenchmarkReader = Args.hasArg(OPT_benchmark_reader);
|
||||
|
||||
if (const llvm::opt::Arg *A = Args.getLastArg(OPT_num_threads_EQ)) {
|
||||
StringRef S{A->getValue()};
|
||||
@@ -212,6 +220,18 @@ static void parseArgs(int argc, char **argv) {
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (const llvm::opt::Arg *A = Args.getLastArg(OPT_output_version_EQ)) {
|
||||
StringRef Val = A->getValue();
|
||||
uint32_t Version;
|
||||
if (Val.getAsInteger(10, Version) || (Version != Header::getVersion() &&
|
||||
Version != HeaderV2::getVersion())) {
|
||||
llvm::errs() << ToolName << ": for the --output-version option: '" << Val
|
||||
<< "' is invalid. Use '1' or '2'.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
OutputVersion = Version;
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
@@ -348,7 +368,19 @@ static llvm::Error handleObjectFile(ObjectFile &Obj, const std::string &OutFile,
|
||||
auto ThreadCount =
|
||||
NumThreads > 0 ? NumThreads : std::thread::hardware_concurrency();
|
||||
|
||||
GsymCreator Gsym(Quiet);
|
||||
std::unique_ptr<GsymCreator> GsymPtr;
|
||||
switch (OutputVersion) {
|
||||
case Header::getVersion():
|
||||
GsymPtr = std::make_unique<GsymCreatorV1>(Quiet);
|
||||
break;
|
||||
case HeaderV2::getVersion():
|
||||
GsymPtr = std::make_unique<GsymCreatorV2>(Quiet);
|
||||
break;
|
||||
default:
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"invalid --output-version option");
|
||||
}
|
||||
GsymCreator &Gsym = *GsymPtr;
|
||||
|
||||
// See if we can figure out the base address for a given object file, and if
|
||||
// we can, then set the base address to use to this value. This will ease
|
||||
@@ -597,6 +629,25 @@ static void doLookup(GsymReader &Gsym, uint64_t Addr, raw_ostream &OS) {
|
||||
}
|
||||
}
|
||||
|
||||
static llvm::Error benchmarkReader(StringRef GSYMPath) {
|
||||
auto Gsym = GsymReader::openFile(GSYMPath);
|
||||
if (!Gsym)
|
||||
return Gsym.takeError();
|
||||
auto NumAddrs = (*Gsym)->getNumAddresses();
|
||||
for (uint32_t I = 0; I < NumAddrs; ++I) {
|
||||
auto Addr = (*Gsym)->getAddress(I);
|
||||
if (!Addr)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"failed to extract address[%u]", I);
|
||||
auto LR = (*Gsym)->lookup(*Addr);
|
||||
if (!LR)
|
||||
return LR.takeError();
|
||||
}
|
||||
outs() << "Benchmarked " << NumAddrs << " lookups in \"" << GSYMPath
|
||||
<< "\"\n";
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
// Print a stack trace if we signal out.
|
||||
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
||||
@@ -609,6 +660,13 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
|
||||
raw_ostream &OS = outs();
|
||||
|
||||
if (BenchmarkReader) {
|
||||
for (const auto &GSYMPath : InputFilenames)
|
||||
if (auto Err = benchmarkReader(GSYMPath))
|
||||
error("Benchmark failed: ", std::move(Err));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
OutputAggregator Aggregation(&OS);
|
||||
if (!ConvertFilename.empty()) {
|
||||
// Convert DWARF to GSYM
|
||||
@@ -661,7 +719,7 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
|
||||
std::string InputLine;
|
||||
std::string CurrentGSYMPath;
|
||||
std::optional<Expected<GsymReader>> CurrentGsym;
|
||||
std::unique_ptr<GsymReader> CurrentGsym;
|
||||
|
||||
while (std::getline(std::cin, InputLine)) {
|
||||
// Strip newline characters.
|
||||
@@ -674,9 +732,10 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
llvm::StringRef{StrippedInputLine}.split(' ');
|
||||
|
||||
if (GSYMPath != CurrentGSYMPath) {
|
||||
CurrentGsym = GsymReader::openFile(GSYMPath);
|
||||
if (!*CurrentGsym)
|
||||
error(GSYMPath, CurrentGsym->takeError());
|
||||
auto GsymOrErr = GsymReader::openFile(GSYMPath);
|
||||
if (!GsymOrErr)
|
||||
error(GSYMPath, GsymOrErr.takeError());
|
||||
CurrentGsym = std::move(*GsymOrErr);
|
||||
CurrentGSYMPath = GSYMPath;
|
||||
}
|
||||
|
||||
@@ -687,7 +746,7 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
doLookup(**CurrentGsym, Addr, OS);
|
||||
doLookup(*CurrentGsym, Addr, OS);
|
||||
|
||||
OS << "\n";
|
||||
OS.flush();
|
||||
@@ -703,14 +762,14 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
error(GSYMPath, Gsym.takeError());
|
||||
|
||||
if (LookupAddresses.empty()) {
|
||||
Gsym->dump(outs());
|
||||
(*Gsym)->dump(outs());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lookup an address in a GSYM file and print any matches.
|
||||
OS << "Looking up addresses in \"" << GSYMPath << "\":\n";
|
||||
for (auto Addr : LookupAddresses) {
|
||||
doLookup(*Gsym, Addr, OS);
|
||||
doLookup(**Gsym, Addr, OS);
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -7,7 +7,9 @@ set(LLVM_LINK_COMPONENTS
|
||||
)
|
||||
|
||||
add_llvm_unittest(DebugInfoGSYMTests
|
||||
GsymDataExtractorTest.cpp
|
||||
GSYMTest.cpp
|
||||
GSYMV2Test.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(DebugInfoGSYMTests PRIVATE LLVMTestingSupport)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1135
llvm/unittests/DebugInfo/GSYM/GSYMV2Test.cpp
Normal file
1135
llvm/unittests/DebugInfo/GSYM/GSYMV2Test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
106
llvm/unittests/DebugInfo/GSYM/GsymDataExtractorTest.cpp
Normal file
106
llvm/unittests/DebugInfo/GSYM/GsymDataExtractorTest.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
//===- GsymDataExtractorTest.cpp ------------------------------------------===//
|
||||
//
|
||||
// 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/GsymDataExtractor.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace gsym;
|
||||
|
||||
TEST(GsymDataExtractorTest, DefaultStringOffsetSize) {
|
||||
GsymDataExtractor DE(StringRef("\0", 1), false);
|
||||
EXPECT_EQ(8u, DE.getStringOffsetSize());
|
||||
}
|
||||
|
||||
TEST(GsymDataExtractorTest, ExplicitStringOffsetSize) {
|
||||
for (uint8_t Size = 1; Size <= 8; ++Size) {
|
||||
GsymDataExtractor DE(StringRef("\0", 1), false, Size);
|
||||
EXPECT_EQ(Size, DE.getStringOffsetSize());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GsymDataExtractorTest, SubrangeConstructor) {
|
||||
const char Data[] = "\x01\x02\x03\x04\x05\x06\x07\x08";
|
||||
GsymDataExtractor Parent(StringRef(Data, 8), true, 5);
|
||||
|
||||
GsymDataExtractor Sub(Parent, 2, 4);
|
||||
EXPECT_EQ(5u, Sub.getStringOffsetSize());
|
||||
EXPECT_TRUE(Sub.isLittleEndian());
|
||||
EXPECT_EQ(4u, Sub.getData().size());
|
||||
|
||||
uint64_t Offset = 0;
|
||||
EXPECT_EQ(0x03u, Sub.getU8(&Offset));
|
||||
}
|
||||
|
||||
TEST(GsymDataExtractorTest, GetStringOffset) {
|
||||
// Data: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
|
||||
const char Data[] = "\x01\x02\x03\x04\x05\x06\x07\x08";
|
||||
|
||||
// Expected little-endian values for sizes 1-8 reading from offset 0.
|
||||
const uint64_t ExpectedLE[] = {
|
||||
0x01u, // size 1
|
||||
0x0201u, // size 2
|
||||
0x030201u, // size 3
|
||||
0x04030201u, // size 4
|
||||
0x0504030201u, // size 5
|
||||
0x060504030201u, // size 6
|
||||
0x07060504030201u, // size 7
|
||||
0x0807060504030201u, // size 8
|
||||
};
|
||||
// Expected big-endian values for sizes 1-8 reading from offset 0.
|
||||
const uint64_t ExpectedBE[] = {
|
||||
0x01u, // size 1
|
||||
0x0102u, // size 2
|
||||
0x010203u, // size 3
|
||||
0x01020304u, // size 4
|
||||
0x0102030405u, // size 5
|
||||
0x010203040506u, // size 6
|
||||
0x01020304050607u, // size 7
|
||||
0x0102030405060708u, // size 8
|
||||
};
|
||||
|
||||
for (uint8_t Size = 1; Size <= 8; ++Size) {
|
||||
GsymDataExtractor LE(StringRef(Data, 8), true, Size);
|
||||
GsymDataExtractor BE(StringRef(Data, 8), false, Size);
|
||||
uint64_t Offset = 0;
|
||||
EXPECT_EQ(ExpectedLE[Size - 1], LE.getStringOffset(&Offset))
|
||||
<< "LE size=" << (int)Size;
|
||||
EXPECT_EQ(Size, Offset);
|
||||
Offset = 0;
|
||||
EXPECT_EQ(ExpectedBE[Size - 1], BE.getStringOffset(&Offset))
|
||||
<< "BE size=" << (int)Size;
|
||||
EXPECT_EQ(Size, Offset);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GsymDataExtractorTest, GetStringOffsetCursor) {
|
||||
const char Data[] = "\x01\x02\x03\x04\x05\x06\x07\x08";
|
||||
|
||||
// Test Cursor-based reads for all sizes 1-8.
|
||||
for (uint8_t Size = 1; Size <= 8; ++Size) {
|
||||
GsymDataExtractor DE(StringRef(Data, 8), true, Size);
|
||||
DataExtractor::Cursor C(0);
|
||||
// Read the first element.
|
||||
uint64_t Val = DE.getStringOffset(C);
|
||||
EXPECT_EQ(Size, C.tell()) << "size=" << (int)Size;
|
||||
EXPECT_NE(0u, Val) << "size=" << (int)Size;
|
||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded());
|
||||
}
|
||||
|
||||
// Verify reading past end fails.
|
||||
GsymDataExtractor DE(StringRef(Data, 8), true, 4);
|
||||
DataExtractor::Cursor C(0);
|
||||
DE.getStringOffset(C); // offset 0-3
|
||||
DE.getStringOffset(C); // offset 4-7
|
||||
EXPECT_EQ(8u, C.tell());
|
||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded());
|
||||
EXPECT_EQ(0u, DE.getStringOffset(C)); // past end
|
||||
EXPECT_THAT_ERROR(C.takeError(), Failed());
|
||||
}
|
||||
Reference in New Issue
Block a user